From edd731b232ebb518eb173b4d3429402383587f04 Mon Sep 17 00:00:00 2001 From: David Rose Date: Wed, 29 Jan 2014 17:30:40 +0000 Subject: [PATCH 01/71] copy_channel_masked() --- panda/src/pnmimage/pfmFile.cxx | 23 +++++++++++++++++++++++ panda/src/pnmimage/pfmFile.h | 1 + 2 files changed, 24 insertions(+) diff --git a/panda/src/pnmimage/pfmFile.cxx b/panda/src/pnmimage/pfmFile.cxx index b78a3dcbab..06e13106bd 100644 --- a/panda/src/pnmimage/pfmFile.cxx +++ b/panda/src/pnmimage/pfmFile.cxx @@ -1467,6 +1467,29 @@ copy_channel(int to_channel, const PfmFile &other, int from_channel) { } } +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::copy_channel_masked +// Access: Published +// Description: Copies just the specified channel values from the +// indicated PfmFile, but only where the other file has +// a data point. +//////////////////////////////////////////////////////////////////// +void PfmFile:: +copy_channel_masked(int to_channel, const PfmFile &other, int from_channel) { + nassertv(is_valid() && other.is_valid()); + nassertv(other._x_size == _x_size && other._y_size == _y_size); + nassertv(to_channel >= 0 && to_channel < get_num_channels() && + from_channel >= 0 && from_channel < other.get_num_channels()); + + for (int yi = 0; yi < _y_size; ++yi) { + for (int xi = 0; xi < _x_size; ++xi) { + if (other.has_point(xi, yi)) { + set_channel(xi, yi, to_channel, other.get_channel(xi, yi, from_channel)); + } + } + } +} + //////////////////////////////////////////////////////////////////// // Function: PfmFile::apply_crop // Access: Published diff --git a/panda/src/pnmimage/pfmFile.h b/panda/src/pnmimage/pfmFile.h index 723940ce5b..9a3cd444c4 100644 --- a/panda/src/pnmimage/pfmFile.h +++ b/panda/src/pnmimage/pfmFile.h @@ -121,6 +121,7 @@ PUBLISHED: BLOCKING void reverse_distort(const PfmFile &dist, PN_float32 scale_factor = 1.0); BLOCKING void merge(const PfmFile &other); BLOCKING void copy_channel(int to_channel, const PfmFile &other, int from_channel); + BLOCKING void copy_channel_masked(int to_channel, const PfmFile &other, int from_channel); BLOCKING void apply_crop(int x_begin, int x_end, int y_begin, int y_end); BLOCKING void clear_to_texcoords(int x_size, int y_size); From b8935be2c320722526e1748602604d77d9394e2f Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 9 Feb 2014 10:41:26 +0000 Subject: [PATCH 02/71] fix bug LP#1277916: MeshDrawer causes crash if setBudget not explicitly called --- panda/src/grutil/meshDrawer.I | 1 + panda/src/grutil/meshDrawer.cxx | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/panda/src/grutil/meshDrawer.I b/panda/src/grutil/meshDrawer.I index 1e22a653e9..e1aeace6e8 100644 --- a/panda/src/grutil/meshDrawer.I +++ b/panda/src/grutil/meshDrawer.I @@ -29,6 +29,7 @@ MeshDrawer() { _uv = NULL; _color = NULL; _budget = 5000; + _vdata = NULL; } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/grutil/meshDrawer.cxx b/panda/src/grutil/meshDrawer.cxx index 8aeb9d7d65..e270a43786 100644 --- a/panda/src/grutil/meshDrawer.cxx +++ b/panda/src/grutil/meshDrawer.cxx @@ -110,6 +110,11 @@ void MeshDrawer::begin(NodePath camera, NodePath render) { if (_normal != NULL) delete _normal; if (_uv != NULL) delete _uv; if (_color != NULL) delete _color; + + if (_vdata == NULL) { + generator(_budget); + } + _vertex = new GeomVertexRewriter(_vdata, "vertex"); _uv = new GeomVertexRewriter(_vdata, "texcoord"); _normal = new GeomVertexRewriter(_vdata, "normal"); From de7ec424c7c5e2c2dcb9344fa19b24ef9994fd44 Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 9 Feb 2014 16:45:08 +0000 Subject: [PATCH 03/71] Add get_tangent_name and get_binormal_name --- panda/src/gobj/textureStage.I | 35 ++++++++++++++++++++++++++++++++++- panda/src/gobj/textureStage.h | 23 +++++++++++------------ 2 files changed, 45 insertions(+), 13 deletions(-) diff --git a/panda/src/gobj/textureStage.I b/panda/src/gobj/textureStage.I index e5ca4d94d8..cb5c6c0504 100755 --- a/panda/src/gobj/textureStage.I +++ b/panda/src/gobj/textureStage.I @@ -141,13 +141,46 @@ set_texcoord_name(const string &name) { //////////////////////////////////////////////////////////////////// // Function: TextureStage::get_texcoord_name // Access: Published -// Description: Returns the InternalName +// Description: See set_texcoord_name. The default is +// InternalName::get_texcoord(). //////////////////////////////////////////////////////////////////// INLINE InternalName *TextureStage:: get_texcoord_name() const { return _texcoord_name; } +//////////////////////////////////////////////////////////////////// +// Function: TextureStage::get_tangent_name +// Access: Published +// Description: Returns the set of tangents this texture stage will +// use. This is the same as get_texcoord_name(), +// except that the first part is "tangent". +//////////////////////////////////////////////////////////////////// +INLINE InternalName *TextureStage:: +get_tangent_name() const { + if (_texcoord_name->get_parent() == NULL) { + return InternalName::get_tangent(); + } else { + return InternalName::get_tangent_name(_texcoord_name->get_basename()); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TextureStage::get_binormal_name +// Access: Published +// Description: Returns the set of binormals this texture stage will +// use. This is the same as get_binormal_name(), +// except that the first part is "binormal". +//////////////////////////////////////////////////////////////////// +INLINE InternalName *TextureStage:: +get_binormal_name() const { + if (_texcoord_name->get_parent() == NULL) { + return InternalName::get_binormal(); + } else { + return InternalName::get_binormal_name(_texcoord_name->get_basename()); + } +} + //////////////////////////////////////////////////////////////////// // Function: TextureStage::set_mode // Access: Published diff --git a/panda/src/gobj/textureStage.h b/panda/src/gobj/textureStage.h index ff1734e7cf..9831124864 100644 --- a/panda/src/gobj/textureStage.h +++ b/panda/src/gobj/textureStage.h @@ -45,7 +45,7 @@ PUBLISHED: enum Mode { // Modes that pertain to the fixed-function pipeline. - + M_modulate, M_decal, M_blend, @@ -53,12 +53,12 @@ PUBLISHED: M_add, M_combine, M_blend_color_scale, - + M_modulate_glow, // When fixed-function, equivalent to modulate. M_modulate_gloss, // When fixed-function, equivalent to modulate. - + // Modes that are only relevant to shader-based rendering. - + M_normal, M_normal_height, M_glow, // Rarely used: modulate_glow is more efficient. @@ -67,7 +67,7 @@ PUBLISHED: M_selector, M_normal_gloss, }; - + enum CombineMode { CM_undefined, CM_replace, @@ -113,12 +113,14 @@ PUBLISHED: INLINE void set_texcoord_name(InternalName *name); INLINE void set_texcoord_name(const string &texcoord_name); INLINE InternalName *get_texcoord_name() const; - + INLINE InternalName *get_tangent_name() const; + INLINE InternalName *get_binormal_name() const; + INLINE void set_mode(Mode mode); INLINE Mode get_mode() const; - + INLINE bool is_fixed_function() const; - + INLINE void set_color(const LColor &color); INLINE LColor get_color() const; @@ -231,7 +233,7 @@ private: static PT(TextureStage) _default_stage; static UpdateSeq _sort_seq; - + public: // Datagram stuff static void register_with_read_factory(); @@ -272,6 +274,3 @@ EXPCL_PANDA_GOBJ ostream &operator << (ostream &out, TextureStage::CombineOperan #include "textureStage.I" #endif - - - From 94962c15d2772425d830c6fefd0e084509c56f87 Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 10 Feb 2014 09:50:37 +0000 Subject: [PATCH 04/71] addTexture -> add_texture --- panda/src/gobj/textureCollection.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panda/src/gobj/textureCollection.cxx b/panda/src/gobj/textureCollection.cxx index 9fd40bc4b4..6bddb5f199 100644 --- a/panda/src/gobj/textureCollection.cxx +++ b/panda/src/gobj/textureCollection.cxx @@ -76,7 +76,7 @@ TextureCollection(PyObject *self, PyObject *sequence) { if (item == NULL) { return; } - PyObject *result = PyObject_CallMethod(self, (char *)"addTexture", (char *)"O", item); + PyObject *result = PyObject_CallMethod(self, (char *)"add_texture", (char *)"O", item); Py_DECREF(item); if (result == NULL) { // Unable to add item--probably it wasn't of the appropriate type. From 67d12cdd4a50f83cb876376c85ee86a7747276ea Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 10 Feb 2014 09:51:21 +0000 Subject: [PATCH 05/71] add InternalName::join(sep) --- panda/src/gobj/internalName.cxx | 19 +++++++++++++++++++ panda/src/gobj/internalName.h | 1 + 2 files changed, 20 insertions(+) diff --git a/panda/src/gobj/internalName.cxx b/panda/src/gobj/internalName.cxx index 703b12dc9e..8ee046338f 100644 --- a/panda/src/gobj/internalName.cxx +++ b/panda/src/gobj/internalName.cxx @@ -154,6 +154,25 @@ get_name() const { } } +//////////////////////////////////////////////////////////////////// +// Function: InternalName::join +// Access: Published +// Description: Like get_name, but uses a custom separator instead +// of ".". +//////////////////////////////////////////////////////////////////// +string InternalName:: +join(const string &sep) const { + if (_parent == get_root()) { + return _basename; + + } else if (_parent == (InternalName *)NULL) { + return string(); + + } else { + return _parent->join(sep) + sep + _basename; + } +} + //////////////////////////////////////////////////////////////////// // Function: InternalName::find_ancestor // Access: Published diff --git a/panda/src/gobj/internalName.h b/panda/src/gobj/internalName.h index 88ad2fdda4..7c3c42e259 100644 --- a/panda/src/gobj/internalName.h +++ b/panda/src/gobj/internalName.h @@ -53,6 +53,7 @@ PUBLISHED: INLINE InternalName *get_parent() const; string get_name() const; + string join(const string &sep) const; INLINE const string &get_basename() const; int find_ancestor(const string &basename) const; From ddcd74047b6f7e68142866c1b288fed8e69f6b89 Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 10 Feb 2014 11:24:36 +0000 Subject: [PATCH 06/71] more error checks and allow disabling seeking and VFS behaviour --- panda/src/movies/config_movies.cxx | 10 ++++++++++ panda/src/movies/config_movies.h | 2 ++ panda/src/movies/vorbisAudioCursor.cxx | 23 +++++++++++++++++++++-- 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/panda/src/movies/config_movies.cxx b/panda/src/movies/config_movies.cxx index a38f5976d7..d5a01b272d 100644 --- a/panda/src/movies/config_movies.cxx +++ b/panda/src/movies/config_movies.cxx @@ -50,6 +50,16 @@ ConfigVariableList load_video_type "either the name of a module, or a space-separate list of filename " "extensions, followed by the name of the module.")); +ConfigVariableBool vorbis_enable_vfs +("vorbis-enable-vfs", true, + PRC_DESC("Set this to false if you're having trouble loading Ogg Vorbis " + "files and don't need to use the virtual file system.")); + +ConfigVariableBool vorbis_enable_seek +("vorbis-enable-seek", true, + PRC_DESC("Set this to false if you're having trouble with seeking while " + "using the Ogg Vorbis decoder.")); + ConfigVariableBool vorbis_seek_lap ("vorbis-seek-lap", true, PRC_DESC("If this is set to true, the Ogg Vorbis decoder will automatically " diff --git a/panda/src/movies/config_movies.h b/panda/src/movies/config_movies.h index d0c02724d8..ada94cb98c 100644 --- a/panda/src/movies/config_movies.h +++ b/panda/src/movies/config_movies.h @@ -28,6 +28,8 @@ NotifyCategoryDecl(movies, EXPCL_PANDA_MOVIES, EXPTP_PANDA_MOVIES); extern ConfigVariableList load_audio_type; extern ConfigVariableList load_video_type; +extern ConfigVariableBool vorbis_enable_vfs; +extern ConfigVariableBool vorbis_enable_seek; extern ConfigVariableBool vorbis_seek_lap; extern EXPCL_PANDA_MOVIES void init_libmovies(); diff --git a/panda/src/movies/vorbisAudioCursor.cxx b/panda/src/movies/vorbisAudioCursor.cxx index a0265f10ed..b6a8d2d6a6 100644 --- a/panda/src/movies/vorbisAudioCursor.cxx +++ b/panda/src/movies/vorbisAudioCursor.cxx @@ -37,10 +37,15 @@ VorbisAudioCursor(VorbisAudio *src, istream *stream) : // Set up the callbacks to read via the VFS. ov_callbacks callbacks; callbacks.read_func = &cb_read_func; - callbacks.seek_func = &cb_seek_func; callbacks.close_func = &cb_close_func; callbacks.tell_func = &cb_tell_func; + if (vorbis_enable_seek) { + callbacks.seek_func = &cb_seek_func; + } else { + callbacks.seek_func = NULL; + } + if (ov_open_callbacks((void*) stream, &_ov, NULL, 0, callbacks) != 0) { movies_cat.error() << "Failed to read Ogg Vorbis file.\n"; @@ -53,7 +58,7 @@ VorbisAudioCursor(VorbisAudio *src, istream *stream) : _audio_channels = vi->channels; _audio_rate = vi->rate; - _can_seek = (ov_seekable(&_ov) != 0); + _can_seek = vorbis_enable_seek && (ov_seekable(&_ov) != 0); _can_seek_fast = _can_seek; _is_valid = true; @@ -78,6 +83,10 @@ VorbisAudioCursor:: //////////////////////////////////////////////////////////////////// void VorbisAudioCursor:: seek(double t) { + if (!vorbis_enable_seek) { + return; + } + t = max(t, 0.0); // Use ov_time_seek_lap if cross-lapping is enabled. @@ -163,6 +172,7 @@ read_samples(int n, PN_int16 *data) { size_t VorbisAudioCursor:: cb_read_func(void *ptr, size_t size, size_t nmemb, void *datasource) { istream *stream = (istream*) datasource; + nassertr(stream != NULL, -1); stream->read((char *)ptr, size * nmemb); return stream->gcount(); @@ -176,7 +186,12 @@ cb_read_func(void *ptr, size_t size, size_t nmemb, void *datasource) { //////////////////////////////////////////////////////////////////// int VorbisAudioCursor:: cb_seek_func(void *datasource, ogg_int64_t offset, int whence) { + if (!vorbis_enable_seek) { + return -1; + } + istream *stream = (istream*) datasource; + nassertr(stream != NULL, -1); switch (whence) { case SEEK_SET: @@ -213,6 +228,8 @@ cb_seek_func(void *datasource, ogg_int64_t offset, int whence) { int VorbisAudioCursor:: cb_close_func(void *datasource) { istream *stream = (istream*) datasource; + nassertr(stream != NULL, -1); + VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); vfs->close_read_file(stream); @@ -229,6 +246,8 @@ cb_close_func(void *datasource) { long VorbisAudioCursor:: cb_tell_func(void *datasource) { istream *stream = (istream*) datasource; + nassertr(stream != NULL, -1); + return stream->tellg(); } From 89749133ff0ac3a0cf97ef035dec71b7aec547e9 Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 10 Feb 2014 11:25:39 +0000 Subject: [PATCH 07/71] oops, remove vorbis-enable-vfs which I never implemented --- panda/src/movies/config_movies.cxx | 5 ----- panda/src/movies/config_movies.h | 1 - 2 files changed, 6 deletions(-) diff --git a/panda/src/movies/config_movies.cxx b/panda/src/movies/config_movies.cxx index d5a01b272d..dc4202c6f2 100644 --- a/panda/src/movies/config_movies.cxx +++ b/panda/src/movies/config_movies.cxx @@ -50,11 +50,6 @@ ConfigVariableList load_video_type "either the name of a module, or a space-separate list of filename " "extensions, followed by the name of the module.")); -ConfigVariableBool vorbis_enable_vfs -("vorbis-enable-vfs", true, - PRC_DESC("Set this to false if you're having trouble loading Ogg Vorbis " - "files and don't need to use the virtual file system.")); - ConfigVariableBool vorbis_enable_seek ("vorbis-enable-seek", true, PRC_DESC("Set this to false if you're having trouble with seeking while " diff --git a/panda/src/movies/config_movies.h b/panda/src/movies/config_movies.h index ada94cb98c..fa09bc6e8f 100644 --- a/panda/src/movies/config_movies.h +++ b/panda/src/movies/config_movies.h @@ -28,7 +28,6 @@ NotifyCategoryDecl(movies, EXPCL_PANDA_MOVIES, EXPTP_PANDA_MOVIES); extern ConfigVariableList load_audio_type; extern ConfigVariableList load_video_type; -extern ConfigVariableBool vorbis_enable_vfs; extern ConfigVariableBool vorbis_enable_seek; extern ConfigVariableBool vorbis_seek_lap; From 4da840805f5cd706304341aee9a48c536bc426ed Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 10 Feb 2014 11:32:06 +0000 Subject: [PATCH 08/71] Add support for k_time / osg_FrameTime inputs, and change the Shader Generator not to require a separate texcoord set for every texture in an effort to cut down on register overuse. --- panda/src/display/graphicsStateGuardian.cxx | 10 ++ panda/src/glstuff/glShaderContext_src.cxx | 108 ++++++++------ panda/src/gobj/shader.cxx | 75 ++++++---- panda/src/gobj/shader.h | 138 +++++++++--------- panda/src/pgraphnodes/shaderGenerator.cxx | 151 +++++++++++--------- panda/src/pgraphnodes/shaderGenerator.h | 4 +- 6 files changed, 271 insertions(+), 215 deletions(-) diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index fb630022d4..5e67cb81e7 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/panda/src/display/graphicsStateGuardian.cxx @@ -996,6 +996,16 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name, LMatrix4 & 0.0); return &t; } + case Shader::SMO_frame_time: { + PN_stdfloat time = ClockObject::get_global_clock()->get_frame_time(); + t = LMatrix4(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, time, time, time, time); + return &t; + } + case Shader::SMO_frame_delta: { + PN_stdfloat dt = ClockObject::get_global_clock()->get_dt(); + t = LMatrix4(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, dt, dt, dt, dt); + return &t; + } case Shader::SMO_texpad_x: { Texture *tex = _target_shader->get_shader_input_texture(name); nassertr(tex != 0, &LMatrix4::zeros_mat()); diff --git a/panda/src/glstuff/glShaderContext_src.cxx b/panda/src/glstuff/glShaderContext_src.cxx index 281ab0cef9..5bd6d9d7d0 100755 --- a/panda/src/glstuff/glShaderContext_src.cxx +++ b/panda/src/glstuff/glShaderContext_src.cxx @@ -71,14 +71,13 @@ TypeHandle CLP(ShaderContext)::_type_handle; bool CLP(ShaderContext):: parse_and_set_short_hand_shader_vars(Shader::ShaderArgId &arg_id, Shader *objShader) { Shader::ShaderArgInfo p; - p._id = arg_id; - + string basename(arg_id._name); // Split it at the underscores. vector_string pieces; tokenize(basename, pieces, "_"); - + if (pieces[0] == "mstrans") { pieces[0] = "trans"; pieces.push_back("to"); @@ -119,9 +118,9 @@ parse_and_set_short_hand_shader_vars(Shader::ShaderArgId &arg_id, Shader *objSha pieces.push_back("to"); pieces.push_back("clip"); } - - if ((pieces[0] == "mat")||(pieces[0] == "inv")|| - (pieces[0] == "tps")||(pieces[0] == "itp")) { + + if ((pieces[0] == "mat") || (pieces[0] == "inv") || + (pieces[0] == "tps") || (pieces[0] == "itp")) { if (!objShader->cp_errchk_parameter_words(p, 2)) { return false; } @@ -138,33 +137,33 @@ parse_and_set_short_hand_shader_vars(Shader::ShaderArgId &arg_id, Shader *objSha objShader->cp_report_error(p,"unrecognized matrix name"); return false; } - if (trans=="mat") { + if (trans == "mat") { pieces[0] = "trans"; - } else if (trans=="inv") { + } else if (trans == "inv") { string t = pieces[1]; pieces[1] = pieces[3]; pieces[3] = t; - } else if (trans=="tps") { + } else if (trans == "tps") { pieces[0] = "tpose"; - } else if (trans=="itp") { + } else if (trans == "itp") { string t = pieces[1]; pieces[1] = pieces[3]; pieces[3] = t; pieces[0] = "tpose"; - } + } } // Implement the transform-matrix generator. - if ((pieces[0]=="trans")|| - (pieces[0]=="tpose")|| - (pieces[0]=="row0")|| - (pieces[0]=="row1")|| - (pieces[0]=="row2")|| - (pieces[0]=="row3")|| - (pieces[0]=="col0")|| - (pieces[0]=="col1")|| - (pieces[0]=="col2")|| - (pieces[0]=="col3")) { + if ((pieces[0] == "trans") || + (pieces[0] == "tpose") || + (pieces[0] == "row0") || + (pieces[0] == "row1") || + (pieces[0] == "row2") || + (pieces[0] == "row3") || + (pieces[0] == "col0") || + (pieces[0] == "col1") || + (pieces[0] == "col2") || + (pieces[0] == "col3")) { Shader::ShaderMatSpec bind; bind._id = arg_id; @@ -174,27 +173,26 @@ parse_and_set_short_hand_shader_vars(Shader::ShaderArgId &arg_id, Shader *objSha pieces.push_back(""); // Decide whether this is a matrix or vector. - if (pieces[0]=="trans") bind._piece = Shader::SMP_whole; - else if (pieces[0]=="tpose") bind._piece = Shader::SMP_transpose; - else if (pieces[0]=="row0") bind._piece = Shader::SMP_row0; - else if (pieces[0]=="row1") bind._piece = Shader::SMP_row1; - else if (pieces[0]=="row2") bind._piece = Shader::SMP_row2; - else if (pieces[0]=="row3") bind._piece = Shader::SMP_row3; - else if (pieces[0]=="col0") bind._piece = Shader::SMP_col0; - else if (pieces[0]=="col1") bind._piece = Shader::SMP_col1; - else if (pieces[0]=="col2") bind._piece = Shader::SMP_col2; - else if (pieces[0]=="col3") bind._piece = Shader::SMP_col3; + if (pieces[0] == "trans") bind._piece = Shader::SMP_whole; + else if (pieces[0] == "tpose") bind._piece = Shader::SMP_transpose; + else if (pieces[0] == "row0") bind._piece = Shader::SMP_row0; + else if (pieces[0] == "row1") bind._piece = Shader::SMP_row1; + else if (pieces[0] == "row2") bind._piece = Shader::SMP_row2; + else if (pieces[0] == "row3") bind._piece = Shader::SMP_row3; + else if (pieces[0] == "col0") bind._piece = Shader::SMP_col0; + else if (pieces[0] == "col1") bind._piece = Shader::SMP_col1; + else if (pieces[0] == "col2") bind._piece = Shader::SMP_col2; + else if (pieces[0] == "col3") bind._piece = Shader::SMP_col3; if (!objShader->cp_parse_coord_sys(p, pieces, next, bind, true)) { return false; } if (!objShader->cp_parse_delimiter(p, pieces, next)) { return false; - } + } if (!objShader->cp_parse_coord_sys(p, pieces, next, bind, false)) { return false; } - if (!objShader->cp_parse_eol(p, pieces, next)) { return false; } @@ -338,7 +336,7 @@ CLP(ShaderContext)(Shader *s, GSG *gsg) : ShaderContext(s) { } bind._arg[0] = NULL; bind._arg[1] = NULL; - + if (matrix_name == "ModelViewProjectionMatrix") { bind._func = Shader::SMF_compose; if (inverse) { @@ -418,15 +416,14 @@ CLP(ShaderContext)(Shader *s, GSG *gsg) : ShaderContext(s) { // them as well, to increase compatibility. // Other inputs we may support in the future: // int osg_FrameNumber - // float osg_FrameTime - // float osg_DeltaFrameTime + + Shader::ShaderMatSpec bind; + bind._id = arg_id; + bind._arg[0] = NULL; + bind._arg[1] = NULL; if (param_name == "osg_ViewMatrix") { - Shader::ShaderMatSpec bind; - bind._id = arg_id; bind._piece = Shader::SMP_whole; - bind._arg[0] = NULL; - bind._arg[1] = NULL; bind._func = Shader::SMF_first; bind._part[0] = Shader::SMO_world_to_view; bind._part[1] = Shader::SMO_identity; @@ -436,11 +433,7 @@ CLP(ShaderContext)(Shader *s, GSG *gsg) : ShaderContext(s) { continue; } else if (param_name == "osg_InverseViewMatrix") { - Shader::ShaderMatSpec bind; - bind._id = arg_id; bind._piece = Shader::SMP_whole; - bind._arg[0] = NULL; - bind._arg[1] = NULL; bind._func = Shader::SMF_first; bind._part[0] = Shader::SMO_view_to_world; bind._part[1] = Shader::SMO_identity; @@ -448,6 +441,26 @@ CLP(ShaderContext)(Shader *s, GSG *gsg) : ShaderContext(s) { bind._dep[1] = Shader::SSD_NONE; s->_mat_spec.push_back(bind); continue; + + } else if (param_name == "osg_FrameTime") { + bind._piece = Shader::SMP_row3x1; + bind._func = Shader::SMF_first; + bind._part[0] = Shader::SMO_frame_time; + bind._part[1] = Shader::SMO_identity; + bind._dep[0] = Shader::SSD_general; + bind._dep[1] = Shader::SSD_NONE; + s->_mat_spec.push_back(bind); + continue; + + } else if (param_name == "osg_DeltaFrameTime") { + bind._piece = Shader::SMP_row3x1; + bind._func = Shader::SMF_first; + bind._part[0] = Shader::SMO_frame_delta; + bind._part[1] = Shader::SMO_identity; + bind._dep[0] = Shader::SSD_general; + bind._dep[1] = Shader::SSD_NONE; + s->_mat_spec.push_back(bind); + continue; } } @@ -455,7 +468,7 @@ CLP(ShaderContext)(Shader *s, GSG *gsg) : ShaderContext(s) { if (parse_and_set_short_hand_shader_vars(arg_id, s)) { continue; } - + if (param_size == 1) { switch (param_type) { #ifndef OPENGLES @@ -706,7 +719,7 @@ CLP(ShaderContext)(Shader *s, GSG *gsg) : ShaderContext(s) { } } } - + gsg->report_my_gl_errors(); } @@ -1162,12 +1175,13 @@ update_shader_vertex_arrays(CLP(ShaderContext) *prev, GSG *gsg, } } #endif - + InternalName *name = _shader->_var_spec[i]._name; int texslot = _shader->_var_spec[i]._append_uv; if (texslot >= 0 && texslot < gsg->_state_texture->get_num_on_stages()) { TextureStage *stage = gsg->_state_texture->get_on_stage(texslot); InternalName *texname = stage->get_texcoord_name(); + if (name == InternalName::get_texcoord()) { name = texname; } else if (texname != InternalName::get_texcoord()) { diff --git a/panda/src/gobj/shader.cxx b/panda/src/gobj/shader.cxx index 8807206409..675fbca876 100755 --- a/panda/src/gobj/shader.cxx +++ b/panda/src/gobj/shader.cxx @@ -414,23 +414,23 @@ cp_dependency(ShaderMatInput inp) { (inp == SMO_view_to_model)) { dep |= SSD_transform; } - if ((inp == SMO_texpad_x)|| - (inp == SMO_texpix_x)|| - (inp == SMO_alight_x)|| - (inp == SMO_dlight_x)|| - (inp == SMO_plight_x)|| - (inp == SMO_slight_x)|| - (inp == SMO_satten_x)|| - (inp == SMO_mat_constant_x)|| - (inp == SMO_vec_constant_x)|| - (inp == SMO_clipplane_x)|| - (inp == SMO_view_x_to_view)|| - (inp == SMO_view_to_view_x)|| - (inp == SMO_apiview_x_to_view)|| - (inp == SMO_view_to_apiview_x)|| - (inp == SMO_clip_x_to_view)|| - (inp == SMO_view_to_clip_x)|| - (inp == SMO_apiclip_x_to_view)|| + if ((inp == SMO_texpad_x) || + (inp == SMO_texpix_x) || + (inp == SMO_alight_x) || + (inp == SMO_dlight_x) || + (inp == SMO_plight_x) || + (inp == SMO_slight_x) || + (inp == SMO_satten_x) || + (inp == SMO_mat_constant_x) || + (inp == SMO_vec_constant_x) || + (inp == SMO_clipplane_x) || + (inp == SMO_view_x_to_view) || + (inp == SMO_view_to_view_x) || + (inp == SMO_apiview_x_to_view) || + (inp == SMO_view_to_apiview_x) || + (inp == SMO_clip_x_to_view) || + (inp == SMO_view_to_clip_x) || + (inp == SMO_apiclip_x_to_view) || (inp == SMO_view_to_apiclip_x)) { dep |= SSD_shaderinputs; } @@ -588,36 +588,45 @@ compile_parameter(const ShaderArgId &arg_id, } ShaderVarSpec bind; bind._id = arg_id; + bind._append_uv = -1; + if (pieces.size() == 2) { - if (pieces[1]=="position") { + if (pieces[1] == "position") { bind._name = InternalName::get_vertex(); bind._append_uv = -1; _var_spec.push_back(bind); return true; } - if (pieces[1].substr(0,8)=="texcoord") { + if (pieces[1].substr(0, 8) == "texcoord") { bind._name = InternalName::get_texcoord(); - bind._append_uv = atoi(pieces[1].c_str()+8); + if (pieces[1].size() > 8) { + bind._append_uv = atoi(pieces[1].c_str() + 8); + } _var_spec.push_back(bind); return true; } - if (pieces[1].substr(0,7)=="tangent") { + if (pieces[1].substr(0, 7) == "tangent") { bind._name = InternalName::get_tangent(); - bind._append_uv = atoi(pieces[1].c_str()+7); + if (pieces[1].size() > 7) { + bind._append_uv = atoi(pieces[1].c_str() + 7); + } _var_spec.push_back(bind); return true; } - if (pieces[1].substr(0,8)=="binormal") { + if (pieces[1].substr(0, 8) == "binormal") { bind._name = InternalName::get_binormal(); - bind._append_uv = atoi(pieces[1].c_str()+8); + if (pieces[1].size() > 8) { + bind._append_uv = atoi(pieces[1].c_str() + 8); + } _var_spec.push_back(bind); return true; } } + bind._name = InternalName::get_root(); - bind._append_uv = -1; - for (int i=1; i<(int)(pieces.size()-0); i++) + for (int i = 1; i < pieces.size(); ++i) { bind._name = bind._name->append(pieces[i]); + } _var_spec.push_back(bind); return true; } @@ -1008,7 +1017,7 @@ compile_parameter(const ShaderArgId &arg_id, // Keywords to access unusual parameters. if (pieces[0] == "sys") { - if ((!cp_errchk_parameter_words(p,2)) || + if ((!cp_errchk_parameter_words(p, 2)) || (!cp_errchk_parameter_in(p)) || (!cp_errchk_parameter_uniform(p))) { return false; @@ -1025,14 +1034,24 @@ compile_parameter(const ShaderArgId &arg_id, } bind._part[0] = SMO_pixel_size; bind._arg[0] = NULL; + } else if (pieces[1] == "windowsize") { if (!cp_errchk_parameter_float(p, 2, 2)) { return false; } bind._part[0] = SMO_window_size; bind._arg[0] = NULL; + + } else if (pieces[1] == "time") { + if (!cp_errchk_parameter_float(p, 1, 1)) { + return false; + } + bind._piece = SMP_row3x1; + bind._part[0] = SMO_frame_time; + bind._arg[0] = NULL; + } else { - cp_report_error(p,"unknown system parameter"); + cp_report_error(p, "unknown system parameter"); return false; } diff --git a/panda/src/gobj/shader.h b/panda/src/gobj/shader.h index 420fc0ab8f..7980a3dc78 100755 --- a/panda/src/gobj/shader.h +++ b/panda/src/gobj/shader.h @@ -30,7 +30,7 @@ #include "pta_LVecBase2.h" #ifdef HAVE_CG -// I don't want to include the Cg header file into panda as a +// I don't want to include the Cg header file into panda as a // whole. Instead, I'll just excerpt some opaque declarations. typedef struct _CGcontext *CGcontext; typedef struct _CGprogram *CGprogram; @@ -40,8 +40,8 @@ typedef struct _CGparameter *CGparameter; //////////////////////////////////////////////////////////////////// // Class : Shader // Summary: The Shader class is meant to select the Shader Language, -// select the available profile, compile the shader, and -// finally compile and store the shader parameters +// select the available profile, compile the shader, and +// finally compile and store the shader parameters // in the appropriate structure. //////////////////////////////////////////////////////////////////// class EXPCL_PANDA_GOBJ Shader : public TypedWritableReferenceCount { @@ -115,11 +115,11 @@ public: SMO_pixel_size, SMO_texpad_x, SMO_texpix_x, - + SMO_attr_material, SMO_attr_color, SMO_attr_colorscale, - + SMO_alight_x, SMO_dlight_x, SMO_plight_x, @@ -128,7 +128,7 @@ public: SMO_texmat_x, SMO_plane_x, SMO_clipplane_x, - + SMO_mat_constant_x, SMO_vec_constant_x, @@ -146,7 +146,7 @@ public: SMO_apiclip_to_view, SMO_view_to_apiclip, - + SMO_view_x_to_view, SMO_view_to_view_x, @@ -161,11 +161,15 @@ public: SMO_attr_fog, SMO_attr_fogcolor, - + + SMO_frame_number, + SMO_frame_time, + SMO_frame_delta, + SMO_INVALID }; - - enum ShaderArgClass { + + enum ShaderArgClass { SAC_scalar, SAC_vector, SAC_matrix, @@ -173,28 +177,28 @@ public: SAC_array, SAC_unknown, }; - - enum ShaderArgType { - SAT_scalar, - SAT_vec1, - SAT_vec2, - SAT_vec3, - SAT_vec4, - SAT_mat1x1, - SAT_mat1x2, - SAT_mat1x3, + + enum ShaderArgType { + SAT_scalar, + SAT_vec1, + SAT_vec2, + SAT_vec3, + SAT_vec4, + SAT_mat1x1, + SAT_mat1x2, + SAT_mat1x3, SAT_mat1x4, SAT_mat2x1, - SAT_mat2x2, - SAT_mat2x3, - SAT_mat2x4, - SAT_mat3x1, - SAT_mat3x2, - SAT_mat3x3, - SAT_mat3x4, - SAT_mat4x1, - SAT_mat4x2, - SAT_mat4x3, + SAT_mat2x2, + SAT_mat2x3, + SAT_mat2x4, + SAT_mat3x1, + SAT_mat3x2, + SAT_mat3x3, + SAT_mat3x4, + SAT_mat4x1, + SAT_mat4x2, + SAT_mat4x3, SAT_mat4x4, SAT_sampler1d, SAT_sampler2d, @@ -230,20 +234,20 @@ public: }; enum ShaderStateDep { - SSD_NONE = 0, - SSD_general = 1, - SSD_transform = 2, - SSD_color = 4, - SSD_colorscale = 8, - SSD_material = 16, - SSD_shaderinputs = 32, - SSD_fog = 64, + SSD_NONE = 0x000, + SSD_general = 0x001, + SSD_transform = 0x002, + SSD_color = 0x004, + SSD_colorscale = 0x008, + SSD_material = 0x010, + SSD_shaderinputs = 0x020, + SSD_fog = 0x040, }; enum ShaderBug { SBUG_ati_draw_buffers, }; - + enum ShaderMatFunc { SMF_compose, SMF_transform_dlight, @@ -251,36 +255,36 @@ public: SMF_transform_slight, SMF_first, }; - + struct ShaderArgId { string _name; ShaderType _type; int _seqno; - }; - - struct ShaderArgInfo { + }; + + struct ShaderArgInfo { ShaderArgId _id; ShaderArgClass _class; ShaderArgClass _subclass; - ShaderArgType _type; + ShaderArgType _type; ShaderArgDir _direction; bool _varying; NotifyCategory *_cat; }; - + enum ShaderPtrType { SPT_float, SPT_double, SPT_unknown - }; - + }; + // Container structure for data of parameters ShaderPtrSpec. struct ShaderPtrData { private: PT(ReferenceCount) _pta; public: - void *_ptr; + void *_ptr; ShaderPtrType _type; bool _updated; int _size; //number of elements vec3[4]=12 @@ -339,8 +343,8 @@ public: PT(InternalName) _name; int _append_uv; }; - - struct ShaderPtrSpec { + + struct ShaderPtrSpec { ShaderArgId _id; int _dim[3]; //n_elements,rows,cols int _dep[2]; @@ -376,8 +380,8 @@ public: public: INLINE ShaderFile() {}; INLINE ShaderFile(const string &shared); - INLINE ShaderFile(const string &vertex, - const string &fragment, + INLINE ShaderFile(const string &vertex, + const string &fragment, const string &geometry, const string &tess_control, const string &tess_evaluation); @@ -404,18 +408,18 @@ public: void parse_upto(string &result, string pattern, bool include); void parse_rest(string &result); bool parse_eof(); - + void cp_report_error(ShaderArgInfo &arg, const string &msg); bool cp_errchk_parameter_words(ShaderArgInfo &arg, int len); bool cp_errchk_parameter_in(ShaderArgInfo &arg); - bool cp_errchk_parameter_ptr(ShaderArgInfo &p); + bool cp_errchk_parameter_ptr(ShaderArgInfo &p); bool cp_errchk_parameter_varying(ShaderArgInfo &arg); bool cp_errchk_parameter_uniform(ShaderArgInfo &arg); bool cp_errchk_parameter_float(ShaderArgInfo &arg, int lo, int hi); bool cp_errchk_parameter_sampler(ShaderArgInfo &arg); bool cp_parse_eol(ShaderArgInfo &arg, vector_string &pieces, int &next); - bool cp_parse_delimiter(ShaderArgInfo &arg, + bool cp_parse_delimiter(ShaderArgInfo &arg, vector_string &pieces, int &next); string cp_parse_non_delimiter(vector_string &pieces, int &next); bool cp_parse_coord_sys(ShaderArgInfo &arg, @@ -425,12 +429,12 @@ public: void cp_optimize_mat_spec(ShaderMatSpec &spec); #ifdef HAVE_CG - void cg_recurse_parameters(CGparameter parameter, - const ShaderType &type, + void cg_recurse_parameters(CGparameter parameter, + const ShaderType &type, bool &success); #endif - - bool compile_parameter(const ShaderArgId &arg_id, + + bool compile_parameter(const ShaderArgId &arg_id, const ShaderArgClass &arg_class, const ShaderArgClass &arg_subclass, const ShaderArgType &arg_type, @@ -443,7 +447,7 @@ public: #ifdef HAVE_CG private: - ShaderArgClass cg_parameter_class(CGparameter p); + ShaderArgClass cg_parameter_class(CGparameter p); ShaderArgType cg_parameter_type(CGparameter p); ShaderArgDir cg_parameter_dir(CGparameter p); @@ -455,7 +459,7 @@ private: bool cg_compile_shader(const ShaderCaps &caps); void cg_release_resources(); void cg_report_errors(); - + // Determines the appropriate cg profile settings and stores them in the active shader caps // based on any profile settings stored in the shader's header void cg_get_profile_from_header(ShaderCaps &caps); @@ -477,24 +481,24 @@ public: bool cg_compile_for(const ShaderCaps &caps, CGcontext &ctx, CGprogram &vprogram, CGprogram &fprogram, CGprogram &gprogram, pvector &map); - + #endif public: - pvector _ptr_spec; + pvector _ptr_spec; epvector _mat_spec; pvector _tex_spec; pvector _var_spec; - + bool _error_flag; CPT(ShaderFile) _text; protected: - CPT(ShaderFile) _filename; + CPT(ShaderFile) _filename; int _parse; bool _loaded; ShaderLanguage _language; - + static ShaderCaps _default_caps; static ShaderUtilization _shader_utilization; static int _shaders_generated; @@ -510,7 +514,7 @@ protected: typedef pmap Contexts; Contexts _contexts; -private: +private: void clear_prepared(PreparedGraphicsObjects *prepared_objects); Shader(); diff --git a/panda/src/pgraphnodes/shaderGenerator.cxx b/panda/src/pgraphnodes/shaderGenerator.cxx index b77dddd5c4..7460a102ff 100644 --- a/panda/src/pgraphnodes/shaderGenerator.cxx +++ b/panda/src/pgraphnodes/shaderGenerator.cxx @@ -622,15 +622,13 @@ synthesize_shader(const RenderState *rs) { // These variables will hold the results of register allocation. - char *ntangent_vreg = 0; - char *ntangent_freg = 0; - char *nbinormal_vreg = 0; - char *nbinormal_freg = 0; - char *htangent_vreg = 0; - char *hbinormal_vreg = 0; - pvector texcoord_freg; - pvector dlightcoord_freg; - pvector slightcoord_freg; + char *tangent_freg = 0; + char *binormal_freg = 0; + string tangent_input; + string binormal_input; + pmap texcoord_fregs; + pvector dlightcoord_fregs; + pvector slightcoord_fregs; char *world_position_freg = 0; char *world_normal_freg = 0; char *eye_position_freg = 0; @@ -659,11 +657,39 @@ synthesize_shader(const RenderState *rs) { for (int i = 0; i < _num_textures; ++i) { TextureStage *stage = texture->get_on_stage(i); if (!tex_gen->has_stage(stage)) { - texcoord_freg.push_back(alloc_freg()); - text << "\t in float4 vtx_texcoord" << i << " : " << alloc_vreg() << ",\n"; - text << "\t out float4 l_texcoord" << i << " : " << texcoord_freg[i] << ",\n"; - } else { - texcoord_freg.push_back(NULL); + const InternalName *texcoord_name = stage->get_texcoord_name(); + + if (texcoord_fregs.count(texcoord_name) == 0) { + char *freg = alloc_freg(); + string tcname = texcoord_name->join("_"); + texcoord_fregs[texcoord_name] = freg; + + text << "\t in float4 vtx_" << tcname << " : " << alloc_vreg() << ",\n"; + text << "\t out float4 l_" << tcname << " : " << freg << ",\n"; + } + } + + if ((_map_index_normal == i && _lighting && _auto_normal_on) || _map_index_height == i) { + const InternalName *texcoord_name = stage->get_texcoord_name(); + PT(InternalName) tangent_name = InternalName::get_tangent(); + PT(InternalName) binormal_name = InternalName::get_binormal(); + + if (texcoord_name != InternalName::get_texcoord()) { + tangent_name = tangent_name->append(texcoord_name->get_basename()); + binormal_name = binormal_name->append(texcoord_name->get_basename()); + } + tangent_input = tangent_name->join("_"); + binormal_input = binormal_name->join("_"); + + text << "\t in float4 vtx_" << tangent_input << " : " << alloc_vreg() << ",\n"; + text << "\t in float4 vtx_" << binormal_input << " : " << alloc_vreg() << ",\n"; + + if (_map_index_normal == i && _lighting && _auto_normal_on) { + tangent_freg = alloc_freg(); + binormal_freg = alloc_freg(); + text << "\t out float4 l_tangent : " << tangent_freg << ",\n"; + text << "\t out float4 l_binormal : " << binormal_freg << ",\n"; + } } } if (_vertex_colors) { @@ -695,49 +721,27 @@ synthesize_shader(const RenderState *rs) { text << "\t in float4 vtx_normal : NORMAL,\n"; } if (_map_index_height >= 0) { - htangent_vreg = alloc_vreg(); - hbinormal_vreg = alloc_vreg(); - if (_map_index_normal == _map_index_height) { - ntangent_vreg = htangent_vreg; - nbinormal_vreg = hbinormal_vreg; - } - text << "\t in float4 vtx_tangent" << _map_index_height << " : " << htangent_vreg << ",\n"; - text << "\t in float4 vtx_binormal" << _map_index_height << " : " << hbinormal_vreg << ",\n"; text << "\t uniform float4 mspos_view,\n"; text << "\t out float3 l_eyevec,\n"; } if (_lighting) { - if (_map_index_normal >= 0 && _auto_normal_on) { - // If we had a height map and it used the same stage, that means we already have those inputs. - if (_map_index_normal != _map_index_height) { - ntangent_vreg = alloc_vreg(); - nbinormal_vreg = alloc_vreg(); - // NB. If we used TANGENT and BINORMAL, Cg would have them overlap with TEXCOORD6-7. - text << "\t in float4 vtx_tangent" << _map_index_normal << " : " << ntangent_vreg << ",\n"; - text << "\t in float4 vtx_binormal" << _map_index_normal << " : " << nbinormal_vreg << ",\n"; - } - ntangent_freg = alloc_freg(); - nbinormal_freg = alloc_freg(); - text << "\t out float4 l_tangent : " << ntangent_freg << ",\n"; - text << "\t out float4 l_binormal : " << nbinormal_freg << ",\n"; - } if (_shadows && _auto_shadow_on) { for (int i=0; i < (int)_dlights.size(); i++) { if (_dlights[i]->_shadow_caster) { - dlightcoord_freg.push_back(alloc_freg()); + dlightcoord_fregs.push_back(alloc_freg()); text << "\t uniform float4x4 trans_model_to_clip_of_dlight" << i << ",\n"; - text << "\t out float4 l_dlightcoord" << i << " : " << dlightcoord_freg[i] << ",\n"; + text << "\t out float4 l_dlightcoord" << i << " : " << dlightcoord_fregs[i] << ",\n"; } else { - dlightcoord_freg.push_back(NULL); + dlightcoord_fregs.push_back(NULL); } } for (int i=0; i < (int)_slights.size(); i++) { if (_slights[i]->_shadow_caster) { - slightcoord_freg.push_back(alloc_freg()); + slightcoord_fregs.push_back(alloc_freg()); text << "\t uniform float4x4 trans_model_to_clip_of_slight" << i << ",\n"; - text << "\t out float4 l_slightcoord" << i << " : " << slightcoord_freg[i] << ",\n"; + text << "\t out float4 l_slightcoord" << i << " : " << slightcoord_fregs[i] << ",\n"; } else { - slightcoord_freg.push_back(NULL); + slightcoord_fregs.push_back(NULL); } } } @@ -768,18 +772,19 @@ synthesize_shader(const RenderState *rs) { text << "\t l_eye_normal.xyz = mul((float3x3)tpose_view_to_model, vtx_normal.xyz);\n"; text << "\t l_eye_normal.w = 0;\n"; } - for (int i = 0; i < _num_textures; ++i) { - if (!tex_gen->has_stage(texture->get_on_stage(i))) { - text << "\t l_texcoord" << i << " = vtx_texcoord" << i << ";\n"; - } + pmap::const_iterator it; + for (it = texcoord_fregs.begin(); it != texcoord_fregs.end(); ++it) { + // Pass through all texcoord inputs as-is. + string tcname = it->first->join("_"); + text << "\t l_" << tcname << " = vtx_" << tcname << ";\n"; } if (_vertex_colors) { text << "\t l_color = vtx_color;\n"; } if (_lighting && (_map_index_normal >= 0 && _auto_normal_on)) { - text << "\t l_tangent.xyz = mul((float3x3)tpose_view_to_model, vtx_tangent" << _map_index_normal << ".xyz);\n"; + text << "\t l_tangent.xyz = mul((float3x3)tpose_view_to_model, vtx_" << tangent_input << ".xyz);\n"; text << "\t l_tangent.w = 0;\n"; - text << "\t l_binormal.xyz = mul((float3x3)tpose_view_to_model, -vtx_binormal" << _map_index_normal << ".xyz);\n"; + text << "\t l_binormal.xyz = mul((float3x3)tpose_view_to_model, -vtx_" << binormal_input << ".xyz);\n"; text << "\t l_binormal.w = 0;\n"; } if (_shadows && _auto_shadow_on) { @@ -824,22 +829,22 @@ synthesize_shader(const RenderState *rs) { if (_need_eye_normal) { text << "\t in float4 l_eye_normal : " << eye_normal_freg << ",\n"; } + for (it = texcoord_fregs.begin(); it != texcoord_fregs.end(); ++it) { + text << "\t in float4 l_" << it->first->join("_") << " : " << it->second << ",\n"; + } const TexMatrixAttrib *tex_matrix = DCAST(TexMatrixAttrib, rs->get_attrib_def(TexMatrixAttrib::get_class_slot())); for (int i=0; i<_num_textures; i++) { TextureStage *stage = texture->get_on_stage(i); Texture *tex = texture->get_on_texture(stage); nassertr(tex != NULL, NULL); text << "\t uniform sampler" << texture_type_as_string(tex->get_texture_type()) << " tex_" << i << ",\n"; - if (!tex_gen->has_stage(stage)) { - text << "\t in float4 l_texcoord" << i << " : " << texcoord_freg[i] << ",\n"; - } if (tex_matrix->has_stage(stage)) { text << "\t uniform float4x4 texmat_" << i << ",\n"; } } if (_lighting && (_map_index_normal >= 0 && _auto_normal_on)) { - text << "\t in float3 l_tangent : " << ntangent_freg << ",\n"; - text << "\t in float3 l_binormal : " << nbinormal_freg << ",\n"; + text << "\t in float3 l_tangent : " << tangent_freg << ",\n"; + text << "\t in float3 l_binormal : " << binormal_freg << ",\n"; } if (_lighting) { for (int i=0; i < (int)_alights.size(); i++) { @@ -853,7 +858,7 @@ synthesize_shader(const RenderState *rs) { } else { text << "\t uniform sampler2D k_dlighttex" << i << ",\n"; } - text << "\t in float4 l_dlightcoord" << i << " : " << dlightcoord_freg[i] << ",\n"; + text << "\t in float4 l_dlightcoord" << i << " : " << dlightcoord_fregs[i] << ",\n"; } } for (int i=0; i < (int)_plights.size(); i++) { @@ -868,7 +873,7 @@ synthesize_shader(const RenderState *rs) { } else { text << "\t uniform sampler2D k_slighttex" << i << ",\n"; } - text << "\t in float4 l_slightcoord" << i << " : " << slightcoord_freg[i] << ",\n"; + text << "\t in float4 l_slightcoord" << i << " : " << slightcoord_fregs[i] << ",\n"; } } if (_need_material_props) { @@ -916,26 +921,30 @@ synthesize_shader(const RenderState *rs) { if (tex_gen != NULL && tex_gen->has_stage(stage)) { switch (tex_gen->get_mode(stage)) { case TexGenAttrib::M_world_position: - text << "\t float4 l_texcoord" << i << " = l_world_position;\n"; + text << "\t float4 texcoord" << i << " = l_world_position;\n"; break; case TexGenAttrib::M_world_normal: - text << "\t float4 l_texcoord" << i << " = l_world_normal;\n"; + text << "\t float4 texcoord" << i << " = l_world_normal;\n"; break; case TexGenAttrib::M_eye_position: - text << "\t float4 l_texcoord" << i << " = l_eye_position;\n"; + text << "\t float4 texcoord" << i << " = l_eye_position;\n"; break; case TexGenAttrib::M_eye_normal: - text << "\t float4 l_texcoord" << i << " = l_eye_normal;\n"; - text << "\t l_texcoord" << i << ".w = 1.0f;\n"; + text << "\t float4 texcoord" << i << " = l_eye_normal;\n"; + text << "\t texcoord" << i << ".w = 1.0f;\n"; break; default: pgraph_cat.error() << "Unsupported TexGenAttrib mode\n"; - text << "\t float4 l_texcoord" << i << " = float4(0, 0, 0, 0);\n"; + text << "\t float4 texcoord" << i << " = float4(0, 0, 0, 0);\n"; } + } else { + // Cg seems to be able to optimize this temporary away when appropriate. + const InternalName *texcoord_name = stage->get_texcoord_name(); + text << "\t float4 texcoord" << i << " = l_" << texcoord_name->join("_") << ";\n"; } if (tex_matrix != NULL && tex_matrix->has_stage(stage)) { - text << "\t l_texcoord" << i << " = mul(texmat_" << i << ", l_texcoord" << i << ");\n"; - text << "\t l_texcoord" << i << ".xyz /= l_texcoord" << i << ".w;\n"; + text << "\t texcoord" << i << " = mul(texmat_" << i << ", texcoord" << i << ");\n"; + text << "\t texcoord" << i << ".xyz /= texcoord" << i << ".w;\n"; } } text << "\t // Fetch all textures.\n"; @@ -943,7 +952,7 @@ synthesize_shader(const RenderState *rs) { Texture *tex = texture->get_on_texture(texture->get_on_stage(_map_index_height)); nassertr(tex != NULL, NULL); text << "\t float4 tex" << _map_index_height << " = tex" << texture_type_as_string(tex->get_texture_type()); - text << "(tex_" << _map_index_height << ", l_texcoord" << _map_index_height << "."; + text << "(tex_" << _map_index_height << ", texcoord" << _map_index_height << "."; switch (tex->get_texture_type()) { case Texture::TT_cube_map: case Texture::TT_3d_texture: @@ -983,21 +992,21 @@ synthesize_shader(const RenderState *rs) { nassertr(tex != NULL, NULL); // Parallax mapping pushes the texture coordinates of the other textures away from the camera. if (_map_index_height >= 0 && parallax_mapping_samples > 0) { - text << "\t l_texcoord" << i << ".xyz -= parallax_offset;\n"; + text << "\t texcoord" << i << ".xyz -= parallax_offset;\n"; } text << "\t float4 tex" << i << " = tex" << texture_type_as_string(tex->get_texture_type()); - text << "(tex_" << i << ", l_texcoord" << i << "."; + text << "(tex_" << i << ", texcoord" << i << "."; switch(tex->get_texture_type()) { case Texture::TT_cube_map: case Texture::TT_3d_texture: case Texture::TT_2d_texture_array: - text << "xyz"; + text << "xyz"; break; - case Texture::TT_2d_texture: + case Texture::TT_2d_texture: text << "xy"; break; - case Texture::TT_1d_texture: - text << "x"; + case Texture::TT_1d_texture: + text << "x"; break; default: break; @@ -1240,7 +1249,7 @@ synthesize_shader(const RenderState *rs) { } else if (_flat_colors) { text << "\t result = attr_color;\n"; } else { - text << "\t result = float4(1,1,1,1);\n"; + text << "\t result = float4(1, 1, 1, 1);\n"; } } diff --git a/panda/src/pgraphnodes/shaderGenerator.h b/panda/src/pgraphnodes/shaderGenerator.h index 0c588df8a1..d08a3976a8 100644 --- a/panda/src/pgraphnodes/shaderGenerator.h +++ b/panda/src/pgraphnodes/shaderGenerator.h @@ -86,8 +86,8 @@ protected: int _vtregs_used; int _ftregs_used; void reset_register_allocator(); - INLINE char *alloc_vreg(); - INLINE char *alloc_freg(); + char *alloc_vreg(); + char *alloc_freg(); // RenderState analysis information. Created by analyze_renderstate: From 1a96ab9aa9661597158c746a1a8701281988fa2b Mon Sep 17 00:00:00 2001 From: enn0x Date: Fri, 14 Feb 2014 23:31:07 +0000 Subject: [PATCH 09/71] Added filtering for BulletWorld.contactTest --- panda/src/bullet/bulletContactResult.cxx | 37 ++++++++++++++++++++++++ panda/src/bullet/bulletContactResult.h | 12 ++++++++ panda/src/bullet/bulletWorld.cxx | 17 ++++++++--- panda/src/bullet/bulletWorld.h | 3 +- 4 files changed, 64 insertions(+), 5 deletions(-) diff --git a/panda/src/bullet/bulletContactResult.cxx b/panda/src/bullet/bulletContactResult.cxx index 9537ac672a..f111667d31 100644 --- a/panda/src/bullet/bulletContactResult.cxx +++ b/panda/src/bullet/bulletContactResult.cxx @@ -24,9 +24,46 @@ BulletContact BulletContactResult::_empty; BulletContactResult:: BulletContactResult() : btCollisionWorld::ContactResultCallback() { +#if BT_BULLET_VERSION >= 281 + _filter_cb = NULL; + _filter_proxy = NULL; + _filter_set = false; +#endif } #if BT_BULLET_VERSION >= 281 +//////////////////////////////////////////////////////////////////// +// Function: BulletContactResult::use_filter +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +void BulletContactResult:: +use_filter(btOverlapFilterCallback *cb, btBroadphaseProxy *proxy) { + + nassertv(cb); + nassertv(proxy); + + _filter_cb = cb; + _filter_proxy = proxy; + _filter_set = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: BulletContactResult::needsCollision +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +bool BulletContactResult:: +needsCollision(btBroadphaseProxy *proxy0) const { + + if (_filter_set) { + return _filter_cb->needBroadphaseCollision(proxy0, _filter_proxy); + } + else { + return true; + } +} + //////////////////////////////////////////////////////////////////// // Function: BulletContactResult::addSingleResult // Access: Published diff --git a/panda/src/bullet/bulletContactResult.h b/panda/src/bullet/bulletContactResult.h index b00d879dad..56bbe54396 100644 --- a/panda/src/bullet/bulletContactResult.h +++ b/panda/src/bullet/bulletContactResult.h @@ -62,6 +62,8 @@ PUBLISHED: public: #if BT_BULLET_VERSION >= 281 + virtual bool needsCollision(btBroadphaseProxy *proxy0) const; + virtual btScalar addSingleResult(btManifoldPoint &mp, const btCollisionObjectWrapper *wrap0, int part_id0, int idx0, const btCollisionObjectWrapper *wrap1, int part_id1, int idx1); @@ -74,11 +76,21 @@ public: protected: BulletContactResult(); +#if BT_BULLET_VERSION >= 281 + void use_filter(btOverlapFilterCallback *cb, btBroadphaseProxy *proxy); +#endif + private: static BulletContact _empty; btAlignedObjectArray _contacts; +#if BT_BULLET_VERSION >= 281 + bool _filter_set; + btOverlapFilterCallback *_filter_cb; + btBroadphaseProxy *_filter_proxy; +#endif + friend class BulletWorld; }; diff --git a/panda/src/bullet/bulletWorld.cxx b/panda/src/bullet/bulletWorld.cxx index c570ff1e18..14502a40af 100644 --- a/panda/src/bullet/bulletWorld.cxx +++ b/panda/src/bullet/bulletWorld.cxx @@ -87,18 +87,21 @@ BulletWorld() { // Filter callback switch (bullet_filter_algorithm) { case FA_mask: - _world->getPairCache()->setOverlapFilterCallback(&_filter_cb1); + _filter_cb = &_filter_cb1; break; case FA_groups_mask: - _world->getPairCache()->setOverlapFilterCallback(&_filter_cb2); + _filter_cb = &_filter_cb2; break; case FA_callback: - _world->getPairCache()->setOverlapFilterCallback(&_filter_cb3); + _filter_cb = &_filter_cb3; break; default: bullet_cat.error() << "no proper filter algorithm!" << endl; + _filter_cb = NULL; } + _world->getPairCache()->setOverlapFilterCallback(_filter_cb); + // Tick callback _tick_callback_obj = NULL; @@ -718,13 +721,18 @@ sweep_test_closest(BulletShape *shape, const TransformState &from_ts, const Tran // Description: //////////////////////////////////////////////////////////////////// BulletContactResult BulletWorld:: -contact_test(PandaNode *node) const { +contact_test(PandaNode *node, bool use_filter) const { btCollisionObject *obj = get_collision_object(node); BulletContactResult cb; if (obj) { + + if (use_filter) { + cb.use_filter(_filter_cb, obj->getBroadphaseHandle()); + } + _world->contactTest(obj, cb); } @@ -745,6 +753,7 @@ contact_test_pair(PandaNode *node0, PandaNode *node1) const { BulletContactResult cb; if (obj0 && obj1) { + _world->contactPairTest(obj0, obj1, cb); } diff --git a/panda/src/bullet/bulletWorld.h b/panda/src/bullet/bulletWorld.h index 3ebfe59ce0..3b57138c0f 100644 --- a/panda/src/bullet/bulletWorld.h +++ b/panda/src/bullet/bulletWorld.h @@ -123,7 +123,7 @@ PUBLISHED: const CollideMask &mask=CollideMask::all_on(), PN_stdfloat penetration=0.0f) const; - BulletContactResult contact_test(PandaNode *node) const; + BulletContactResult contact_test(PandaNode *node, bool use_filter=false) const; BulletContactResult contact_test_pair(PandaNode *node0, PandaNode *node1) const; // Manifolds @@ -232,6 +232,7 @@ private: btFilterCallback1 _filter_cb1; btFilterCallback2 _filter_cb2; btFilterCallback3 _filter_cb3; + btOverlapFilterCallback *_filter_cb; PT(CallbackObject) _tick_callback_obj; From bfebff1d0ae4d2520b0cb6df621f34495501c2f5 Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 15 Feb 2014 16:14:21 +0000 Subject: [PATCH 10/71] don't compare modes an unnecessary second time. --- panda/src/gobj/textureStage.cxx | 3 --- 1 file changed, 3 deletions(-) diff --git a/panda/src/gobj/textureStage.cxx b/panda/src/gobj/textureStage.cxx index 28f2230c6c..f235c4e5e5 100755 --- a/panda/src/gobj/textureStage.cxx +++ b/panda/src/gobj/textureStage.cxx @@ -154,9 +154,6 @@ compare_to(const TextureStage &other) const { if (get_tex_view_offset() != other.get_tex_view_offset()) { return get_tex_view_offset() < other.get_tex_view_offset() ? -1 : 1; } - if (get_mode() != other.get_mode()) { - return get_mode() < other.get_mode() ? -1 : 1; - } if (get_mode() == M_combine) { if (get_combine_rgb_mode() != other.get_combine_rgb_mode()) { return get_combine_rgb_mode() < other.get_combine_rgb_mode() ? -1 : 1; From edab64c5704d31c1ee02331394d8c1ca33fa643b Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 15 Feb 2014 21:34:57 +0000 Subject: [PATCH 11/71] fix Cg error reporting and lack of shader unbinding --- panda/src/glstuff/glShaderContext_src.cxx | 50 ++++++++++++++--------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/panda/src/glstuff/glShaderContext_src.cxx b/panda/src/glstuff/glShaderContext_src.cxx index 5bd6d9d7d0..c5abad0675 100755 --- a/panda/src/glstuff/glShaderContext_src.cxx +++ b/panda/src/glstuff/glShaderContext_src.cxx @@ -221,51 +221,58 @@ CLP(ShaderContext)(Shader *s, GSG *gsg) : ShaderContext(s) { #if defined(HAVE_CG) && !defined(OPENGLES) _cg_context = 0; + _cg_vprofile = CG_PROFILE_UNKNOWN; + _cg_fprofile = CG_PROFILE_UNKNOWN; + _cg_gprofile = CG_PROFILE_UNKNOWN; if (s->get_language() == Shader::SL_Cg) { - // Ask the shader to compile itself for us and // to give us the resulting Cg program objects. if (!s->cg_compile_for(gsg->_shader_caps, _cg_context, _cg_vprogram, - _cg_fprogram, + _cg_fprogram, _cg_gprogram, _cg_parameter_map)) { return; } - + // Load the program. - if (_cg_vprogram != 0) { + _cg_vprofile = cgGetProgramProfile(_cg_vprogram); cgGLLoadProgram(_cg_vprogram); CGerror verror = cgGetError(); if (verror != CG_NO_ERROR) { - const char *str = (const char *)GLP(GetString)(GL_PROGRAM_ERROR_STRING_ARB); - GLCAT.error() << "Could not load Cg vertex program:" << s->get_filename(Shader::ST_vertex) << " (" << - cgGetProfileString(cgGetProgramProfile(_cg_vprogram)) << " " << str << ")\n"; + const char *str = cgGetErrorString(verror); + GLCAT.error() + << "Could not load Cg vertex program: " << s->get_filename(Shader::ST_vertex) + << " (" << cgGetProfileString(_cg_vprofile) << " " << str << ")\n"; release_resources(gsg); } } if (_cg_fprogram != 0) { + _cg_fprofile = cgGetProgramProfile(_cg_fprogram); cgGLLoadProgram(_cg_fprogram); CGerror ferror = cgGetError(); if (ferror != CG_NO_ERROR) { - const char *str = (const char *)GLP(GetString)(GL_PROGRAM_ERROR_STRING_ARB); - GLCAT.error() << "Could not load Cg fragment program:" << s->get_filename(Shader::ST_fragment) << " (" << - cgGetProfileString(cgGetProgramProfile(_cg_fprogram)) << " " << str << ")\n"; + const char *str = cgGetErrorString(ferror); + GLCAT.error() + << "Could not load Cg fragment program: " << s->get_filename(Shader::ST_fragment) + << " (" << cgGetProfileString(_cg_fprofile) << " " << str << ")\n"; release_resources(gsg); } } if (_cg_gprogram != 0) { + _cg_gprofile = cgGetProgramProfile(_cg_gprogram); cgGLLoadProgram(_cg_gprogram); CGerror gerror = cgGetError(); if (gerror != CG_NO_ERROR) { - const char *str = (const char *)GLP(GetString)(GL_PROGRAM_ERROR_STRING_ARB); - GLCAT.error() << "Could not load Cg geometry program:" << s->get_filename(Shader::ST_geometry) << " (" << - cgGetProfileString(cgGetProgramProfile(_cg_gprogram)) << " " << str << ")\n"; + const char *str = cgGetErrorString(gerror); + GLCAT.error() + << "Could not load Cg geometry program: " << s->get_filename(Shader::ST_geometry) + << " (" << cgGetProfileString(_cg_gprofile) << " " << str << ")\n"; release_resources(gsg); } } @@ -832,15 +839,15 @@ bind(GSG *gsg, bool reissue_parameters) { if (_cg_context != 0) { // Bind the shaders. if (_cg_vprogram != 0) { - cgGLEnableProfile(cgGetProgramProfile(_cg_vprogram)); + cgGLEnableProfile(_cg_vprofile); cgGLBindProgram(_cg_vprogram); } if (_cg_fprogram != 0) { - cgGLEnableProfile(cgGetProgramProfile(_cg_fprogram)); + cgGLEnableProfile(_cg_fprofile); cgGLBindProgram(_cg_fprogram); } if (_cg_gprogram != 0) { - cgGLEnableProfile(cgGetProgramProfile(_cg_gprogram)); + cgGLEnableProfile(_cg_gprofile); cgGLBindProgram(_cg_gprogram); } @@ -863,19 +870,22 @@ unbind(GSG *gsg) { #if defined(HAVE_CG) && !defined(OPENGLES) if (_cg_context != 0) { if (_cg_vprogram != 0) { - cgGLDisableProfile(cgGetProgramProfile(_cg_vprogram)); + cgGLUnbindProgram(_cg_vprofile); + cgGLDisableProfile(_cg_vprofile); } if (_cg_fprogram != 0) { - cgGLDisableProfile(cgGetProgramProfile(_cg_fprogram)); + cgGLUnbindProgram(_cg_fprofile); + cgGLDisableProfile(_cg_fprofile); } if (_cg_gprogram != 0) { - cgGLDisableProfile(cgGetProgramProfile(_cg_gprogram)); + cgGLUnbindProgram(_cg_gprofile); + cgGLDisableProfile(_cg_gprofile); } cg_report_errors(); } #endif - + if (_shader->get_language() == Shader::SL_GLSL) { gsg->_glUseProgram(0); } From 04bbf28a71e1385edbbcd7c95b0b51751ae8fa7b Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 16 Feb 2014 10:22:11 +0000 Subject: [PATCH 12/71] Oops, forgot to check in header file as well. --- panda/src/glstuff/glShaderContext_src.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/panda/src/glstuff/glShaderContext_src.h b/panda/src/glstuff/glShaderContext_src.h index 3d56e72002..cb5d1a0065 100755 --- a/panda/src/glstuff/glShaderContext_src.h +++ b/panda/src/glstuff/glShaderContext_src.h @@ -21,6 +21,10 @@ #include "shaderContext.h" #include "deletedChain.h" +#if defined(HAVE_CG) && !defined(OPENGLES) +#include +#endif + class CLP(GraphicsStateGuardian); //////////////////////////////////////////////////////////////////// @@ -57,10 +61,13 @@ private: CGprogram _cg_vprogram; CGprogram _cg_fprogram; CGprogram _cg_gprogram; + CGprofile _cg_vprofile; + CGprofile _cg_fprofile; + CGprofile _cg_gprofile; pvector _cg_parameter_map; #endif - + GLuint _glsl_program; GLuint _glsl_vshader; GLuint _glsl_fshader; From 259d2df0c4f6ab8a018ad6fb6c33ad8f27b7be00 Mon Sep 17 00:00:00 2001 From: enn0x Date: Sun, 16 Feb 2014 21:58:40 +0000 Subject: [PATCH 13/71] Added utility function BulletWorld::test_filter --- panda/src/bullet/bulletWorld.cxx | 45 +++++++++++++++++++++++++++++--- panda/src/bullet/bulletWorld.h | 2 ++ 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/panda/src/bullet/bulletWorld.cxx b/panda/src/bullet/bulletWorld.cxx index 14502a40af..1a01ffb79b 100644 --- a/panda/src/bullet/bulletWorld.cxx +++ b/panda/src/bullet/bulletWorld.cxx @@ -715,10 +715,46 @@ sweep_test_closest(BulletShape *shape, const TransformState &from_ts, const Tran return cb; } +//////////////////////////////////////////////////////////////////// +// Function: BulletWorld::filter_test +// Access: Published +// Description: Performs a test if two bodies should collide or +// not, based on the collision filter setting. +//////////////////////////////////////////////////////////////////// +bool BulletWorld:: +filter_test(PandaNode *node0, PandaNode *node1) const { + + nassertr(node0, false); + nassertr(node1, false); + nassertr(_filter_cb, false); + + btCollisionObject *obj0 = get_collision_object(node0); + btCollisionObject *obj1 = get_collision_object(node1); + + nassertr(obj0, false); + nassertr(obj1, false); + + btBroadphaseProxy *proxy0 = obj0->getBroadphaseHandle(); + btBroadphaseProxy *proxy1 = obj1->getBroadphaseHandle(); + + nassertr(proxy0, false); + nassertr(proxy1, false); + + return _filter_cb->needBroadphaseCollision(proxy0, proxy1); +} + //////////////////////////////////////////////////////////////////// // Function: BulletWorld::contact_test // Access: Published -// Description: +// Description: Performas a test for all bodies which are +// currently in contact with the given body. +// The test returns a BulletContactResult object +// which may contain zero, one or more contacts. +// +// If the optional parameter use_filter is set to +// TRUE this test will consider filter settings. +// Otherwise all objects in contact are reported, +// no matter if they would collide or not. //////////////////////////////////////////////////////////////////// BulletContactResult BulletWorld:: contact_test(PandaNode *node, bool use_filter) const { @@ -742,7 +778,10 @@ contact_test(PandaNode *node, bool use_filter) const { //////////////////////////////////////////////////////////////////// // Function: BulletWorld::contact_pair_test // Access: Published -// Description: +// Description: Performas a test if the two bodies given as +// parameters are in contact or not. +// The test returns a BulletContactResult object +// which may contain zero or one contacts. //////////////////////////////////////////////////////////////////// BulletContactResult BulletWorld:: contact_test_pair(PandaNode *node0, PandaNode *node1) const { @@ -815,7 +854,7 @@ set_group_collision_flag(unsigned int group1, unsigned int group2, bool enable) } //////////////////////////////////////////////////////////////////// -// Function: BulletWorld::get_collision_object +// Function: BulletWorld::get_group_collision_flag // Access: Public // Description: //////////////////////////////////////////////////////////////////// diff --git a/panda/src/bullet/bulletWorld.h b/panda/src/bullet/bulletWorld.h index 3b57138c0f..6a798cde7f 100644 --- a/panda/src/bullet/bulletWorld.h +++ b/panda/src/bullet/bulletWorld.h @@ -126,6 +126,8 @@ PUBLISHED: BulletContactResult contact_test(PandaNode *node, bool use_filter=false) const; BulletContactResult contact_test_pair(PandaNode *node0, PandaNode *node1) const; + bool filter_test(PandaNode *node0, PandaNode *node1) const; + // Manifolds INLINE int get_num_manifolds() const; BulletPersistentManifold *get_manifold(int idx) const; From bf4221da1e57579e073a18afdd1eef5f43c278c0 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 18 Feb 2014 19:03:47 +0000 Subject: [PATCH 14/71] Fix crashes and borks with the Ogg Vorbis loader --- panda/src/movies/vorbisAudioCursor.cxx | 32 +++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/panda/src/movies/vorbisAudioCursor.cxx b/panda/src/movies/vorbisAudioCursor.cxx index b6a8d2d6a6..2f206cbc39 100644 --- a/panda/src/movies/vorbisAudioCursor.cxx +++ b/panda/src/movies/vorbisAudioCursor.cxx @@ -33,6 +33,7 @@ VorbisAudioCursor(VorbisAudio *src, istream *stream) : _bitstream(0) { nassertv(stream != NULL); + nassertv(stream->good()); // Set up the callbacks to read via the VFS. ov_callbacks callbacks; @@ -52,7 +53,10 @@ VorbisAudioCursor(VorbisAudio *src, istream *stream) : return; } - _length = ov_time_total(&_ov, -1); + double time_total = ov_time_total(&_ov, -1); + if (time_total != OV_EINVAL) { + _length = time_total; + } vorbis_info *vi = ov_info(&_ov, -1); _audio_channels = vi->channels; @@ -175,6 +179,12 @@ cb_read_func(void *ptr, size_t size, size_t nmemb, void *datasource) { nassertr(stream != NULL, -1); stream->read((char *)ptr, size * nmemb); + + if (stream->eof()) { + // Gracefully handle EOF. + stream->clear(); + } + return stream->gcount(); } @@ -213,6 +223,26 @@ cb_seek_func(void *datasource, ogg_int64_t offset, int whence) { } if (stream->fail()) { + // This is a fatal error and usually leads to + // a libvorbis crash. + movies_cat.error() + << "Failure to seek to byte " << offset; + + switch (whence) { + case SEEK_CUR: + movies_cat.error(false) + << " from current location!\n"; + break; + + case SEEK_END: + movies_cat.error(false) + << " from end of file!\n"; + break; + + default: + movies_cat.error(false) << "!\n"; + } + return -1; } From 3070f18c24133f546a7f2866daaa9a24ee912224 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 19 Feb 2014 08:38:57 +0000 Subject: [PATCH 15/71] Add assertion in a case where I've seen an occasional crash --- dtool/src/interrogate/interrogateBuilder.cxx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dtool/src/interrogate/interrogateBuilder.cxx b/dtool/src/interrogate/interrogateBuilder.cxx index e98ef7fc87..3624de5352 100644 --- a/dtool/src/interrogate/interrogateBuilder.cxx +++ b/dtool/src/interrogate/interrogateBuilder.cxx @@ -1679,6 +1679,10 @@ get_function(CPPInstance *function, string description, InterrogateFunction &ifunction = InterrogateDatabase::get_ptr()->update_function(index); + // Not 100% sure why, but there's a case where this happens, + // in a case where a typedef shadowed an actual type. ~rdb + nassertr(&ifunction != NULL, 0); + ifunction._flags |= flags; // Also, make sure this particular signature is defined. From 2ae458c2df34cfd759e80e8168daca9d5d5eb67b Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 20 Feb 2014 12:37:08 +0000 Subject: [PATCH 16/71] Fix a bug with tangents and binormals --- panda/src/pgraphnodes/shaderGenerator.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/panda/src/pgraphnodes/shaderGenerator.cxx b/panda/src/pgraphnodes/shaderGenerator.cxx index 7460a102ff..c0422d1aa2 100644 --- a/panda/src/pgraphnodes/shaderGenerator.cxx +++ b/panda/src/pgraphnodes/shaderGenerator.cxx @@ -802,8 +802,8 @@ synthesize_shader(const RenderState *rs) { } if (_map_index_height >= 0) { text << "\t float3 eyedir = mspos_view.xyz - vtx_position.xyz;\n"; - text << "\t l_eyevec.x = dot(vtx_tangent" << _map_index_height << ".xyz, eyedir);\n"; - text << "\t l_eyevec.y = dot(vtx_binormal" << _map_index_height << ".xyz, eyedir);\n"; + text << "\t l_eyevec.x = dot(vtx_" << tangent_input << ".xyz, eyedir);\n"; + text << "\t l_eyevec.y = dot(vtx_" << binormal_input << ".xyz, eyedir);\n"; text << "\t l_eyevec.z = dot(vtx_normal.xyz, eyedir);\n"; text << "\t l_eyevec = normalize(l_eyevec);\n"; } From 5f2c0a6e043fdfc76dce555160e59ac4ac6fe294 Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 20 Feb 2014 13:33:26 +0000 Subject: [PATCH 18/71] Big commit aiding in separation of Python bindings: * Most extension functions have been moved to C++. * In makepanda, Python bindings are compiled into panda3d/.pyd. --- direct/src/directnotify/DirectNotify.py | 2 +- direct/src/directnotify/Notifier.py | 4 +- .../src/extensions/EggGroupNode-extensions.py | 11 - .../src/extensions/EggPrimitive-extensions.py | 8 - .../NodePathCollection-extensions.py | 34 -- direct/src/extensions/OdeBody-extensions.py | 6 - direct/src/extensions/OdeGeom-extensions.py | 44 -- direct/src/extensions/OdeJoint-extensions.py | 39 -- direct/src/extensions/OdeSpace-extensions.py | 32 -- direct/src/extensions/Ramfile-extensions.py | 15 - .../src/extensions/StreamReader-extensions.py | 15 - .../EggGroupNode_extensions.py | 16 - .../EggPrimitive_extensions.py | 13 - .../NodePathCollection_extensions.py | 31 -- .../extensions_native/NodePath_extensions.py | 9 - .../extensions_native/OdeBody_extensions.py | 18 - .../extensions_native/OdeGeom_extensions.py | 60 --- .../extensions_native/OdeJoint_extensions.py | 54 -- .../extensions_native/OdeSpace_extensions.py | 50 -- .../extensions_native/Ramfile_extensions.py | 16 - .../StreamReader_extensions.py | 16 - direct/src/showbase/Messenger.py | 2 +- direct/src/showbase/PythonUtil.py | 5 +- dtool/src/interrogate/functionRemap.cxx | 46 +- dtool/src/interrogate/functionRemap.h | 3 +- .../interfaceMakerPythonNative.cxx | 332 ++++++------ dtool/src/interrogatedb/extension.h | 12 +- dtool/src/interrogatedb/py_panda.cxx | 6 +- dtool/src/prc/streamReader.h | 1 + makepanda/makepanda.py | 486 ++++++++++-------- makepanda/makepandacore.py | 40 +- panda/src/display/Sources.pp | 4 +- panda/src/display/graphicsStateGuardian.cxx | 69 +-- panda/src/display/graphicsStateGuardian.h | 8 +- panda/src/egg/Sources.pp | 7 +- panda/src/egg/eggGroupNode.h | 2 + panda/src/event/pythonTask.cxx | 2 +- panda/src/express/Sources.pp | 12 +- panda/src/express/memoryUsagePointers.cxx | 73 --- panda/src/express/memoryUsagePointers.h | 4 +- panda/src/express/memoryUsagePointers_ext.cxx | 72 +++ panda/src/express/memoryUsagePointers_ext.h | 40 ++ panda/src/express/p3express_ext_composite.cxx | 4 + panda/src/express/ramfile.h | 1 + panda/src/express/ramfile_ext.cxx | 48 ++ panda/src/express/ramfile_ext.h | 40 ++ panda/src/express/streamReader_ext.cxx | 48 ++ panda/src/express/streamReader_ext.h | 40 ++ panda/src/express/virtualFileSystem_ext.cxx | 2 +- panda/src/linmath/lpoint2_ext_src.I | 2 +- panda/src/linmath/lpoint3_ext_src.I | 2 +- panda/src/linmath/lpoint4_ext_src.I | 2 +- panda/src/linmath/lvector2_ext_src.I | 2 +- panda/src/linmath/lvector3_ext_src.I | 2 +- panda/src/linmath/lvector4_ext_src.I | 2 +- panda/src/ode/Sources.pp | 24 +- panda/src/ode/odeAMotorJoint.h | 2 +- panda/src/ode/odeBallJoint.h | 2 +- panda/src/ode/odeBody.h | 4 +- panda/src/ode/odeBoxGeom.h | 2 +- panda/src/ode/odeCappedCylinderGeom.h | 2 +- panda/src/ode/odeContactJoint.h | 2 +- panda/src/ode/odeConvexGeom.h | 2 +- panda/src/ode/odeCylinderGeom.h | 2 +- panda/src/ode/odeFixedJoint.h | 2 +- panda/src/ode/odeGeom.h | 12 +- panda/src/ode/odeHashSpace.h | 2 +- panda/src/ode/odeHeightFieldGeom.h | 2 +- panda/src/ode/odeHinge2Joint.h | 2 +- panda/src/ode/odeHingeJoint.h | 2 +- panda/src/ode/odeJoint.cxx | 2 +- panda/src/ode/odeJoint.h | 12 +- panda/src/ode/odeLMotorJoint.h | 2 +- panda/src/ode/odeNullJoint.h | 2 +- panda/src/ode/odePlane2dJoint.h | 2 +- panda/src/ode/odePlaneGeom.h | 2 +- panda/src/ode/odeQuadTreeSpace.h | 2 +- panda/src/ode/odeRayGeom.h | 2 +- panda/src/ode/odeSimpleSpace.h | 2 +- panda/src/ode/odeSliderJoint.h | 2 +- panda/src/ode/odeSpace.cxx | 74 +-- panda/src/ode/odeSpace.h | 33 +- panda/src/ode/odeSphereGeom.h | 2 +- panda/src/ode/odeTriMeshGeom.h | 5 +- panda/src/ode/odeUniversalJoint.h | 2 +- panda/src/ode/odeUtil.cxx | 57 +- panda/src/ode/odeUtil.h | 18 +- panda/src/ode/p3ode_ext_composite.cxx | 4 + panda/src/pgraph/Sources.pp | 3 + panda/src/pgraph/nodePath.I | 159 ------ panda/src/pgraph/nodePath.cxx | 220 -------- panda/src/pgraph/nodePath.h | 41 +- panda/src/pgraph/nodePathCollection.cxx | 76 +-- panda/src/pgraph/nodePathCollection.h | 6 +- panda/src/putil/Sources.pp | 5 +- panda/src/putil/typedWritable.cxx | 271 +--------- panda/src/putil/typedWritable.h | 29 +- 97 files changed, 1047 insertions(+), 1966 deletions(-) delete mode 100644 direct/src/extensions/EggGroupNode-extensions.py delete mode 100644 direct/src/extensions/EggPrimitive-extensions.py delete mode 100644 direct/src/extensions/NodePathCollection-extensions.py delete mode 100755 direct/src/extensions/OdeBody-extensions.py delete mode 100755 direct/src/extensions/OdeGeom-extensions.py delete mode 100755 direct/src/extensions/OdeJoint-extensions.py delete mode 100755 direct/src/extensions/OdeSpace-extensions.py delete mode 100644 direct/src/extensions/Ramfile-extensions.py delete mode 100644 direct/src/extensions/StreamReader-extensions.py delete mode 100644 direct/src/extensions_native/EggGroupNode_extensions.py delete mode 100644 direct/src/extensions_native/EggPrimitive_extensions.py delete mode 100644 direct/src/extensions_native/NodePathCollection_extensions.py delete mode 100755 direct/src/extensions_native/OdeBody_extensions.py delete mode 100755 direct/src/extensions_native/OdeGeom_extensions.py delete mode 100755 direct/src/extensions_native/OdeJoint_extensions.py delete mode 100755 direct/src/extensions_native/OdeSpace_extensions.py delete mode 100644 direct/src/extensions_native/Ramfile_extensions.py delete mode 100755 direct/src/extensions_native/StreamReader_extensions.py create mode 100644 panda/src/express/memoryUsagePointers_ext.cxx create mode 100644 panda/src/express/memoryUsagePointers_ext.h create mode 100644 panda/src/express/p3express_ext_composite.cxx create mode 100644 panda/src/express/ramfile_ext.cxx create mode 100644 panda/src/express/ramfile_ext.h create mode 100644 panda/src/express/streamReader_ext.cxx create mode 100644 panda/src/express/streamReader_ext.h create mode 100644 panda/src/ode/p3ode_ext_composite.cxx diff --git a/direct/src/directnotify/DirectNotify.py b/direct/src/directnotify/DirectNotify.py index f2b8e351d1..923de31a44 100644 --- a/direct/src/directnotify/DirectNotify.py +++ b/direct/src/directnotify/DirectNotify.py @@ -64,7 +64,7 @@ class DirectNotify: # we're running before ShowBase has finished initializing; and # we import it directly from libpandaexpress, in case we're # running before libpanda.dll is available. - from libpandaexpress import ConfigVariableString + from panda3d.core import ConfigVariableString dconfigParam = ("notify-level-" + categoryName) cvar = ConfigVariableString(dconfigParam, "") diff --git a/direct/src/directnotify/Notifier.py b/direct/src/directnotify/Notifier.py index 5b1508fa99..eb91d5244e 100644 --- a/direct/src/directnotify/Notifier.py +++ b/direct/src/directnotify/Notifier.py @@ -4,7 +4,7 @@ for the programmer/user """ from LoggerGlobal import defaultLogger from direct.showbase import PythonUtil -from libpandaexpress import ConfigVariableBool +from panda3d.core import ConfigVariableBool import time import types import sys @@ -18,7 +18,7 @@ class Notifier: # with the C++ notify system. streamWriter = None if ConfigVariableBool('notify-integrate', True): - from libpandaexpress import StreamWriter, Notify + from panda3d.core import StreamWriter, Notify streamWriter = StreamWriter(Notify.out(), False) showTime = ConfigVariableBool('notify-timestamp', False) diff --git a/direct/src/extensions/EggGroupNode-extensions.py b/direct/src/extensions/EggGroupNode-extensions.py deleted file mode 100644 index 660e556a8f..0000000000 --- a/direct/src/extensions/EggGroupNode-extensions.py +++ /dev/null @@ -1,11 +0,0 @@ - - # For iterating over children - def getChildren(self): - """Returns a Python list of the egg node's children.""" - result = [] - child = self.getFirstChild() - while (child != None): - result.append(child) - child = self.getNextChild() - return result - diff --git a/direct/src/extensions/EggPrimitive-extensions.py b/direct/src/extensions/EggPrimitive-extensions.py deleted file mode 100644 index 60c5bf7392..0000000000 --- a/direct/src/extensions/EggPrimitive-extensions.py +++ /dev/null @@ -1,8 +0,0 @@ - - # For iterating over vertices - def getVertices(self): - """Returns a Python list of the egg primitive's vertices.""" - result = [] - for i in range(self.getNumVertices()): - result.append(self.getVertex(i)) - return result diff --git a/direct/src/extensions/NodePathCollection-extensions.py b/direct/src/extensions/NodePathCollection-extensions.py deleted file mode 100644 index 99a1c7fca6..0000000000 --- a/direct/src/extensions/NodePathCollection-extensions.py +++ /dev/null @@ -1,34 +0,0 @@ - - """ - NodePathCollection-extensions module: contains methods to extend - functionality of the NodePathCollection class - """ - - # For iterating over children - def asList(self): - """Converts a NodePathCollection into a list""" - if self.isEmpty(): - return [] - else: - npList = [] - for nodePathIndex in range(self.getNumPaths()): - npList.append(self.getPath(nodePathIndex)) - return npList - - def getTightBounds(self): - from pandac import Point3 - - if self.getNumPaths() == 0: - return (Point3.Point3(0), Point3.Point3(0)) - - v1, v2 = self.getPath(0).getTightBounds() - for i in range(1, self.getNumPaths()): - v1x, v2x = self.getPath(i).getTightBounds() - v1 = Point3.Point3(min(v1[0], v1x[0]), - min(v1[1], v1x[1]), - min(v1[2], v1x[2])) - v2 = Point3.Point3(max(v2[0], v2x[0]), - max(v2[1], v2x[1]), - max(v2[2], v2x[2])) - - return v1, v2 diff --git a/direct/src/extensions/OdeBody-extensions.py b/direct/src/extensions/OdeBody-extensions.py deleted file mode 100755 index 6b0f60a633..0000000000 --- a/direct/src/extensions/OdeBody-extensions.py +++ /dev/null @@ -1,6 +0,0 @@ - -def getConvertedJoint(self, index): - """ - Return a downcast joint on this body. - """ - return self.getJoint(index).convert() diff --git a/direct/src/extensions/OdeGeom-extensions.py b/direct/src/extensions/OdeGeom-extensions.py deleted file mode 100755 index 0796dc5cf7..0000000000 --- a/direct/src/extensions/OdeGeom-extensions.py +++ /dev/null @@ -1,44 +0,0 @@ - -def convert(self): - """ - Do a sort of pseudo-downcast on this geom in - order to expose its specialized functions. - """ - if self.getGeomClass() == OdeGeom.GCSphere: - return self.convertToSphere() - elif self.getGeomClass() == OdeGeom.GCBox: - return self.convertToBox() - elif self.getGeomClass() == OdeGeom.GCCappedCylinder: - return self.convertToCappedCylinder() - elif self.getGeomClass() == OdeGeom.GCPlane: - return self.convertToPlane() - elif self.getGeomClass() == OdeGeom.GCRay: - return self.convertToRay() - # elif self.getGeomClass() == OdeGeom.GCConvex: - # return self.convertToConvex() - # elif self.getGeomClass() == OdeGeom.GCGeomTransform: - # return self.convertToGeomTransform() - elif self.getGeomClass() == OdeGeom.GCTriMesh: - return self.convertToTriMesh() - # elif self.getGeomClass() == OdeGeom.GCHeightfield: - # return self.convertToHeightfield() - elif self.getGeomClass() == OdeGeom.GCSimpleSpace: - return self.convertToSimpleSpace() - elif self.getGeomClass() == OdeGeom.GCHashSpace: - return self.convertToHashSpace() - elif self.getGeomClass() == OdeGeom.GCQuadTreeSpace: - return self.convertToQuadTreeSpace() - -def getConvertedSpace(self): - """ - """ - return self.getSpace().convert() - -def getAABounds(self): - """ - A more Pythonic way of calling getAABB(). - """ - min = Point3() - max = Point3() - self.getAABB(min,max) - return min,max \ No newline at end of file diff --git a/direct/src/extensions/OdeJoint-extensions.py b/direct/src/extensions/OdeJoint-extensions.py deleted file mode 100755 index 3e3eeccfc8..0000000000 --- a/direct/src/extensions/OdeJoint-extensions.py +++ /dev/null @@ -1,39 +0,0 @@ -def attach(self, body1, body2): - """ - Attach two bodies together. - If either body is None, the other will be attached to the environment. - """ - if body1 and body2: - self.attachBodies(body1, body2) - elif body1 and not body2: - self.attachBody(body1, 0) - elif not body1 and body2: - self.attachBody(body2, 1) - -def convert(self): - """ - Do a sort of pseudo-downcast on this joint in - order to expose its specialized functions. - """ - if self.getJointType() == OdeJoint.JTBall: - return self.convertToBall() - elif self.getJointType() == OdeJoint.JTHinge: - return self.convertToHinge() - elif self.getJointType() == OdeJoint.JTSlider: - return self.convertToSlider() - elif self.getJointType() == OdeJoint.JTContact: - return self.convertToContact() - elif self.getJointType() == OdeJoint.JTUniversal: - return self.convertToUniversal() - elif self.getJointType() == OdeJoint.JTHinge2: - return self.convertToHinge2() - elif self.getJointType() == OdeJoint.JTFixed: - return self.convertToFixed() - elif self.getJointType() == OdeJoint.JTNull: - return self.convertToNull() - elif self.getJointType() == OdeJoint.JTAMotor: - return self.convertToAMotor() - elif self.getJointType() == OdeJoint.JTLMotor: - return self.convertToLMotor() - elif self.getJointType() == OdeJoint.JTPlane2d: - return self.convertToPlane2d() diff --git a/direct/src/extensions/OdeSpace-extensions.py b/direct/src/extensions/OdeSpace-extensions.py deleted file mode 100755 index f110615831..0000000000 --- a/direct/src/extensions/OdeSpace-extensions.py +++ /dev/null @@ -1,32 +0,0 @@ - -def convert(self): - """ - Do a sort of pseudo-downcast on this space in - order to expose its specialized functions. - """ - if self.getClass() == OdeGeom.GCSimpleSpace: - return self.convertToSimpleSpace() - elif self.getClass() == OdeGeom.GCHashSpace: - return self.convertToHashSpace() - elif self.getClass() == OdeGeom.GCQuadTreeSpace: - return self.convertToQuadTreeSpace() - -def getConvertedGeom(self, index): - """ - Return a downcast geom on this body. - """ - return self.getGeom(index).convert() - -def getConvertedSpace(self): - """ - """ - return self.getSpace().convert() - -def getAABounds(self): - """ - A more Pythonic way of calling getAABB() - """ - min = Point3() - max = Point3() - self.getAABB(min,max) - return min,max \ No newline at end of file diff --git a/direct/src/extensions/Ramfile-extensions.py b/direct/src/extensions/Ramfile-extensions.py deleted file mode 100644 index a05a666a24..0000000000 --- a/direct/src/extensions/Ramfile-extensions.py +++ /dev/null @@ -1,15 +0,0 @@ - - """ - Ramfile-extensions module: contains methods to extend functionality - of the Ramfile class - """ - - def readlines(self): - """Reads all the lines at once and returns a list.""" - lines = [] - line = self.readline() - while line: - lines.append(line) - line = self.readline() - return lines - diff --git a/direct/src/extensions/StreamReader-extensions.py b/direct/src/extensions/StreamReader-extensions.py deleted file mode 100644 index 792abde3a6..0000000000 --- a/direct/src/extensions/StreamReader-extensions.py +++ /dev/null @@ -1,15 +0,0 @@ - - """ - StreamReader-extensions module: contains methods to extend functionality - of the StreamReader class - """ - - def readlines(self): - """Reads all the lines at once and returns a list.""" - lines = [] - line = self.readline() - while line: - lines.append(line) - line = self.readline() - return lines - diff --git a/direct/src/extensions_native/EggGroupNode_extensions.py b/direct/src/extensions_native/EggGroupNode_extensions.py deleted file mode 100644 index 6c6173e66f..0000000000 --- a/direct/src/extensions_native/EggGroupNode_extensions.py +++ /dev/null @@ -1,16 +0,0 @@ -#################################################################### -#Dtool_funcToMethod(func, class) -#del func -##################################################################### - # For iterating over children -def getChildren(self): - """Returns a Python list of the egg node's children.""" - result = [] - child = self.getFirstChild() - while (child != None): - result.append(child) - child = self.getNextChild() - return result - -Dtool_funcToMethod(getChildren, EggGroupNode) -del getChildren diff --git a/direct/src/extensions_native/EggPrimitive_extensions.py b/direct/src/extensions_native/EggPrimitive_extensions.py deleted file mode 100644 index 657f99a132..0000000000 --- a/direct/src/extensions_native/EggPrimitive_extensions.py +++ /dev/null @@ -1,13 +0,0 @@ -#################################################################### -#Dtool_funcToMethod(func, class) -#del func -##################################################################### - # For iterating over vertices -def getVertices(self): - """Returns a Python list of the egg primitive's vertices.""" - result = [] - for i in range(self.getNumVertices()): - result.append(self.getVertex(i)) - return result -Dtool_funcToMethod(getVertices, EggPrimitive) -del getVertices diff --git a/direct/src/extensions_native/NodePathCollection_extensions.py b/direct/src/extensions_native/NodePathCollection_extensions.py deleted file mode 100644 index f7f379ab32..0000000000 --- a/direct/src/extensions_native/NodePathCollection_extensions.py +++ /dev/null @@ -1,31 +0,0 @@ -##################################################################### - -# For iterating over children -def asList(self): - """Converts a NodePathCollection into a list""" - print "Warning: NodePathCollection.asList() is no longer needed and deprecated. Iterate on the collection directly instead." - return list(self) - -Dtool_funcToMethod(asList, NodePathCollection) -del asList -#####################################################################3333 - -def getTightBounds(self): - from pandac.PandaModules import Point3 - - if self.getNumPaths() == 0: - return (Point3(0), Point3(0)) - v1, v2 = self.getPath(0).getTightBounds() - for i in range(1, self.getNumPaths()): - v1x, v2x = self.getPath(i).getTightBounds() - v1 = Point3(min(v1[0], v1x[0]), - min(v1[1], v1x[1]), - min(v1[2], v1x[2])) - v2 = Point3(max(v2[0], v2x[0]), - max(v2[1], v2x[1]), - max(v2[2], v2x[2])) - return v1, v2 - -Dtool_funcToMethod(getTightBounds, NodePathCollection) -del getTightBounds -#####################################################################3333 diff --git a/direct/src/extensions_native/NodePath_extensions.py b/direct/src/extensions_native/NodePath_extensions.py index c1a19f3784..7b17dc34f6 100644 --- a/direct/src/extensions_native/NodePath_extensions.py +++ b/direct/src/extensions_native/NodePath_extensions.py @@ -154,15 +154,6 @@ def getAncestry(self): Dtool_funcToMethod(getAncestry, NodePath) del getAncestry ##################################################################### -def getTightBounds(self): - from pandac.PandaModules import Point3 - v1 = Point3(0) - v2 = Point3(0) - self.calcTightBounds(v1, v2) - return v1, v2 -Dtool_funcToMethod(getTightBounds, NodePath) -del getTightBounds -##################################################################### def pPrintString(self, other = None): """ diff --git a/direct/src/extensions_native/OdeBody_extensions.py b/direct/src/extensions_native/OdeBody_extensions.py deleted file mode 100755 index 78b71cb638..0000000000 --- a/direct/src/extensions_native/OdeBody_extensions.py +++ /dev/null @@ -1,18 +0,0 @@ -#################################################################### -#Dtool_funcToMethod(func, class) -#del func -##################################################################### - -""" -OdeBody-extensions module: contains methods to extend functionality -of the OdeBody classe -""" - -def getConvertedJoint(self, index): - """ - Return a downcast joint on this body. - """ - return self.getJoint(index).convert() -Dtool_funcToMethod(getConvertedJoint, OdeBody) -del getConvertedJoint - diff --git a/direct/src/extensions_native/OdeGeom_extensions.py b/direct/src/extensions_native/OdeGeom_extensions.py deleted file mode 100755 index e154e05e55..0000000000 --- a/direct/src/extensions_native/OdeGeom_extensions.py +++ /dev/null @@ -1,60 +0,0 @@ -#################################################################### -#Dtool_funcToMethod(func, class) -#del func -##################################################################### - -""" -OdeGeom-extensions module: contains methods to extend functionality -of the OdeGeom class -""" - -def convert(self): - """ - Do a sort of pseudo-downcast on this geom in - order to expose its specialized functions. - """ - if self.getClass() == OdeGeom.GCSphere: - return self.convertToSphere() - elif self.getClass() == OdeGeom.GCBox: - return self.convertToBox() - elif self.getClass() == OdeGeom.GCCappedCylinder: - return self.convertToCappedCylinder() - elif self.getClass() == OdeGeom.GCPlane: - return self.convertToPlane() - elif self.getClass() == OdeGeom.GCRay: - return self.convertToRay() - # elif self.getClass() == OdeGeom.GCConvex: - # return self.convertToConvex() - # elif self.getClass() == OdeGeom.GCGeomTransform: - # return self.convertToGeomTransform() - elif self.getClass() == OdeGeom.GCTriMesh: - return self.convertToTriMesh() - # elif self.getClass() == OdeGeom.GCHeightfield: - # return self.convertToHeightfield() - elif self.getClass() == OdeGeom.GCSimpleSpace: - return self.convertToSimpleSpace() - elif self.getClass() == OdeGeom.GCHashSpace: - return self.convertToHashSpace() - elif self.getClass() == OdeGeom.GCQuadTreeSpace: - return self.convertToQuadTreeSpace() -Dtool_funcToMethod(convert, OdeGeom) -del convert - -def getConvertedSpace(self): - """ - """ - return self.getSpace().convert() -Dtool_funcToMethod(getConvertedSpace, OdeGeom) -del getConvertedSpace - -def getAABounds(self): - """ - A more Pythonic way of calling getAABB() - """ - min = Point3() - max = Point3() - self.getAABB(min,max) - return min,max -Dtool_funcToMethod(getAABounds, OdeGeom) -del getAABounds - diff --git a/direct/src/extensions_native/OdeJoint_extensions.py b/direct/src/extensions_native/OdeJoint_extensions.py deleted file mode 100755 index e113e6fdfa..0000000000 --- a/direct/src/extensions_native/OdeJoint_extensions.py +++ /dev/null @@ -1,54 +0,0 @@ -#################################################################### -#Dtool_funcToMethod(func, class) -#del func -##################################################################### - -""" -OdeJoint-extensions module: contains methods to extend functionality -of the OdeJoint class -""" - -def attach(self, body1, body2): - """ - Attach two bodies together. - If either body is None, the other will be attached to the environment. - """ - if body1 and body2: - self.attachBodies(body1, body2) - elif body1 and not body2: - self.attachBody(body1, 0) - elif not body1 and body2: - self.attachBody(body2, 1) -Dtool_funcToMethod(attach, OdeJoint) -del attach - -def convert(self): - """ - Do a sort of pseudo-downcast on this joint in - order to expose its specialized functions. - """ - if self.getJointType() == OdeJoint.JTBall: - return self.convertToBall() - elif self.getJointType() == OdeJoint.JTHinge: - return self.convertToHinge() - elif self.getJointType() == OdeJoint.JTSlider: - return self.convertToSlider() - elif self.getJointType() == OdeJoint.JTContact: - return self.convertToContact() - elif self.getJointType() == OdeJoint.JTUniversal: - return self.convertToUniversal() - elif self.getJointType() == OdeJoint.JTHinge2: - return self.convertToHinge2() - elif self.getJointType() == OdeJoint.JTFixed: - return self.convertToFixed() - elif self.getJointType() == OdeJoint.JTNull: - return self.convertToNull() - elif self.getJointType() == OdeJoint.JTAMotor: - return self.convertToAMotor() - elif self.getJointType() == OdeJoint.JTLMotor: - return self.convertToLMotor() - elif self.getJointType() == OdeJoint.JTPlane2d: - return self.convertToPlane2d() -Dtool_funcToMethod(convert, OdeJoint) -del convert - diff --git a/direct/src/extensions_native/OdeSpace_extensions.py b/direct/src/extensions_native/OdeSpace_extensions.py deleted file mode 100755 index 14b3c1e618..0000000000 --- a/direct/src/extensions_native/OdeSpace_extensions.py +++ /dev/null @@ -1,50 +0,0 @@ -#################################################################### -#Dtool_funcToMethod(func, class) -#del func -##################################################################### - -""" -OdeSpace-extensions module: contains methods to extend functionality -of the OdeSpace classe -""" - -def convert(self): - """ - Do a sort of pseudo-downcast on this space in - order to expose its specialized functions. - """ - if self.getClass() == OdeGeom.GCSimpleSpace: - return self.convertToSimpleSpace() - elif self.getClass() == OdeGeom.GCHashSpace: - return self.convertToHashSpace() - elif self.getClass() == OdeGeom.GCQuadTreeSpace: - return self.convertToQuadTreeSpace() -Dtool_funcToMethod(convert, OdeSpace) -del convert - -def getConvertedGeom(self, index): - """ - Return a downcast geom on this space. - """ - return self.getGeom(index).convert() -Dtool_funcToMethod(getConvertedGeom, OdeSpace) -del getConvertedGeom - -def getConvertedSpace(self): - """ - """ - return self.getSpace().convert() -Dtool_funcToMethod(getConvertedSpace, OdeSpace) -del getConvertedSpace - -def getAABounds(self): - """ - A more Pythonic way of calling getAABB() - """ - min = Point3() - max = Point3() - self.getAABB(min,max) - return min,max -Dtool_funcToMethod(getAABounds, OdeSpace) -del getAABounds - diff --git a/direct/src/extensions_native/Ramfile_extensions.py b/direct/src/extensions_native/Ramfile_extensions.py deleted file mode 100644 index c05040c73e..0000000000 --- a/direct/src/extensions_native/Ramfile_extensions.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -Ramfile_extensions module: contains methods to extend functionality -of the Ramfile class -""" - -def readlines(self): - """Reads all the lines at once and returns a list.""" - lines = [] - line = self.readline() - while line: - lines.append(line) - line = self.readline() - return lines - -Dtool_funcToMethod(readlines, Ramfile) -del readlines diff --git a/direct/src/extensions_native/StreamReader_extensions.py b/direct/src/extensions_native/StreamReader_extensions.py deleted file mode 100755 index b4111cc965..0000000000 --- a/direct/src/extensions_native/StreamReader_extensions.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -StreamReader_extensions module: contains methods to extend functionality -of the StreamReader class -""" - -def readlines(self): - """Reads all the lines at once and returns a list.""" - lines = [] - line = self.readline() - while line: - lines.append(line) - line = self.readline() - return lines - -Dtool_funcToMethod(readlines, StreamReader) -del readlines diff --git a/direct/src/showbase/Messenger.py b/direct/src/showbase/Messenger.py index 56ec2958de..44248e6a4e 100644 --- a/direct/src/showbase/Messenger.py +++ b/direct/src/showbase/Messenger.py @@ -7,7 +7,7 @@ from PythonUtil import * from direct.directnotify import DirectNotifyGlobal import types -from libpandaexpress import ConfigVariableBool +from panda3d.core import ConfigVariableBool # If using the Toontown ActiveX launcher, this must be set true. # Also, Panda must be compiled with SIMPLE_THREADS or no HAVE_THREADS diff --git a/direct/src/showbase/PythonUtil.py b/direct/src/showbase/PythonUtil.py index 418bb86f6b..38d4df8663 100644 --- a/direct/src/showbase/PythonUtil.py +++ b/direct/src/showbase/PythonUtil.py @@ -60,10 +60,7 @@ import bisect __report_indent = 3 from direct.directutil import Verify -# Don't import libpandaexpressModules, which doesn't get built until -# genPyCode. -import direct.extensions_native.extension_native_helpers -from libpandaexpress import ConfigVariableBool +from panda3d.core import ConfigVariableBool ScalarTypes = (types.FloatType, types.IntType, types.LongType) diff --git a/dtool/src/interrogate/functionRemap.cxx b/dtool/src/interrogate/functionRemap.cxx index b6bae90ab7..ec583b082d 100644 --- a/dtool/src/interrogate/functionRemap.cxx +++ b/dtool/src/interrogate/functionRemap.cxx @@ -14,7 +14,7 @@ #include "functionRemap.h" #include "typeManager.h" -#include "interrogate.h" +#include "interrogate.h" #include "parameterRemap.h" #include "parameterRemapThis.h" #include "interfaceMaker.h" @@ -324,7 +324,6 @@ make_wrapper_entry(FunctionIndex function_index) { _flags |= F_explicit_self; } } - if (!_void_return) { iwrapper._flags |= InterrogateFunctionWrapper::F_has_return; @@ -340,15 +339,15 @@ make_wrapper_entry(FunctionIndex function_index) { if (_return_value_needs_management) { iwrapper._flags |= InterrogateFunctionWrapper::F_caller_manages; FunctionIndex destructor = _return_value_destructor; - + if (destructor != 0) { iwrapper._return_value_destructor = destructor; - + } else { // We don't need to report this warning, since the FFI code // understands that if the destructor function is zero, it // should use the regular class destructor. - + // nout << "Warning! Destructor for " // << *_return_type->get_orig_type() // << " is unavailable.\n" @@ -397,8 +396,12 @@ get_call_str(const string &container, const vector_string &pexprs) const { // If this function is marked as having an extension function, // call that instead. - if (_extension && !container.empty()) { - call << "invoke_extension(" << container << ")."; + if (_extension) { + if (!container.empty()) { + call << "invoke_extension(" << container << ")."; + } else { + call << "Extension<" << _cpptype->get_local_name(&parser) << ">::"; + } call << _cppfunc->get_local_name(); call << "("; @@ -413,7 +416,7 @@ get_call_str(const string &container, const vector_string &pexprs) const { // If we have a "this" parameter, the calling convention is also // a bit different. call << "(" << container << ")->" << _cppfunc->get_local_name(); - + } else { call << _cppfunc->get_local_name(&parser); } @@ -465,7 +468,7 @@ get_parameter_expr(int n, const vector_string &pexprs) const { //////////////////////////////////////////////////////////////////// bool FunctionRemap:: setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_maker) { - _function_signature = + _function_signature = TypeManager::get_function_signature(_cppfunc, _num_default_parameters); _expression = ifunc._expression; @@ -519,7 +522,7 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak _parameters.push_back(param); _first_true_parameter = 1; } - + // Also check the name of the function. If it's one of the // assignment-style operators, flag it as such. if (fname == "operator =" || @@ -611,7 +614,7 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak } } - if (_return_type == (ParameterRemap *)NULL || + if (_return_type == (ParameterRemap *)NULL || !_return_type->is_valid()) { // If our return type isn't something we can deal with, treat the // function as if it returns NULL. @@ -621,25 +624,25 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak _return_type = interface_maker->remap_parameter(_cpptype, void_type); assert(_return_type != (ParameterRemap *)NULL); } - + // Do we need to manage the return value? - _return_value_needs_management = + _return_value_needs_management = _return_type->return_value_needs_management(); - _return_value_destructor = + _return_value_destructor = _return_type->get_return_value_destructor(); - + // Should we manage a reference count? CPPType *return_type = _return_type->get_new_type(); return_type = TypeManager::resolve_type(return_type, _cppscope); CPPType *return_meat_type = TypeManager::unwrap_pointer(return_type); - + if (manage_reference_counts && TypeManager::is_reference_count_pointer(return_type) && !TypeManager::has_protected_destructor(return_meat_type)) { // Yes! _manage_reference_count = true; _return_value_needs_management = true; - + // This is problematic, because we might not have the class in // question fully defined here, particularly if the class is // defined in some other library. @@ -709,11 +712,18 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak _flags |= F_releasebuffer; } + + } else if (fname == "compare_to" ) { + if (_has_this && _parameters.size() == 2 && + TypeManager::is_integer(_return_type->get_new_type())) { + // It receives one parameter, and returns an integer. + _flags |= F_compare_to; + } } } else if (_type == T_constructor) { if (!_has_this && _parameters.size() == 1) { - if (TypeManager::unwrap(_parameters[0]._remap->get_orig_type()) == + if (TypeManager::unwrap(_parameters[0]._remap->get_orig_type()) == TypeManager::unwrap(_return_type->get_orig_type())) { // If this is the only parameter, and it's the same as the // "this" type, this is a copy constructor. diff --git a/dtool/src/interrogate/functionRemap.h b/dtool/src/interrogate/functionRemap.h index 7ab8224ad1..db23ba0139 100644 --- a/dtool/src/interrogate/functionRemap.h +++ b/dtool/src/interrogate/functionRemap.h @@ -90,6 +90,7 @@ public: F_iter = 0x0100, F_getbuffer = 0x0200, F_releasebuffer = 0x0400, + F_compare_to = 0x0800, }; typedef vector Parameters; @@ -113,7 +114,7 @@ public: string _reported_name; string _wrapper_name; FunctionWrapperIndex _wrapper_index; - + bool _return_value_needs_management; FunctionIndex _return_value_destructor; bool _manage_reference_count; diff --git a/dtool/src/interrogate/interfaceMakerPythonNative.cxx b/dtool/src/interrogate/interfaceMakerPythonNative.cxx index 01ec64a781..564fb41420 100755 --- a/dtool/src/interrogate/interfaceMakerPythonNative.cxx +++ b/dtool/src/interrogate/interfaceMakerPythonNative.cxx @@ -196,8 +196,8 @@ classNameFromCppName(const std::string &cppName, bool mangle) { bool nextCap = false; bool firstChar = true && mangle; - for (std::string::const_iterator chr = cppName.begin(); - chr != cppName.end(); + for (std::string::const_iterator chr = cppName.begin(); + chr != cppName.end(); chr++) { if ((*chr == '_' || *chr == ' ') && mangle) { nextCap = true; @@ -222,10 +222,10 @@ classNameFromCppName(const std::string &cppName, bool mangle) { className = classRenameDictionary[x]._to; } } - + if (className.empty()) { std::string text = "** ERROR ** Renaming class: " + cppName + " to empty string"; - printf("%s",text.c_str()); + printf("%s", text.c_str()); } className = checkKeyword(className); @@ -248,8 +248,8 @@ methodNameFromCppName(const std::string &cppName, const std::string &className, std::string methodName; const std::string badChars("!@#$%^&*()<>,.-=+~{}? "); bool nextCap = false; - for (std::string::const_iterator chr = origName.begin(); - chr != origName.end(); + for (std::string::const_iterator chr = origName.begin(); + chr != origName.end(); chr++) { if ((*chr == '_' || *chr == ' ') && mangle) { nextCap = true; @@ -282,7 +282,7 @@ methodNameFromCppName(const std::string &cppName, const std::string &className, } } } - + // # Mangle names that happen to be python keywords so they are not anymore methodName = checkKeyword(methodName); return methodName; @@ -301,13 +301,13 @@ std::string methodNameFromCppName(InterfaceMaker::Function *func, const std::str bool isInplaceFunction(InterfaceMaker::Function *func) { std::string wname = methodNameFromCppName(func, "", false); - + for (int x = 0; InPlaceSet[x] != NULL; x++) { if (InPlaceSet[x] == wname) { return true; } } - + return false; } @@ -343,13 +343,13 @@ get_slotted_function_def(Object *obj, Function *func, SlottedFunctionDef &def) { if (method_name == "operator -" && is_unary_op) { def._answer_location = "tp_as_number->nb_negative"; def._wrapper_type = WT_no_params; - return true; + return true; } if (method_name == "operator -") { def._answer_location = "tp_as_number->nb_subtract"; def._wrapper_type = WT_numeric_operator; - return true; + return true; } if (method_name == "operator *") { @@ -486,7 +486,7 @@ get_slotted_function_def(Object *obj, Function *func, SlottedFunctionDef &def) { return true; } } - + if (obj->_protocol_types & Object::PT_mapping) { if (func->_flags & FunctionRemap::F_getitem) { def._answer_location = "tp_as_mapping->mp_subscript"; @@ -602,7 +602,7 @@ get_valid_child_classes(std::map &answer, CPPStructTyp ++bi) { const CPPStructType::Base &base = (*bi); -// if (base._vis <= V_public) +// if (base._vis <= V_public) // can_downcast = false; CPPStructType *base_type = TypeManager::resolve_type(base._base)->as_struct_type(); if (base_type != NULL) { @@ -855,13 +855,13 @@ write_class_details(ostream &out, Object *obj) { out << "//********************************************************************\n"; out << "//*** Functions for .. " << cClassName << "\n" ; out << "//********************************************************************\n"; - + for (fi = obj->_methods.begin(); fi != obj->_methods.end(); ++fi) { Function *func = (*fi); if (func) { SlottedFunctionDef def; get_slotted_function_def(obj, func, def); - + ostringstream GetThis; GetThis << " " << cClassName << " *local_this = NULL;\n"; GetThis << " DTOOL_Call_ExtractThisPointerForType(self, &Dtool_" << ClassName << ", (void **)&local_this);\n"; @@ -902,7 +902,7 @@ write_class_details(ostream &out, Object *obj) { for (fi = obj->_constructors.begin(); fi != obj->_constructors.end(); ++fi) { Function *func = (*fi); std::string fname = "int Dtool_Init_" + ClassName + "(PyObject *self, PyObject *args, PyObject *kwds)"; - + write_function_for_name(out, obj, func, fname, "", ClassName, true, coercion_attempted); } if (coercion_attempted) { @@ -912,7 +912,7 @@ write_class_details(ostream &out, Object *obj) { for (fi = obj->_constructors.begin(); fi != obj->_constructors.end(); ++fi) { Function *func = (*fi); std::string fname = "int Dtool_InitNoCoerce_" + ClassName + "(PyObject *self, PyObject *args, PyObject *kwds)"; - + write_function_for_name(out, obj, func, fname, "", ClassName, false, coercion_attempted); } } else { @@ -930,7 +930,7 @@ write_class_details(ostream &out, Object *obj) { for (msi = obj->_make_seqs.begin(); msi != obj->_make_seqs.end(); ++msi) { write_make_seq(out, obj, ClassName, *msi); } - + CPPType *cpptype = TypeManager::resolve_type(obj->_itype._cpptype); std::map details; std::map::iterator di; @@ -944,7 +944,7 @@ write_class_details(ostream &out, Object *obj) { } { // the Cast Converter - + out << "inline void *Dtool_UpcastInterface_" << ClassName << "(PyObject *self, Dtool_PyTypedObject *requested_type) {\n"; out << " Dtool_PyTypedObject *SelfType = ((Dtool_PyInstDef *)self)->_My_Type;\n"; out << " if (SelfType != &Dtool_" << ClassName << ") {\n"; @@ -959,7 +959,7 @@ write_class_details(ostream &out, Object *obj) { for (di = details.begin(); di != details.end(); di++) { if (di->second._is_legal_py_class) { - out << " if (requested_type == &Dtool_" <second._to_class_name) << ") {\n"; + out << " if (requested_type == &Dtool_" << make_safe_name(di->second._to_class_name) << ") {\n"; out << " return " << di->second._up_cast_string << " local_this;\n"; out << " }\n"; } @@ -1099,7 +1099,7 @@ write_module_support(ostream &out, ostream *out_h, InterrogateModuleDef *def) { out << " PyModule_AddStringConstant(module, \"" << name2 << "\", \"" << value << "\");\n"; } } - } + } for (oi = _objects.begin(); oi != _objects.end(); ++oi) { Object *object = (*oi).second; @@ -1131,10 +1131,10 @@ write_module_support(ostream &out, ostream *out_h, InterrogateModuleDef *def) { if (!func->_itype.is_global() && is_function_legal(func)) { string name1 = methodNameFromCppName(func, "", false); string name2 = methodNameFromCppName(func, "", true); - out << " { \"" << name1 << "\", (PyCFunction) &" + out << " { \"" << name1 << "\", (PyCFunction) &" << func->_name << ", METH_VARARGS | METH_KEYWORDS, (char *)" << func->_name << "_comment},\n"; if (name1 != name2) { - out << " { \"" << name2 << "\", (PyCFunction) &" + out << " { \"" << name2 << "\", (PyCFunction) &" << func->_name << ", METH_VARARGS | METH_KEYWORDS, (char *)" << func->_name << "_comment},\n"; } } @@ -1261,11 +1261,11 @@ write_module_class(ostream &out, Object *obj) { string name1 = methodNameFromCppName(func, export_class_name, false); string name2 = methodNameFromCppName(func, export_class_name, true); - out << " { \"" << name1 << "\", (PyCFunction) &" + out << " { \"" << name1 << "\", (PyCFunction) &" << func->_name << ", METH_VARARGS | METH_KEYWORDS, (char *) " << func->_name << "_comment},\n"; ++x; if (name1 != name2) { - out << " { \"" << name2 << "\", (PyCFunction) &" + out << " { \"" << name2 << "\", (PyCFunction) &" << func->_name << ", METH_VARARGS | METH_KEYWORDS, (char *) " << func->_name << "_comment},\n"; ++x; } @@ -1291,7 +1291,7 @@ write_module_class(ostream &out, Object *obj) { if (got_copy && !got_deepcopy) { out << " { \"__deepcopy__\", (PyCFunction) &map_deepcopy_to_copy, METH_VARARGS, NULL},\n"; } - + MakeSeqs::iterator msi; for (msi = obj->_make_seqs.begin(); msi != obj->_make_seqs.end(); ++msi) { string flags = "METH_NOARGS"; @@ -1316,7 +1316,7 @@ write_module_class(ostream &out, Object *obj) { for (di = 0; di < num_derivations; di++) { TypeIndex d_type_Index = obj->_itype.get_derivation(di); if (!interrogate_type_is_unpublished(d_type_Index)) { - const InterrogateType &d_itype = idb->get_type(d_type_Index); + const InterrogateType &d_itype = idb->get_type(d_type_Index); if (is_cpp_type_legal(d_itype._cpptype)) { if (!isExportThisRun(d_itype._cpptype)) { _external_imports.insert(make_safe_name(d_itype.get_scoped_name().c_str())); @@ -1331,7 +1331,7 @@ write_module_class(ostream &out, Object *obj) { for (di = 0; di < num_derivations; di++) { TypeIndex d_type_Index = obj->_itype.get_derivation(di); if (!interrogate_type_is_unpublished(d_type_Index)) { - const InterrogateType &d_itype = idb->get_type(d_type_Index); + const InterrogateType &d_itype = idb->get_type(d_type_Index); if (is_cpp_type_legal(d_itype._cpptype)) { bases.push_back(make_safe_name(d_itype.get_scoped_name().c_str())); } @@ -1362,7 +1362,7 @@ write_module_class(ostream &out, Object *obj) { out << "}\n\n"; } break; - + case WT_one_param: case WT_numeric_operator: // PyObject *func(PyObject *self, PyObject *one) @@ -1518,7 +1518,7 @@ write_module_class(ostream &out, Object *obj) { out << "}\n\n"; } break; - + case WT_inquiry: // int func(PyObject *self) { @@ -1739,7 +1739,7 @@ write_module_class(ostream &out, Object *obj) { has_local_hash = true; } } - + int need_repr = NeedsAReprFunction(obj->_itype); if (need_repr > 0) { out << "//////////////////\n"; @@ -1824,6 +1824,7 @@ write_module_class(ostream &out, Object *obj) { out << "\n"; out << " switch (op) {\n"; + Function *compare_to_func = NULL; for (fi = obj->_methods.begin(); fi != obj->_methods.end(); ++fi) { std::set remaps; Function *func = (*fi); @@ -1851,13 +1852,18 @@ write_module_class(ostream &out, Object *obj) { out << " case Py_GT:\n"; } else if (fname == "operator >=") { out << " case Py_GE:\n"; + } else if (fname == "compare_to") { + compare_to_func = func; + continue; } else { continue; } + ostringstream forward_decl; string expected_params; bool coercion_attempted = false; write_function_forset(out, obj, func, remaps, expected_params, 2, forward_decl, false, true, coercion_attempted, args_cleanup); + out << " if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_TypeError)) {\n"; out << " PyErr_Clear();\n"; out << " }\n"; @@ -1865,12 +1871,48 @@ write_module_class(ostream &out, Object *obj) { has_local_richcompare = true; } - out << " }\n"; + out << " }\n\n"; + out << " " << args_cleanup << "\n"; out << " if (PyErr_Occurred()) {\n"; out << " return (PyObject *)NULL;\n"; out << " }\n\n"; + if (compare_to_func != NULL) { + out << "#if PY_MAJOR_VERSION >= 3\n"; + out << " // All is not lost; we still have the compare_to function to fall back onto.\n"; + out << " PyObject *result = " << compare_to_func->_name << "(self, args, kwds);\n"; + out << " if (result != NULL) {\n"; + out << " if (PyLong_Check(result)) {;\n"; + out << " long cmpval = PyLong_AsLong(result);\n"; + out << " switch (op) {\n"; + out << " case Py_LT:\n"; + out << " return PyBool_FromLong(cmpval < 0);\n"; + out << " case Py_LE:\n"; + out << " return PyBool_FromLong(cmpval <= 0);\n"; + out << " case Py_EQ:\n"; + out << " return PyBool_FromLong(cmpval == 0);\n"; + out << " case Py_NE:\n"; + out << " return PyBool_FromLong(cmpval != 0);\n"; + out << " case Py_GT:\n"; + out << " return PyBool_FromLong(cmpval > 0);\n"; + out << " case Py_GE:\n"; + out << " return PyBool_FromLong(cmpval >= 0);\n"; + out << " }\n"; + out << " }\n"; + out << " Py_DECREF(result);\n"; + out << " }\n\n"; + + out << " if (PyErr_Occurred()) {\n"; + out << " if (PyErr_ExceptionMatches(PyExc_TypeError)) {\n"; + out << " PyErr_Clear();\n"; + out << " } else {\n"; + out << " return (PyObject *)NULL;\n"; + out << " }\n"; + out << " }\n"; + out << "#endif\n\n"; + } + out << " Py_INCREF(Py_NotImplemented);\n"; out << " return Py_NotImplemented;\n"; out << "}\n\n"; @@ -1884,11 +1926,11 @@ write_module_class(ostream &out, Object *obj) { // out << " memset(Dtool_" << ClassName << ".As_PyTypeObject().tp_as_mapping,0,sizeof(PyMappingMethods));\n"; // out << " static Dtool_PyTypedObject *InheritsFrom[] = {"; - // add doc string + // add doc string if (obj->_itype.has_comment()) { out << "#ifndef NDEBUG\n"; out << " // Class documentation string\n"; - out << " Dtool_" << ClassName + out << " Dtool_" << ClassName << ".As_PyTypeObject().tp_doc =\n"; output_quoted(out, 6, obj->_itype.get_comment()); out << ";\n" @@ -2242,7 +2284,7 @@ write_function_for_name(ostream &out1, InterfaceMaker::Object *obj, InterfaceMak if (MapSets.size() > 1) { string expected_params; - + indent(out, 2) << "int parameter_count = 1;\n"; indent(out, 2) << "if (PyTuple_Check(args)) {\n"; indent(out, 2) << " parameter_count = PyTuple_Size(args);\n"; @@ -2250,7 +2292,7 @@ write_function_for_name(ostream &out1, InterfaceMaker::Object *obj, InterfaceMak indent(out, 2) << " parameter_count += PyDict_Size(kwds);\n"; indent(out, 2) << " }\n"; indent(out, 2) << "}\n"; - + indent(out, 2) << "switch (parameter_count) {\n"; for (mii = MapSets.begin(); mii != MapSets.end(); mii ++) { indent(out, 2) << "case " << mii->first << ": {\n"; @@ -2263,7 +2305,7 @@ write_function_for_name(ostream &out1, InterfaceMaker::Object *obj, InterfaceMak indent(out, 4) << "}\n"; indent(out, 4) << "break;\n"; } - + indent(out, 2) << "default:\n"; indent(out, 4) << "{\n"; indent(out, 6) @@ -2275,8 +2317,8 @@ write_function_for_name(ostream &out1, InterfaceMaker::Object *obj, InterfaceMak // Python convention. int add_self = func->_has_this ? 1 : 0; size_t mic; - for (mic = 0, mii = MapSets.begin(); - mii != MapSets.end(); + for (mic = 0, mii = MapSets.begin(); + mii != MapSets.end(); ++mii, ++mic) { if (mic == MapSets.size() - 1) { if (mic == 1) { @@ -2292,16 +2334,16 @@ write_function_for_name(ostream &out1, InterfaceMaker::Object *obj, InterfaceMak } out << " arguments (%d given)\", parameter_count + " << add_self << ");\n"; - + if (constructor) indent(out, 6) << "return -1;\n"; else indent(out, 6) << "return (PyObject *) NULL;\n"; - + indent(out, 4) << "}\n"; indent(out, 4) << "break;\n"; indent(out, 2) << "}\n"; - + out << " if (!PyErr_Occurred()) { // Let error pass on\n"; out << " PyErr_SetString(PyExc_TypeError,\n"; out << " \"Arguments must match one of:\\n\"\n"; @@ -2318,7 +2360,7 @@ write_function_for_name(ostream &out1, InterfaceMaker::Object *obj, InterfaceMak if (!expected_params.empty() && FunctionComment1.empty()) { FunctionComment1 += "C++ Interface:\n"; } - + FunctionComment1 += expected_params; } else { @@ -2345,7 +2387,7 @@ write_function_for_name(ostream &out1, InterfaceMaker::Object *obj, InterfaceMak if (!expected_params.empty() && FunctionComment1.empty()) { FunctionComment1 += "C++ Interface:\n"; } - + FunctionComment1 += expected_params; } @@ -2400,13 +2442,13 @@ int GetParnetDepth(CPPType *type) { int deepest = 0; TypeIndex type_index = builder.get_type(TypeManager::unwrap(TypeManager::resolve_type(type)), false); InterrogateDatabase *idb = InterrogateDatabase::get_ptr(); - const InterrogateType &itype = idb->get_type(type_index); + const InterrogateType &itype = idb->get_type(type_index); if (itype.is_class() || itype.is_struct()) { int num_derivations = itype.number_of_derivations(); for (int di = 0; di < num_derivations; di++) { TypeIndex d_type_Index = itype.get_derivation(di); - const InterrogateType &d_itype = idb->get_type(d_type_Index); + const InterrogateType &d_itype = idb->get_type(d_type_Index); int this_one = GetParnetDepth(d_itype._cpptype); if (this_one > deepest) { deepest = this_one; @@ -2466,10 +2508,10 @@ SortFunctionSet(std::set &remaps) { // A set is defined as all remaps that have the same number of paramaters.. /////////////////////////////////////////////////////////// void InterfaceMakerPythonNative:: -write_function_forset(ostream &out, InterfaceMaker::Object *obj, - InterfaceMaker::Function *func, - std::set &remapsin, - string &expected_params, int indent_level, +write_function_forset(ostream &out, InterfaceMaker::Object *obj, + InterfaceMaker::Function *func, + std::set &remapsin, + string &expected_params, int indent_level, ostream &forward_decl, bool is_inplace, bool coercion_allowed, bool &coercion_attempted, @@ -2489,7 +2531,7 @@ write_function_forset(ostream &out, InterfaceMaker::Object *obj, } while (pn < (int)remap->_parameters.size()) { CPPType *type = remap->_parameters[pn]._remap->get_new_type(); - + if (TypeManager::is_char_pointer(type)) { } else if (TypeManager::is_wchar_pointer(type)) { } else if (TypeManager::is_pointer_to_PyObject(type)) { @@ -2512,17 +2554,17 @@ write_function_forset(ostream &out, InterfaceMaker::Object *obj, indent(out, indent_level) << "{\n"; indent_level += 2; - indent(out, indent_level) + indent(out, indent_level) << "PyObject *coerced = NULL;\n"; - indent(out, indent_level) + indent(out, indent_level) << "PyObject **coerced_ptr = NULL;\n"; - indent(out, indent_level) + indent(out, indent_level) << "bool report_errors = false;\n"; - indent(out, indent_level) + indent(out, indent_level) << "while (true) {\n"; indent_level += 2; } - + if (remapsin.size() > 1) { // There are multiple different overloads for this number of // parameters. Sort them all into order from most-specific to @@ -2536,7 +2578,7 @@ write_function_forset(ostream &out, InterfaceMaker::Object *obj, if (remap->_has_this && !remap->_const_method) { // If it's a non-const method, we only allow a // non-const this. - indent(out, indent_level) + indent(out, indent_level) << "if (!((Dtool_PyInstDef *)self)->_is_const) {\n"; } else { indent(out, indent_level) @@ -2549,7 +2591,7 @@ write_function_forset(ostream &out, InterfaceMaker::Object *obj, write_function_instance(out, obj, func, remap, expected_params, indent_level + 2, false, forward_decl, func->_name, is_inplace, coercion_possible, coercion_attempted, args_cleanup); indent(out, indent_level + 2) << "PyErr_Clear();\n"; - indent(out, indent_level) << "}\n\n"; + indent(out, indent_level) << "}\n\n"; } } } else { @@ -2562,16 +2604,16 @@ write_function_forset(ostream &out, InterfaceMaker::Object *obj, if (remap->_has_this && !remap->_const_method) { // If it's a non-const method, we only allow a // non-const this. - indent(out, indent_level) + indent(out, indent_level) << "if (!((Dtool_PyInstDef *)self)->_is_const) {\n"; } else { indent(out, indent_level) << "{\n"; } - indent(out, indent_level + 2) + indent(out, indent_level + 2) << "// 1-" ; - remap->write_orig_prototype(out, 0); + remap->write_orig_prototype(out, 0); out << "\n" ; write_function_instance(out, obj, func, remap, expected_params, indent_level + 2, true, forward_decl, func->_name, is_inplace, coercion_possible, coercion_attempted, args_cleanup); @@ -2596,7 +2638,7 @@ write_function_forset(ostream &out, InterfaceMaker::Object *obj, << "}\n\n"; } else { indent(out, indent_level) - << "}\n\n"; + << "}\n\n"; } } } @@ -2606,38 +2648,38 @@ write_function_forset(ostream &out, InterfaceMaker::Object *obj, if (coercion_possible) { // Try again, this time with coercion enabled. - indent(out, indent_level) + indent(out, indent_level) << "if (coerced_ptr == NULL && !report_errors) {\n"; - indent(out, indent_level + 2) + indent(out, indent_level + 2) << "coerced_ptr = &coerced;\n"; - indent(out, indent_level + 2) + indent(out, indent_level + 2) << "continue;\n"; - indent(out, indent_level) + indent(out, indent_level) << "}\n"; // No dice. Go back one more time, and this time get the error // message. - indent(out, indent_level) + indent(out, indent_level) << "if (!report_errors) {\n"; - indent(out, indent_level + 2) + indent(out, indent_level + 2) << "report_errors = true;\n"; - indent(out, indent_level + 2) + indent(out, indent_level + 2) << "continue;\n"; - indent(out, indent_level) + indent(out, indent_level) << "}\n"; // We've been through three times. We're done. - indent(out, indent_level) + indent(out, indent_level) << "break;\n"; - + indent_level -= 2; - indent(out, indent_level) + indent(out, indent_level) << "}\n"; - - indent(out, indent_level) + + indent(out, indent_level) << "Py_XDECREF(coerced);\n"; indent_level -= 2; - indent(out, indent_level) + indent(out, indent_level) << "}\n"; } } @@ -2649,11 +2691,11 @@ write_function_forset(ostream &out, InterfaceMaker::Object *obj, // single instance of an overloaded function. //////////////////////////////////////////////////////////////////// void InterfaceMakerPythonNative:: -write_function_instance(ostream &out, InterfaceMaker::Object *obj, +write_function_instance(ostream &out, InterfaceMaker::Object *obj, InterfaceMaker::Function *func1, - FunctionRemap *remap, string &expected_params, - int indent_level, bool errors_fatal, - ostream &ForwardDeclrs, + FunctionRemap *remap, string &expected_params, + int indent_level, bool errors_fatal, + ostream &ForwardDeclrs, const std::string &functionnamestr, bool is_inplace, bool coercion_possible, bool &coercion_attempted, const string &args_cleanup) { @@ -2672,21 +2714,21 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, if (remap->_type == FunctionRemap::T_constructor) { is_constructor = true; } - + if (is_constructor && (remap->_flags & FunctionRemap::F_explicit_self) != 0) { // If we'll be passing "self" to the constructor, we need to // pre-initialize it here. Unfortunately, we can't pre-load the // "this" pointer, but the constructor itself can do this. - + indent(out, indent_level) << "// Pre-initialize self for the constructor\n"; CPPType *orig_type = remap->_return_type->get_orig_type(); TypeIndex type_index = builder.get_type(TypeManager::unwrap(TypeManager::resolve_type(orig_type)), false); InterrogateDatabase *idb = InterrogateDatabase::get_ptr(); - const InterrogateType &itype = idb->get_type(type_index); + const InterrogateType &itype = idb->get_type(type_index); indent(out, indent_level) - << "DTool_PyInit_Finalize(self, NULL, &" + << "DTool_PyInit_Finalize(self, NULL, &" << CLASS_PREFIX << make_safe_name(itype.get_scoped_name()) << ", false, false);\n"; } @@ -2723,7 +2765,7 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, indent(out, indent_level) << "char *" << param_name << ";\n"; format_specifiers += "s"; parameter_list += ", &" + param_name; - + } else if (TypeManager::is_wchar_pointer(orig_type)) { out << "#if PY_MAJOR_VERSION >= 3\n"; indent(out, indent_level) << "PyObject *" << param_name << ";\n"; @@ -2741,7 +2783,7 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, pexpr_string = param_name + "_str"; extra_cleanup += " delete[] " + param_name + "_str;"; - + } else if (TypeManager::is_wstring(orig_type)) { out << "#if PY_MAJOR_VERSION >= 3\n"; indent(out, indent_level) << "PyObject *" << param_name << ";\n"; @@ -2892,7 +2934,7 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, expected_params += classNameFromCppName(obj_type->get_simple_name(), false); if (!remap->_has_this || pn != 0) { - indent(out, indent_level) + indent(out, indent_level) << "PyObject *" << param_name << ";\n"; format_specifiers += "O"; parameter_list += ", &" + param_name; @@ -2900,7 +2942,7 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, TypeIndex p_type_index = builder.get_type(obj_type, false); InterrogateDatabase *idb = InterrogateDatabase::get_ptr(); - const InterrogateType &p_itype = idb->get_type(p_type_index); + const InterrogateType &p_itype = idb->get_type(p_type_index); bool is_copy_constructor = false; if (is_constructor && remap->_parameters.size() == 1 && pn == 0) { @@ -2927,9 +2969,9 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, } ostringstream str; - str << "DTOOL_Call_GetPointerThisClass(" << param_name - << ", &Dtool_" << make_safe_name(p_itype.get_scoped_name()) - << ", " << pn << ", \"" + str << "DTOOL_Call_GetPointerThisClass(" << param_name + << ", &Dtool_" << make_safe_name(p_itype.get_scoped_name()) + << ", " << pn << ", \"" << method_prefix << methodNameFromCppName(func1, class_name, false) << "\", " << const_ok; if (coercion_possible && !is_copy_constructor) { @@ -2971,29 +3013,29 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, pexprs.push_back(pexpr_string); } expected_params += ")\n"; - + // If we got what claimed to be a unary operator, don't check for // parameters, since we won't be getting any anyway. if (!func1->_ifunc.is_unary_op()) { - std::string format_specifiers1 = format_specifiers + ":" + + std::string format_specifiers1 = format_specifiers + ":" + methodNameFromCppName(func1, "", false); indent(out, indent_level) << "static char *keyword_list[] = {" << keyword_list << "NULL};\n"; - if (remap->_parameters.size() == 1 || + if (remap->_parameters.size() == 1 || (remap->_has_this && remap->_parameters.size() == 2)) { indent(out, indent_level) << "// Special case to make operators work\n"; indent(out, indent_level) << "if (PyTuple_Check(args) || (kwds != NULL && PyDict_Check(kwds))) {\n"; indent(out, indent_level) - << " PyArg_ParseTupleAndKeywords(args, kwds, \"" + << " PyArg_ParseTupleAndKeywords(args, kwds, \"" << format_specifiers1 << "\", keyword_list" << parameter_list << ");\n"; indent(out, indent_level) << "} else {\n"; indent(out, indent_level) - << " PyArg_Parse(args, \"" << format_specifiers1 << "\"" + << " PyArg_Parse(args, \"" << format_specifiers1 << "\"" << parameter_list << ");\n"; indent(out, indent_level) << "}\n"; @@ -3002,7 +3044,7 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, } else { indent(out, indent_level) - << "if (PyArg_ParseTupleAndKeywords(args, kwds, \"" + << "if (PyArg_ParseTupleAndKeywords(args, kwds, \"" << format_specifiers1 << "\", keyword_list" << parameter_list << ")) {\n"; } @@ -3048,11 +3090,11 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, indent(out, extra_indent_level) << "}\n"; } - - if (!remap->_void_return && + + if (!remap->_void_return && remap->_return_type->new_type_is_atomic_string()) { // Treat strings as a special case. We don't want to format the - // return expression. + // return expression. if (remap->_blocking) { // With SIMPLE_THREADS, it's important that we never release the // interpreter lock. @@ -3166,7 +3208,7 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, extra_indent_level -= 2; indent(out, extra_indent_level) << "}\n"; } - + indent(out, indent_level) << "}\n"; } @@ -3187,7 +3229,7 @@ pack_return_value(ostream &out, int indent_level, FunctionRemap *remap, TypeIndex type_index = builder.get_type(TypeManager::unwrap(TypeManager::resolve_type(orig_type)), false); InterrogateDatabase *idb = InterrogateDatabase::get_ptr(); - const InterrogateType &itype = idb->get_type(type_index); + const InterrogateType &itype = idb->get_type(type_index); indent(out, indent_level) << "return DTool_PyInit_Finalize(self, " << return_expr << ", &" << CLASS_PREFIX << make_safe_name(itype.get_scoped_name()) << ", true, false);\n"; @@ -3240,7 +3282,7 @@ pack_python_value(ostream &out, int indent_level, FunctionRemap *remap, indent(out, indent_level+2) << assign_stmt << "Py_None;\n"; indent(out, indent_level) << "} else {\n"; indent(out, indent_level+2) - << assign_stmt << "PyUnicode_FromWideChar(" + << assign_stmt << "PyUnicode_FromWideChar(" << return_expr << ", wcslen(" << return_expr << "));\n"; indent(out, indent_level) << "}\n"; @@ -3320,11 +3362,11 @@ pack_python_value(ostream &out, int indent_level, FunctionRemap *remap, indent(out, indent_level) << assign_stmt << "PyInt_FromLong(" << return_expr << ");\n"; out << "#endif\n"; - + } else if (TypeManager::is_float(type)) { indent(out, indent_level) << assign_stmt << "PyFloat_FromDouble(" << return_expr << ");\n"; - + } else if (TypeManager::is_char_pointer(type)) { indent(out, indent_level) << "if (" << return_expr << " == NULL) {\n"; indent(out, indent_level) << " Py_INCREF(Py_None);\n"; @@ -3340,14 +3382,14 @@ pack_python_value(ostream &out, int indent_level, FunctionRemap *remap, out << "#endif\n"; indent(out, indent_level) << "}\n"; - + } else if (TypeManager::is_wchar_pointer(type)) { indent(out, indent_level) << "if (" << return_expr << " == NULL) {\n"; indent(out, indent_level) << " Py_INCREF(Py_None);\n"; indent(out, indent_level+2) << assign_stmt << "Py_None;\n"; indent(out, indent_level) << "} else {\n"; indent(out, indent_level+2) << assign_stmt - << "PyUnicode_FromWideChar(" + << "PyUnicode_FromWideChar(" << return_expr << ", wcslen(" << return_expr << "));\n"; indent(out, indent_level) << "}\n"; @@ -3372,12 +3414,12 @@ pack_python_value(ostream &out, int indent_level, FunctionRemap *remap, } else { const_flag = "false"; } - + if (TypeManager::is_struct(orig_type) || TypeManager::is_ref_to_anything(orig_type)) { if (TypeManager::is_ref_to_anything(orig_type)) { TypeIndex type_index = builder.get_type(TypeManager::unwrap(TypeManager::resolve_type(type)),false); InterrogateDatabase *idb = InterrogateDatabase::get_ptr(); - const InterrogateType &itype = idb->get_type(type_index); + const InterrogateType &itype = idb->get_type(type_index); std::string owns_memory_flag("true"); if (remap->_return_value_needs_management) { @@ -3404,7 +3446,7 @@ pack_python_value(ostream &out, int indent_level, FunctionRemap *remap, if (remap->_manage_reference_count) { TypeIndex type_index = builder.get_type(TypeManager::unwrap(TypeManager::resolve_type(type)),false); InterrogateDatabase *idb = InterrogateDatabase::get_ptr(); - const InterrogateType &itype = idb->get_type(type_index); + const InterrogateType &itype = idb->get_type(type_index); if (!isExportThisRun(itype._cpptype)) { _external_imports.insert(make_safe_name(itype.get_scoped_name())); @@ -3416,7 +3458,7 @@ pack_python_value(ostream &out, int indent_level, FunctionRemap *remap, } else { TypeIndex type_index = builder.get_type(TypeManager::unwrap(TypeManager::resolve_type(orig_type)),false); InterrogateDatabase *idb = InterrogateDatabase::get_ptr(); - const InterrogateType &itype = idb->get_type(type_index); + const InterrogateType &itype = idb->get_type(type_index); if (!isExportThisRun(itype._cpptype)) { _external_imports.insert(make_safe_name(itype.get_scoped_name())); @@ -3430,7 +3472,7 @@ pack_python_value(ostream &out, int indent_level, FunctionRemap *remap, } else if (TypeManager::is_struct(orig_type->as_pointer_type()->_pointing_at)) { TypeIndex type_index = builder.get_type(TypeManager::unwrap(TypeManager::resolve_type(orig_type)),false); InterrogateDatabase *idb = InterrogateDatabase::get_ptr(); - const InterrogateType &itype = idb->get_type(type_index); + const InterrogateType &itype = idb->get_type(type_index); std::string owns_memory_flag("true"); if (remap->_return_value_needs_management) { @@ -3474,7 +3516,7 @@ write_make_seq(ostream &out, Object *obj, const std::string &ClassName, string num_name = methodNameFromCppName(make_seq->_num_name, ClassName, false); string element_name = methodNameFromCppName(make_seq->_element_name, ClassName, false); - out << " return make_list_for_item(self, \"" << num_name + out << " return make_list_for_item(self, \"" << num_name << "\", \"" << element_name << "\");\n"; out << "}\n"; } @@ -3498,7 +3540,7 @@ record_object(TypeIndex type_index) { } InterrogateDatabase *idb = InterrogateDatabase::get_ptr(); - const InterrogateType &itype = idb->get_type(type_index); + const InterrogateType &itype = idb->get_type(type_index); if (!is_cpp_type_legal(itype._cpptype)) { return (Object *)NULL; @@ -3517,7 +3559,7 @@ record_object(TypeIndex type_index) { object->_constructors.push_back(function); } } - + int num_methods = itype.number_of_methods(); int mi; for (mi = 0; mi < num_methods; mi++) { @@ -3526,7 +3568,7 @@ record_object(TypeIndex type_index) { object->_methods.push_back(function); } } - + int num_casts = itype.number_of_casts(); for (mi = 0; mi < num_casts; mi++) { function = record_function(itype, itype.get_cast(mi)); @@ -3534,11 +3576,11 @@ record_object(TypeIndex type_index) { object->_methods.push_back(function); } } - + int num_derivations = itype.number_of_derivations(); for (int di = 0; di < num_derivations; di++) { TypeIndex d_type_Index = itype.get_derivation(di); - idb->get_type(d_type_Index); + idb->get_type(d_type_Index); if (!interrogate_type_is_unpublished(d_type_Index)) { if (itype.derivation_has_upcast(di)) { @@ -3576,7 +3618,7 @@ record_object(TypeIndex type_index) { FunctionIndex func_index = ielement.get_setter(); record_function(itype, func_index); } - } + } object->check_protocols(); @@ -3622,9 +3664,7 @@ generate_wrappers() { for (int fi = 0; fi < num_functions; fi++) { FunctionIndex func_index = idb->get_global_function(fi); record_function(dummy_type, func_index); - } - - + } int num_manifests = idb->get_num_global_manifests(); for (int mi = 0; mi < num_manifests; mi++) { @@ -3634,7 +3674,7 @@ generate_wrappers() { FunctionIndex func_index = iman.get_getter(); record_function(dummy_type, func_index); } - } + } int num_elements = idb->get_num_global_elements(); for (int ei = 0; ei < num_elements; ei++) { @@ -3648,10 +3688,11 @@ generate_wrappers() { FunctionIndex func_index = ielement.get_setter(); record_function(dummy_type, func_index); } - } + } inside_python_native = false; -} -////////////////////////////////////////////// +} + +////////////////////////////////////////////// // Function :is_cpp_type_legal // // is the cpp object supported by by the dtool_py interface.. @@ -3769,7 +3810,7 @@ is_function_legal(Function *func) { return true; } - } + } // printf(" Function Is Marked Illegal %s\n",func->_name.c_str()); return false; @@ -3788,7 +3829,7 @@ isFunctionWithThis(Function *func) { if (remap->_has_this) { return true; } - } + } return false; } @@ -3900,7 +3941,7 @@ DoesInheritFromIsClass(const CPPStructType *inclass, const std::string &name) { return true; } } - } + } return false; } @@ -3928,22 +3969,22 @@ HasAGetKeyFunction(const InterrogateType &itype_class) { ++ii) { CPPInstance *cppinst = (*ii).second; CPPFunctionType *cppfunc = cppinst->_type->as_function_type(); - + if (cppfunc != NULL) { if (cppfunc->_parameters != NULL && - cppfunc->_return_type != NULL && + cppfunc->_return_type != NULL && TypeManager::is_integer(cppfunc->_return_type)) { if (cppfunc->_parameters->_parameters.size() == 0) { return ifunc.get_name(); } } } - } + } } } } return string(); -}; +} //////////////////////////////////////////////////////////////////////////////////////////// // Function : HasAGetClassTypeFunction @@ -3953,7 +3994,7 @@ HasAGetKeyFunction(const InterrogateType &itype_class) { bool InterfaceMakerPythonNative:: HasAGetClassTypeFunction(const InterrogateType &itype_class) { InterrogateDatabase *idb = InterrogateDatabase::get_ptr(); - + int num_methods = itype_class.number_of_methods(); int mi; for (mi = 0; mi < num_methods; mi++) { @@ -3965,7 +4006,7 @@ HasAGetClassTypeFunction(const InterrogateType &itype_class) { for (ii = ifunc._instances->begin();ii != ifunc._instances->end();++ii) { CPPInstance *cppinst = (*ii).second; CPPFunctionType *cppfunc = cppinst->_type->as_function_type(); - + if (cppfunc != NULL && cppfunc->_return_type != NULL && cppfunc->_parameters != NULL) { CPPType *ret_type = TypeManager::unwrap(cppfunc->_return_type); @@ -3976,8 +4017,7 @@ HasAGetClassTypeFunction(const InterrogateType &itype_class) { } } } - - } + } } } } @@ -4013,8 +4053,8 @@ NeedsAStrFunction(const InterrogateType &itype_class) { CPPFunctionType *cppfunc = cppinst->_type->as_function_type(); if (cppfunc != NULL) { - if (cppfunc->_parameters != NULL && - cppfunc->_return_type != NULL && + if (cppfunc->_parameters != NULL && + cppfunc->_return_type != NULL && TypeManager::is_void(cppfunc->_return_type)) { if (cppfunc->_parameters->_parameters.size() == 1) { CPPInstance *inst1 = cppfunc->_parameters->_parameters[0]; @@ -4041,7 +4081,7 @@ NeedsAStrFunction(const InterrogateType &itype_class) { } } } - } + } } } } @@ -4082,8 +4122,8 @@ NeedsAReprFunction(const InterrogateType &itype_class) { CPPFunctionType *cppfunc = cppinst->_type->as_function_type(); if (cppfunc != NULL) { - if (cppfunc->_parameters != NULL && - cppfunc->_return_type != NULL && + if (cppfunc->_parameters != NULL && + cppfunc->_return_type != NULL && TypeManager::is_void(cppfunc->_return_type)) { if (cppfunc->_parameters->_parameters.size() == 2) { CPPInstance *inst1 = cppfunc->_parameters->_parameters[0]; @@ -4102,7 +4142,7 @@ NeedsAReprFunction(const InterrogateType &itype_class) { } } } - } + } } } } @@ -4120,8 +4160,8 @@ NeedsAReprFunction(const InterrogateType &itype_class) { CPPFunctionType *cppfunc = cppinst->_type->as_function_type(); if (cppfunc != NULL) { - if (cppfunc->_parameters != NULL && - cppfunc->_return_type != NULL && + if (cppfunc->_parameters != NULL && + cppfunc->_return_type != NULL && TypeManager::is_void(cppfunc->_return_type)) { if (cppfunc->_parameters->_parameters.size() == 1) { CPPInstance *inst1 = cppfunc->_parameters->_parameters[0]; @@ -4143,7 +4183,7 @@ NeedsAReprFunction(const InterrogateType &itype_class) { } } } - } + } } } } diff --git a/dtool/src/interrogatedb/extension.h b/dtool/src/interrogatedb/extension.h index 53f2e6241d..0d41ef2c60 100644 --- a/dtool/src/interrogatedb/extension.h +++ b/dtool/src/interrogatedb/extension.h @@ -17,10 +17,7 @@ #include "dtoolbase.h" -struct _object; -typedef struct _object PyObject; - -//////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////// // Class : ExtensionBase // Description : This is where all extensions should derive from. // It defines the _self and _this members that can @@ -30,7 +27,6 @@ template class EXPCL_DTOOLCONFIG ExtensionBase { public: T * _this; - PyObject * _self; }; //////////////////////////////////////////////////////////////////// @@ -52,10 +48,9 @@ class EXPCL_DTOOLCONFIG Extension : public ExtensionBase { //////////////////////////////////////////////////////////////////// template inline Extension -invoke_extension(T *ptr, PyObject *self = NULL) { +invoke_extension(T *ptr) { Extension ext; ext._this = ptr; - ext._self = self; return ext; } @@ -65,10 +60,9 @@ invoke_extension(T *ptr, PyObject *self = NULL) { //////////////////////////////////////////////////////////////////// template inline const Extension -invoke_extension(const T *ptr, PyObject *self = NULL) { +invoke_extension(const T *ptr) { Extension ext; ext._this = (T *) ptr; - ext._self = self; return ext; } diff --git a/dtool/src/interrogatedb/py_panda.cxx b/dtool/src/interrogatedb/py_panda.cxx index 786b45d15e..f0123e459e 100644 --- a/dtool/src/interrogatedb/py_panda.cxx +++ b/dtool/src/interrogatedb/py_panda.cxx @@ -49,7 +49,7 @@ bool DtoolCanThisBeAPandaInstance(PyObject *self) { //////////////////////////////////////////////////////////////////////// void DTOOL_Call_ExtractThisPointerForType(PyObject *self, Dtool_PyTypedObject *classdef, void **answer) { if (DtoolCanThisBeAPandaInstance(self)) { - *answer = ((Dtool_PyInstDef *)self)->_My_Type->_Dtool_UpcastInterface(self,classdef); + *answer = ((Dtool_PyInstDef *)self)->_My_Type->_Dtool_UpcastInterface(self, classdef); } else { *answer = NULL; } @@ -298,7 +298,7 @@ void *DTOOL_Call_GetPointerThis(PyObject *self) { // this function relies on the behavior of typed objects in the panda system. // //////////////////////////////////////////////////////////////////////// -PyObject *DTool_CreatePyInstanceTyped(void *local_this_in, Dtool_PyTypedObject & known_class_type, bool memory_rules, bool is_const, int RunTimeType) { +PyObject *DTool_CreatePyInstanceTyped(void *local_this_in, Dtool_PyTypedObject & known_class_type, bool memory_rules, bool is_const, int RunTimeType) { if (local_this_in == NULL) { // Let's not be stupid.. PyErr_SetString(PyExc_TypeError, "C Function Return Null 'this'"); @@ -339,7 +339,7 @@ PyObject *DTool_CreatePyInstanceTyped(void *local_this_in, Dtool_PyTypedObject & // if we get this far .. just wrap the thing in the known type ?? // better than aborting...I guess.... ///////////////////////////////////////////////////// - Dtool_PyInstDef * self = (Dtool_PyInstDef *) known_class_type.As_PyTypeObject().tp_new(&known_class_type.As_PyTypeObject(), NULL, NULL); + Dtool_PyInstDef *self = (Dtool_PyInstDef *) known_class_type.As_PyTypeObject().tp_new(&known_class_type.As_PyTypeObject(), NULL, NULL); if (self != NULL) { self->_ptr_to_object = local_this_in; self->_memory_rules = memory_rules; diff --git a/dtool/src/prc/streamReader.h b/dtool/src/prc/streamReader.h index e8900704cd..8d71cf6b9b 100644 --- a/dtool/src/prc/streamReader.h +++ b/dtool/src/prc/streamReader.h @@ -70,6 +70,7 @@ PUBLISHED: BLOCKING size_t extract_bytes(unsigned char *into, size_t size); BLOCKING string readline(); + EXTENSION(BLOCKING PyObject *readlines()); private: istream *_in; diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index 9fbe316221..2b7564b94f 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -2376,17 +2376,24 @@ CreatePandaVersionFiles() ########################################################################################## # -# Copy the "direct" tree and panda3d.py +# Copy the "direct" tree # ########################################################################################## if (PkgSkip("DIRECT")==0): CopyPythonTree(GetOutputDir() + '/direct', 'direct/src', lib2to3_fixers=['all']) ConditionalWriteFile(GetOutputDir() + '/direct/__init__.py', "") - if (GetTarget() == 'windows'): - CopyFile(GetOutputDir()+'/bin/panda3d.py', 'direct/src/ffi/panda3d.py') - else: - CopyFile(GetOutputDir()+'/lib/panda3d.py', 'direct/src/ffi/panda3d.py') + + # This file used to be copied, but would nowadays cause conflicts. + # Let's get it out of the way in case someone hasn't cleaned their build since. + if os.path.isfile(GetOutputDir() + '/bin/panda3d.py'): + os.remove(GetOutputDir() + '/bin/panda3d.py') + if os.path.isfile(GetOutputDir() + '/lib/panda3d.py'): + os.remove(GetOutputDir() + '/lib/panda3d.py') + + # Don't copy this file, which would cause conflict with our 'panda3d' module. + if os.path.isfile(GetOutputDir() + '/direct/ffi/panda3d.py'): + os.remove(GetOutputDir() + '/direct/ffi/panda3d.py') ########################################################################################## # @@ -2919,11 +2926,11 @@ TargetAdd('p3pandabase_pandabase.obj', opts=OPTS, input='pandabase.cxx') OPTS=['DIR:panda/src/express', 'BUILDING:PANDAEXPRESS', 'OPENSSL', 'ZLIB'] TargetAdd('p3express_composite1.obj', opts=OPTS, input='p3express_composite1.cxx') TargetAdd('p3express_composite2.obj', opts=OPTS, input='p3express_composite2.cxx') +TargetAdd('p3express_ext_composite.obj', opts=OPTS, input='p3express_ext_composite.cxx') IGATEFILES=GetDirectoryContents('panda/src/express', ["*.h", "*_composite*.cxx"]) TargetAdd('libp3express.in', opts=OPTS, input=IGATEFILES) -TargetAdd('libp3express.in', opts=['IMOD:pandaexpress', 'ILIB:libp3express', 'SRCDIR:panda/src/express']) +TargetAdd('libp3express.in', opts=['IMOD:core', 'ILIB:libp3express', 'SRCDIR:panda/src/express']) TargetAdd('libp3express_igate.obj', input='libp3express.in', opts=["DEPENDENCYONLY"]) -TargetAdd('p3express_virtualFileSystem_ext.obj', opts=OPTS, input='virtualFileSystem_ext.cxx') # # DIRECTORY: panda/src/downloader/ @@ -2934,7 +2941,7 @@ TargetAdd('p3downloader_composite1.obj', opts=OPTS, input='p3downloader_composit TargetAdd('p3downloader_composite2.obj', opts=OPTS, input='p3downloader_composite2.cxx') IGATEFILES=GetDirectoryContents('panda/src/downloader', ["*.h", "*_composite*.cxx"]) TargetAdd('libp3downloader.in', opts=OPTS, input=IGATEFILES) -TargetAdd('libp3downloader.in', opts=['IMOD:pandaexpress', 'ILIB:libp3downloader', 'SRCDIR:panda/src/downloader']) +TargetAdd('libp3downloader.in', opts=['IMOD:core', 'ILIB:libp3downloader', 'SRCDIR:panda/src/downloader']) TargetAdd('libp3downloader_igate.obj', input='libp3downloader.in', opts=["DEPENDENCYONLY"]) # @@ -2943,20 +2950,14 @@ TargetAdd('libp3downloader_igate.obj', input='libp3downloader.in', opts=["DEPEND OPTS=['DIR:panda/metalibs/pandaexpress', 'BUILDING:PANDAEXPRESS', 'ZLIB'] TargetAdd('pandaexpress_pandaexpress.obj', opts=OPTS, input='pandaexpress.cxx') -TargetAdd('libpandaexpress_module.obj', input='libp3downloader.in') -TargetAdd('libpandaexpress_module.obj', input='libp3express.in') -TargetAdd('libpandaexpress_module.obj', opts=['ADVAPI', 'OPENSSL']) -TargetAdd('libpandaexpress_module.obj', opts=['IMOD:pandaexpress', 'ILIB:libpandaexpress']) - TargetAdd('libpandaexpress.dll', input='pandaexpress_pandaexpress.obj') -TargetAdd('libpandaexpress.dll', input='libpandaexpress_module.obj') TargetAdd('libpandaexpress.dll', input='p3downloader_composite1.obj') TargetAdd('libpandaexpress.dll', input='p3downloader_composite2.obj') TargetAdd('libpandaexpress.dll', input='libp3downloader_igate.obj') TargetAdd('libpandaexpress.dll', input='p3express_composite1.obj') TargetAdd('libpandaexpress.dll', input='p3express_composite2.obj') +TargetAdd('libpandaexpress.dll', input='p3express_ext_composite.obj') TargetAdd('libpandaexpress.dll', input='libp3express_igate.obj') -TargetAdd('libpandaexpress.dll', input='p3express_virtualFileSystem_ext.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']) @@ -2972,7 +2973,7 @@ if (not RUNTIME): TargetAdd('p3pipeline_contextSwitch.obj', opts=OPTS, input='contextSwitch.c') IGATEFILES=GetDirectoryContents('panda/src/pipeline', ["*.h", "*_composite*.cxx"]) TargetAdd('libp3pipeline.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3pipeline.in', opts=['IMOD:panda', 'ILIB:libp3pipeline', 'SRCDIR:panda/src/pipeline']) + TargetAdd('libp3pipeline.in', opts=['IMOD:core', 'ILIB:libp3pipeline', 'SRCDIR:panda/src/pipeline']) TargetAdd('libp3pipeline_igate.obj', input='libp3pipeline.in', opts=["DEPENDENCYONLY"]) # @@ -2986,8 +2987,9 @@ if (not RUNTIME): IGATEFILES=GetDirectoryContents('panda/src/putil', ["*.h", "*_composite*.cxx"]) IGATEFILES.remove("test_bam.h") TargetAdd('libp3putil.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3putil.in', opts=['IMOD:panda', 'ILIB:libp3putil', 'SRCDIR:panda/src/putil']) + TargetAdd('libp3putil.in', opts=['IMOD:core', 'ILIB:libp3putil', 'SRCDIR:panda/src/putil']) TargetAdd('libp3putil_igate.obj', input='libp3putil.in', opts=["DEPENDENCYONLY"]) + TargetAdd('p3putil_typedWritable_ext.obj', opts=OPTS, input='typedWritable_ext.cxx') # # DIRECTORY: panda/src/audio/ @@ -2998,7 +3000,7 @@ if (not RUNTIME): TargetAdd('p3audio_composite1.obj', opts=OPTS, input='p3audio_composite1.cxx') IGATEFILES=["audio.h"] TargetAdd('libp3audio.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3audio.in', opts=['IMOD:panda', 'ILIB:libp3audio', 'SRCDIR:panda/src/audio']) + TargetAdd('libp3audio.in', opts=['IMOD:core', 'ILIB:libp3audio', 'SRCDIR:panda/src/audio']) TargetAdd('libp3audio_igate.obj', input='libp3audio.in', opts=["DEPENDENCYONLY"]) # @@ -3011,7 +3013,7 @@ if (not RUNTIME): TargetAdd('p3event_composite2.obj', opts=OPTS, input='p3event_composite2.cxx') IGATEFILES=GetDirectoryContents('panda/src/event', ["*.h", "*_composite*.cxx"]) TargetAdd('libp3event.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3event.in', opts=['IMOD:panda', 'ILIB:libp3event', 'SRCDIR:panda/src/event']) + TargetAdd('libp3event.in', opts=['IMOD:core', 'ILIB:libp3event', 'SRCDIR:panda/src/event']) TargetAdd('libp3event_igate.obj', input='libp3event.in', opts=["DEPENDENCYONLY"]) # @@ -3030,7 +3032,7 @@ if (not RUNTIME): IGATEFILES.remove('lmat_ops.h') IGATEFILES.remove('cast_to_float.h') TargetAdd('libp3linmath.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3linmath.in', opts=['IMOD:panda', 'ILIB:libp3linmath', 'SRCDIR:panda/src/linmath']) + TargetAdd('libp3linmath.in', opts=['IMOD:core', 'ILIB:libp3linmath', 'SRCDIR:panda/src/linmath']) TargetAdd('libp3linmath_igate.obj', input='libp3linmath.in', opts=["DEPENDENCYONLY"]) # @@ -3043,7 +3045,7 @@ if (not RUNTIME): TargetAdd('p3mathutil_composite2.obj', opts=OPTS, input='p3mathutil_composite2.cxx') IGATEFILES=GetDirectoryContents('panda/src/mathutil', ["*.h", "*_composite*.cxx"]) TargetAdd('libp3mathutil.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3mathutil.in', opts=['IMOD:panda', 'ILIB:libp3mathutil', 'SRCDIR:panda/src/mathutil']) + TargetAdd('libp3mathutil.in', opts=['IMOD:core', 'ILIB:libp3mathutil', 'SRCDIR:panda/src/mathutil']) TargetAdd('libp3mathutil_igate.obj', input='libp3mathutil.in', opts=["DEPENDENCYONLY"]) # @@ -3055,7 +3057,7 @@ if (not RUNTIME): TargetAdd('p3gsgbase_composite1.obj', opts=OPTS, input='p3gsgbase_composite1.cxx') IGATEFILES=GetDirectoryContents('panda/src/gsgbase', ["*.h", "*_composite*.cxx"]) TargetAdd('libp3gsgbase.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3gsgbase.in', opts=['IMOD:panda', 'ILIB:libp3gsgbase', 'SRCDIR:panda/src/gsgbase']) + TargetAdd('libp3gsgbase.in', opts=['IMOD:core', 'ILIB:libp3gsgbase', 'SRCDIR:panda/src/gsgbase']) TargetAdd('libp3gsgbase_igate.obj', input='libp3gsgbase.in', opts=["DEPENDENCYONLY"]) # @@ -3068,7 +3070,7 @@ if (not RUNTIME): TargetAdd('p3pnmimage_composite2.obj', opts=OPTS, input='p3pnmimage_composite2.cxx') IGATEFILES=GetDirectoryContents('panda/src/pnmimage', ["*.h", "*_composite*.cxx"]) TargetAdd('libp3pnmimage.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3pnmimage.in', opts=['IMOD:panda', 'ILIB:libp3pnmimage', 'SRCDIR:panda/src/pnmimage']) + TargetAdd('libp3pnmimage.in', opts=['IMOD:core', 'ILIB:libp3pnmimage', 'SRCDIR:panda/src/pnmimage']) TargetAdd('libp3pnmimage_igate.obj', input='libp3pnmimage.in', opts=["DEPENDENCYONLY"]) # @@ -3080,7 +3082,7 @@ if (not RUNTIME): TargetAdd('p3nativenet_composite1.obj', opts=OPTS, input='p3nativenet_composite1.cxx') IGATEFILES=GetDirectoryContents('panda/src/nativenet', ["*.h", "*_composite*.cxx"]) TargetAdd('libp3nativenet.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3nativenet.in', opts=['IMOD:panda', 'ILIB:libp3nativenet', 'SRCDIR:panda/src/nativenet']) + TargetAdd('libp3nativenet.in', opts=['IMOD:core', 'ILIB:libp3nativenet', 'SRCDIR:panda/src/nativenet']) TargetAdd('libp3nativenet_igate.obj', input='libp3nativenet.in', opts=["DEPENDENCYONLY"]) # @@ -3094,7 +3096,7 @@ if (not RUNTIME): IGATEFILES=GetDirectoryContents('panda/src/net', ["*.h", "*_composite*.cxx"]) IGATEFILES.remove("datagram_ui.h") TargetAdd('libp3net.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3net.in', opts=['IMOD:panda', 'ILIB:libp3net', 'SRCDIR:panda/src/net']) + TargetAdd('libp3net.in', opts=['IMOD:core', 'ILIB:libp3net', 'SRCDIR:panda/src/net']) TargetAdd('libp3net_igate.obj', input='libp3net.in', opts=["DEPENDENCYONLY"]) # @@ -3107,7 +3109,7 @@ if (not RUNTIME): TargetAdd('p3pstatclient_composite2.obj', opts=OPTS, input='p3pstatclient_composite2.cxx') IGATEFILES=GetDirectoryContents('panda/src/pstatclient', ["*.h", "*_composite*.cxx"]) TargetAdd('libp3pstatclient.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3pstatclient.in', opts=['IMOD:panda', 'ILIB:libp3pstatclient', 'SRCDIR:panda/src/pstatclient']) + TargetAdd('libp3pstatclient.in', opts=['IMOD:core', 'ILIB:libp3pstatclient', 'SRCDIR:panda/src/pstatclient']) TargetAdd('libp3pstatclient_igate.obj', input='libp3pstatclient.in', opts=["DEPENDENCYONLY"]) # @@ -3121,7 +3123,7 @@ if (not RUNTIME): IGATEFILES=GetDirectoryContents('panda/src/gobj', ["*.h", "*_composite*.cxx"]) if ("cgfx_states.h" in IGATEFILES): IGATEFILES.remove("cgfx_states.h") TargetAdd('libp3gobj.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3gobj.in', opts=['IMOD:panda', 'ILIB:libp3gobj', 'SRCDIR:panda/src/gobj']) + TargetAdd('libp3gobj.in', opts=['IMOD:core', 'ILIB:libp3gobj', 'SRCDIR:panda/src/gobj']) TargetAdd('libp3gobj_igate.obj', input='libp3gobj.in', opts=["DEPENDENCYONLY"]) TargetAdd('p3gobj_geomVertexArrayData_ext.obj', opts=OPTS, input='geomVertexArrayData_ext.cxx') @@ -3135,7 +3137,7 @@ if (not RUNTIME): TargetAdd('p3pgraphnodes_composite2.obj', opts=OPTS, input='p3pgraphnodes_composite2.cxx') IGATEFILES=GetDirectoryContents('panda/src/pgraphnodes', ["*.h", "*_composite*.cxx"]) TargetAdd('libp3pgraphnodes.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3pgraphnodes.in', opts=['IMOD:panda', 'ILIB:libp3pgraphnodes', 'SRCDIR:panda/src/pgraphnodes']) + TargetAdd('libp3pgraphnodes.in', opts=['IMOD:core', 'ILIB:libp3pgraphnodes', 'SRCDIR:panda/src/pgraphnodes']) TargetAdd('libp3pgraphnodes_igate.obj', input='libp3pgraphnodes.in', opts=["DEPENDENCYONLY"]) # @@ -3151,8 +3153,10 @@ if (not RUNTIME): TargetAdd('p3pgraph_composite4.obj', opts=OPTS, input='p3pgraph_composite4.cxx') IGATEFILES=GetDirectoryContents('panda/src/pgraph', ["*.h", "nodePath.cxx", "*_composite*.cxx"]) TargetAdd('libp3pgraph.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3pgraph.in', opts=['IMOD:panda', 'ILIB:libp3pgraph', 'SRCDIR:panda/src/pgraph']) + TargetAdd('libp3pgraph.in', opts=['IMOD:core', 'ILIB:libp3pgraph', 'SRCDIR:panda/src/pgraph']) TargetAdd('libp3pgraph_igate.obj', input='libp3pgraph.in', opts=["DEPENDENCYONLY","BIGOBJ"]) + TargetAdd('p3pgraph_nodePath_ext.obj', opts=OPTS, input='nodePath_ext.cxx') + TargetAdd('p3pgraph_nodePathCollection_ext.obj', opts=OPTS, input='nodePathCollection_ext.cxx') # # DIRECTORY: panda/src/cull/ @@ -3164,7 +3168,7 @@ if (not RUNTIME): TargetAdd('p3cull_composite2.obj', opts=OPTS, input='p3cull_composite2.cxx') IGATEFILES=GetDirectoryContents('panda/src/cull', ["*.h", "*_composite*.cxx"]) TargetAdd('libp3cull.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3cull.in', opts=['IMOD:panda', 'ILIB:libp3cull', 'SRCDIR:panda/src/cull']) + TargetAdd('libp3cull.in', opts=['IMOD:core', 'ILIB:libp3cull', 'SRCDIR:panda/src/cull']) TargetAdd('libp3cull_igate.obj', input='libp3cull.in', opts=["DEPENDENCYONLY"]) # @@ -3179,7 +3183,7 @@ if (not RUNTIME): IGATEFILES.remove('movingPart.h') IGATEFILES.remove('animChannelFixed.h') TargetAdd('libp3chan.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3chan.in', opts=['IMOD:panda', 'ILIB:libp3chan', 'SRCDIR:panda/src/chan']) + TargetAdd('libp3chan.in', opts=['IMOD:core', 'ILIB:libp3chan', 'SRCDIR:panda/src/chan']) TargetAdd('libp3chan_igate.obj', input='libp3chan.in', opts=["DEPENDENCYONLY"]) @@ -3192,7 +3196,7 @@ if (not RUNTIME): TargetAdd('p3char_composite2.obj', opts=OPTS, input='p3char_composite2.cxx') IGATEFILES=GetDirectoryContents('panda/src/char', ["*.h", "*_composite*.cxx"]) TargetAdd('libp3char.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3char.in', opts=['IMOD:panda', 'ILIB:libp3char', 'SRCDIR:panda/src/char']) + TargetAdd('libp3char.in', opts=['IMOD:core', 'ILIB:libp3char', 'SRCDIR:panda/src/char']) TargetAdd('libp3char_igate.obj', input='libp3char.in', opts=["DEPENDENCYONLY"]) # @@ -3205,7 +3209,7 @@ if (not RUNTIME): TargetAdd('p3dgraph_composite2.obj', opts=OPTS, input='p3dgraph_composite2.cxx') IGATEFILES=GetDirectoryContents('panda/src/dgraph', ["*.h", "*_composite*.cxx"]) TargetAdd('libp3dgraph.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3dgraph.in', opts=['IMOD:panda', 'ILIB:libp3dgraph', 'SRCDIR:panda/src/dgraph']) + TargetAdd('libp3dgraph.in', opts=['IMOD:core', 'ILIB:libp3dgraph', 'SRCDIR:panda/src/dgraph']) TargetAdd('libp3dgraph_igate.obj', input='libp3dgraph.in', opts=["DEPENDENCYONLY"]) # @@ -3219,8 +3223,9 @@ if (not RUNTIME): IGATEFILES=GetDirectoryContents('panda/src/display', ["*.h", "*_composite*.cxx"]) IGATEFILES.remove("renderBuffer.h") TargetAdd('libp3display.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3display.in', opts=['IMOD:panda', 'ILIB:libp3display', 'SRCDIR:panda/src/display']) + TargetAdd('libp3display.in', opts=['IMOD:core', 'ILIB:libp3display', 'SRCDIR:panda/src/display']) TargetAdd('libp3display_igate.obj', input='libp3display.in', opts=["DEPENDENCYONLY"]) + TargetAdd('p3display_graphicsStateGuardian_ext.obj', opts=OPTS, input='graphicsStateGuardian_ext.cxx') if RTDIST and GetTarget() == 'darwin': OPTS=['DIR:panda/src/display'] @@ -3237,7 +3242,7 @@ if (not RUNTIME): TargetAdd('p3device_composite2.obj', opts=OPTS, input='p3device_composite2.cxx') IGATEFILES=GetDirectoryContents('panda/src/device', ["*.h", "*_composite*.cxx"]) TargetAdd('libp3device.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3device.in', opts=['IMOD:panda', 'ILIB:libp3device', 'SRCDIR:panda/src/device']) + TargetAdd('libp3device.in', opts=['IMOD:core', 'ILIB:libp3device', 'SRCDIR:panda/src/device']) TargetAdd('libp3device_igate.obj', input='libp3device.in', opts=["DEPENDENCYONLY"]) # @@ -3249,7 +3254,7 @@ if (PkgSkip("FREETYPE")==0 and not RUNTIME): TargetAdd('p3pnmtext_composite1.obj', opts=OPTS, input='p3pnmtext_composite1.cxx') IGATEFILES=GetDirectoryContents('panda/src/pnmtext', ["*.h", "*_composite*.cxx"]) TargetAdd('libp3pnmtext.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3pnmtext.in', opts=['IMOD:panda', 'ILIB:libp3pnmtext', 'SRCDIR:panda/src/pnmtext']) + TargetAdd('libp3pnmtext.in', opts=['IMOD:core', 'ILIB:libp3pnmtext', 'SRCDIR:panda/src/pnmtext']) TargetAdd('libp3pnmtext_igate.obj', input='libp3pnmtext.in', opts=["DEPENDENCYONLY"]) # @@ -3262,7 +3267,7 @@ if (not RUNTIME): TargetAdd('p3text_composite2.obj', opts=OPTS, input='p3text_composite2.cxx') IGATEFILES=GetDirectoryContents('panda/src/text', ["*.h", "*_composite*.cxx"]) TargetAdd('libp3text.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3text.in', opts=['IMOD:panda', 'ILIB:libp3text', 'SRCDIR:panda/src/text']) + TargetAdd('libp3text.in', opts=['IMOD:core', 'ILIB:libp3text', 'SRCDIR:panda/src/text']) TargetAdd('libp3text_igate.obj', input='libp3text.in', opts=["DEPENDENCYONLY"]) # @@ -3274,7 +3279,7 @@ if (not RUNTIME): TargetAdd('p3movies_composite1.obj', opts=OPTS, input='p3movies_composite1.cxx') IGATEFILES=GetDirectoryContents('panda/src/movies', ["*.h", "*_composite*.cxx"]) TargetAdd('libp3movies.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3movies.in', opts=['IMOD:panda', 'ILIB:libp3movies', 'SRCDIR:panda/src/movies']) + TargetAdd('libp3movies.in', opts=['IMOD:core', 'ILIB:libp3movies', 'SRCDIR:panda/src/movies']) TargetAdd('libp3movies_igate.obj', input='libp3movies.in', opts=["DEPENDENCYONLY"]) # @@ -3288,7 +3293,7 @@ if (not RUNTIME): TargetAdd('p3grutil_composite2.obj', opts=OPTS, input='p3grutil_composite2.cxx') IGATEFILES=GetDirectoryContents('panda/src/grutil', ["*.h", "*_composite*.cxx"]) TargetAdd('libp3grutil.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3grutil.in', opts=['IMOD:panda', 'ILIB:libp3grutil', 'SRCDIR:panda/src/grutil']) + TargetAdd('libp3grutil.in', opts=['IMOD:core', 'ILIB:libp3grutil', 'SRCDIR:panda/src/grutil']) TargetAdd('libp3grutil_igate.obj', input='libp3grutil.in', opts=["DEPENDENCYONLY"]) # @@ -3301,7 +3306,7 @@ if (not RUNTIME): TargetAdd('p3tform_composite2.obj', opts=OPTS, input='p3tform_composite2.cxx') IGATEFILES=GetDirectoryContents('panda/src/tform', ["*.h", "*_composite*.cxx"]) TargetAdd('libp3tform.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3tform.in', opts=['IMOD:panda', 'ILIB:libp3tform', 'SRCDIR:panda/src/tform']) + TargetAdd('libp3tform.in', opts=['IMOD:core', 'ILIB:libp3tform', 'SRCDIR:panda/src/tform']) TargetAdd('libp3tform_igate.obj', input='libp3tform.in', opts=["DEPENDENCYONLY"]) # @@ -3314,7 +3319,7 @@ if (not RUNTIME): TargetAdd('p3collide_composite2.obj', opts=OPTS, input='p3collide_composite2.cxx') IGATEFILES=GetDirectoryContents('panda/src/collide', ["*.h", "*_composite*.cxx"]) TargetAdd('libp3collide.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3collide.in', opts=['IMOD:panda', 'ILIB:libp3collide', 'SRCDIR:panda/src/collide']) + TargetAdd('libp3collide.in', opts=['IMOD:core', 'ILIB:libp3collide', 'SRCDIR:panda/src/collide']) TargetAdd('libp3collide_igate.obj', input='libp3collide.in', opts=["DEPENDENCYONLY"]) # @@ -3327,7 +3332,7 @@ if (not RUNTIME): TargetAdd('p3parametrics_composite2.obj', opts=OPTS, input='p3parametrics_composite2.cxx') IGATEFILES=GetDirectoryContents('panda/src/parametrics', ["*.h", "*_composite*.cxx"]) TargetAdd('libp3parametrics.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3parametrics.in', opts=['IMOD:panda', 'ILIB:libp3parametrics', 'SRCDIR:panda/src/parametrics']) + TargetAdd('libp3parametrics.in', opts=['IMOD:core', 'ILIB:libp3parametrics', 'SRCDIR:panda/src/parametrics']) TargetAdd('libp3parametrics_igate.obj', input='libp3parametrics.in', opts=["DEPENDENCYONLY"]) # @@ -3340,7 +3345,7 @@ if (not RUNTIME): TargetAdd('p3pgui_composite2.obj', opts=OPTS, input='p3pgui_composite2.cxx') IGATEFILES=GetDirectoryContents('panda/src/pgui', ["*.h", "*_composite*.cxx"]) TargetAdd('libp3pgui.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3pgui.in', opts=['IMOD:panda', 'ILIB:libp3pgui', 'SRCDIR:panda/src/pgui']) + TargetAdd('libp3pgui.in', opts=['IMOD:core', 'ILIB:libp3pgui', 'SRCDIR:panda/src/pgui']) TargetAdd('libp3pgui_igate.obj', input='libp3pgui.in', opts=["DEPENDENCYONLY"]) # @@ -3362,7 +3367,7 @@ if (not RUNTIME): TargetAdd('p3recorder_composite2.obj', opts=OPTS, input='p3recorder_composite2.cxx') IGATEFILES=GetDirectoryContents('panda/src/recorder', ["*.h", "*_composite*.cxx"]) TargetAdd('libp3recorder.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3recorder.in', opts=['IMOD:panda', 'ILIB:libp3recorder', 'SRCDIR:panda/src/recorder']) + TargetAdd('libp3recorder.in', opts=['IMOD:core', 'ILIB:libp3recorder', 'SRCDIR:panda/src/recorder']) TargetAdd('libp3recorder_igate.obj', input='libp3recorder.in', opts=["DEPENDENCYONLY"]) # @@ -3381,7 +3386,7 @@ if (not RUNTIME): TargetAdd('p3dxml_composite1.obj', opts=OPTS, input='p3dxml_composite1.cxx') IGATEFILES=GetDirectoryContents('panda/src/dxml', ["*.h", "p3dxml_composite1.cxx"]) TargetAdd('libp3dxml.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3dxml.in', opts=['IMOD:panda', 'ILIB:libp3dxml', 'SRCDIR:panda/src/dxml']) + TargetAdd('libp3dxml.in', opts=['IMOD:core', 'ILIB:libp3dxml', 'SRCDIR:panda/src/dxml']) TargetAdd('libp3dxml_igate.obj', input='libp3dxml.in', opts=["DEPENDENCYONLY"]) # @@ -3395,38 +3400,7 @@ if (not RUNTIME): TargetAdd('panda_panda.obj', opts=OPTS, input='panda.cxx') - TargetAdd('libpanda_module.obj', input='libp3recorder.in') - TargetAdd('libpanda_module.obj', input='libp3pgraphnodes.in') - TargetAdd('libpanda_module.obj', input='libp3pgraph.in') - TargetAdd('libpanda_module.obj', input='libp3cull.in') - TargetAdd('libpanda_module.obj', input='libp3grutil.in') - TargetAdd('libpanda_module.obj', input='libp3chan.in') - TargetAdd('libpanda_module.obj', input='libp3pstatclient.in') - TargetAdd('libpanda_module.obj', input='libp3char.in') - TargetAdd('libpanda_module.obj', input='libp3collide.in') - TargetAdd('libpanda_module.obj', input='libp3device.in') - TargetAdd('libpanda_module.obj', input='libp3dgraph.in') - TargetAdd('libpanda_module.obj', input='libp3display.in') - TargetAdd('libpanda_module.obj', input='libp3pipeline.in') - TargetAdd('libpanda_module.obj', input='libp3event.in') - TargetAdd('libpanda_module.obj', input='libp3gobj.in') - TargetAdd('libpanda_module.obj', input='libp3gsgbase.in') - TargetAdd('libpanda_module.obj', input='libp3linmath.in') - TargetAdd('libpanda_module.obj', input='libp3mathutil.in') - TargetAdd('libpanda_module.obj', input='libp3parametrics.in') - TargetAdd('libpanda_module.obj', input='libp3pnmimage.in') - TargetAdd('libpanda_module.obj', input='libp3text.in') - TargetAdd('libpanda_module.obj', input='libp3tform.in') - TargetAdd('libpanda_module.obj', input='libp3putil.in') - TargetAdd('libpanda_module.obj', input='libp3audio.in') - TargetAdd('libpanda_module.obj', input='libp3nativenet.in') - TargetAdd('libpanda_module.obj', input='libp3net.in') - TargetAdd('libpanda_module.obj', input='libp3pgui.in') - TargetAdd('libpanda_module.obj', input='libp3movies.in') - TargetAdd('libpanda_module.obj', input='libp3dxml.in') - TargetAdd('libpanda.dll', input='panda_panda.obj') - TargetAdd('libpanda.dll', input='libpanda_module.obj') TargetAdd('libpanda.dll', input='p3recorder_composite1.obj') TargetAdd('libpanda.dll', input='p3recorder_composite2.obj') TargetAdd('libpanda.dll', input='libp3recorder_igate.obj') @@ -3478,7 +3452,6 @@ if (not RUNTIME): TargetAdd('libpanda.dll', input='p3gobj_composite1.obj') TargetAdd('libpanda.dll', input='p3gobj_composite2.obj') TargetAdd('libpanda.dll', input='libp3gobj_igate.obj') - TargetAdd('libpanda.dll', input='p3gobj_geomVertexArrayData_ext.obj') TargetAdd('libpanda.dll', input='p3gsgbase_composite1.obj') TargetAdd('libpanda.dll', input='libp3gsgbase_igate.obj') TargetAdd('libpanda.dll', input='p3linmath_composite1.obj') @@ -3521,17 +3494,62 @@ if (not RUNTIME): TargetAdd('libpanda.dll', input='libp3dtoolconfig.dll') TargetAdd('libpanda.dll', input='libp3dtool.dll') + TargetAdd('libpanda.dll', input='p3putil_typedWritable_ext.obj') + TargetAdd('libpanda.dll', input='p3gobj_geomVertexArrayData_ext.obj') + TargetAdd('libpanda.dll', input='p3pgraph_nodePath_ext.obj') + TargetAdd('libpanda.dll', input='p3pgraph_nodePathCollection_ext.obj') + TargetAdd('libpanda.dll', input='p3display_graphicsStateGuardian_ext.obj') + if PkgSkip("FREETYPE")==0: TargetAdd('libpanda.dll', input="p3pnmtext_composite1.obj") TargetAdd('libpanda.dll', input="libp3pnmtext_igate.obj") - TargetAdd('libpanda_module.obj', input='libp3pnmtext.in') - - TargetAdd('libpanda_module.obj', opts=OPTS) - TargetAdd('libpanda_module.obj', opts=['IMOD:panda', 'ILIB:libpanda']) TargetAdd('libpanda.dll', dep='dtool_have_freetype.dat') TargetAdd('libpanda.dll', opts=OPTS) + TargetAdd('core_module.obj', input='libp3downloader.in') + TargetAdd('core_module.obj', input='libp3express.in') + + TargetAdd('core_module.obj', input='libp3recorder.in') + TargetAdd('core_module.obj', input='libp3pgraphnodes.in') + TargetAdd('core_module.obj', input='libp3pgraph.in') + TargetAdd('core_module.obj', input='libp3cull.in') + TargetAdd('core_module.obj', input='libp3grutil.in') + TargetAdd('core_module.obj', input='libp3chan.in') + TargetAdd('core_module.obj', input='libp3pstatclient.in') + TargetAdd('core_module.obj', input='libp3char.in') + TargetAdd('core_module.obj', input='libp3collide.in') + TargetAdd('core_module.obj', input='libp3device.in') + TargetAdd('core_module.obj', input='libp3dgraph.in') + TargetAdd('core_module.obj', input='libp3display.in') + TargetAdd('core_module.obj', input='libp3pipeline.in') + TargetAdd('core_module.obj', input='libp3event.in') + TargetAdd('core_module.obj', input='libp3gobj.in') + TargetAdd('core_module.obj', input='libp3gsgbase.in') + TargetAdd('core_module.obj', input='libp3linmath.in') + TargetAdd('core_module.obj', input='libp3mathutil.in') + TargetAdd('core_module.obj', input='libp3parametrics.in') + TargetAdd('core_module.obj', input='libp3pnmimage.in') + TargetAdd('core_module.obj', input='libp3text.in') + TargetAdd('core_module.obj', input='libp3tform.in') + TargetAdd('core_module.obj', input='libp3putil.in') + TargetAdd('core_module.obj', input='libp3audio.in') + TargetAdd('core_module.obj', input='libp3nativenet.in') + TargetAdd('core_module.obj', input='libp3net.in') + TargetAdd('core_module.obj', input='libp3pgui.in') + TargetAdd('core_module.obj', input='libp3movies.in') + TargetAdd('core_module.obj', input='libp3dxml.in') + + if PkgSkip("FREETYPE")==0: + TargetAdd('core_module.obj', input='libp3pnmtext.in') + + TargetAdd('core_module.obj', opts=['IMOD:core', 'ILIB:core']) + + TargetAdd('core.pyd', input='core_module.obj') + TargetAdd('core.pyd', input='libpandaexpress.dll') + TargetAdd('core.pyd', input='libpanda.dll') + TargetAdd('core.pyd', opts=['PYTHON']) + # # DIRECTORY: panda/src/vision/ # @@ -3539,21 +3557,25 @@ if (not RUNTIME): if (PkgSkip("VISION") ==0) and (not RUNTIME): OPTS=['DIR:panda/src/vision', 'BUILDING:VISION', 'ARTOOLKIT', 'OPENCV', 'DX9', 'DIRECTCAM', 'JPEG'] TargetAdd('p3vision_composite1.obj', opts=OPTS, input='p3vision_composite1.cxx') - IGATEFILES=GetDirectoryContents('panda/src/vision', ["*.h", "*_composite*.cxx"]) - TargetAdd('libp3vision.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3vision.in', opts=['IMOD:p3vision', 'ILIB:libp3vision', 'SRCDIR:panda/src/vision']) - TargetAdd('libp3vision_igate.obj', input='libp3vision.in', opts=["DEPENDENCYONLY"]) - - TargetAdd('libp3vision_module.obj', input='libp3vision.in') - TargetAdd('libp3vision_module.obj', opts=OPTS) - TargetAdd('libp3vision_module.obj', opts=['IMOD:p3vision', 'ILIB:libp3vision']) TargetAdd('libp3vision.dll', input='p3vision_composite1.obj') - TargetAdd('libp3vision.dll', input='libp3vision_igate.obj') - TargetAdd('libp3vision.dll', input='libp3vision_module.obj') TargetAdd('libp3vision.dll', input=COMMON_PANDA_LIBS) TargetAdd('libp3vision.dll', opts=OPTS) + IGATEFILES=GetDirectoryContents('panda/src/vision', ["*.h", "*_composite*.cxx"]) + TargetAdd('libp3vision.in', opts=OPTS, input=IGATEFILES) + TargetAdd('libp3vision.in', opts=['IMOD:vision', 'ILIB:libp3vision', 'SRCDIR:panda/src/vision']) + TargetAdd('libp3vision_igate.obj', input='libp3vision.in', opts=["DEPENDENCYONLY"]) + + TargetAdd('vision_module.obj', input='libp3vision.in') + TargetAdd('vision_module.obj', opts=OPTS) + TargetAdd('vision_module.obj', opts=['IMOD:vision', 'ILIB:vision']) + + TargetAdd('vision.pyd', input='vision_module.obj') + TargetAdd('vision.pyd', input='libp3vision_igate.obj') + TargetAdd('vision.pyd', input='libp3vision.dll') + TargetAdd('vision.pyd', opts=['PYTHON']) + # # DIRECTORY: panda/src/rocket/ # @@ -3561,24 +3583,27 @@ if (PkgSkip("VISION") ==0) and (not RUNTIME): if (PkgSkip("ROCKET") ==0) and (not RUNTIME): OPTS=['DIR:panda/src/rocket', 'BUILDING:ROCKET', 'ROCKET'] TargetAdd('p3rocket_composite1.obj', opts=OPTS, input='p3rocket_composite1.cxx') + + TargetAdd('libp3rocket.dll', input='p3rocket_composite1.obj') + TargetAdd('libp3rocket.dll', input=COMMON_PANDA_LIBS) + TargetAdd('libp3rocket.dll', opts=OPTS) + IGATEFILES=GetDirectoryContents('panda/src/rocket', ["rocketInputHandler.h", "rocketInputHandler.cxx", "rocketRegion.h", "rocketRegion.cxx", "rocketRegion_ext.h"]) TargetAdd('libp3rocket.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3rocket.in', opts=['IMOD:p3rocket', 'ILIB:libp3rocket', 'SRCDIR:panda/src/rocket']) + TargetAdd('libp3rocket.in', opts=['IMOD:rocket', 'ILIB:libp3rocket', 'SRCDIR:panda/src/rocket']) TargetAdd('libp3rocket_igate.obj', input='libp3rocket.in', opts=["DEPENDENCYONLY"]) - - TargetAdd('libp3rocket_module.obj', input='libp3rocket.in') - TargetAdd('libp3rocket_module.obj', opts=OPTS) - TargetAdd('libp3rocket_module.obj', opts=['IMOD:p3rocket', 'ILIB:libp3rocket']) - TargetAdd('p3rocket_rocketRegion_ext.obj', opts=OPTS, input='rocketRegion_ext.cxx') - TargetAdd('libp3rocket.dll', input='p3rocket_composite1.obj') - TargetAdd('libp3rocket.dll', input='libp3rocket_igate.obj') - TargetAdd('libp3rocket.dll', input='libp3rocket_module.obj') - TargetAdd('libp3rocket.dll', input='p3rocket_rocketRegion_ext.obj') - TargetAdd('libp3rocket.dll', input=COMMON_PANDA_LIBS) - TargetAdd('libp3rocket.dll', opts=OPTS) + TargetAdd('rocket_module.obj', input='libp3rocket.in') + TargetAdd('rocket_module.obj', opts=OPTS) + TargetAdd('rocket_module.obj', opts=['IMOD:rocket', 'ILIB:rocket']) + + TargetAdd('rocket.pyd', input='rocket_module.obj') + TargetAdd('rocket.pyd', input='libp3rocket_igate.obj') + TargetAdd('rocket.pyd', input='p3rocket_rocketRegion_ext.obj') + TargetAdd('rocket.pyd', input='libp3rocket.dll') + TargetAdd('rocket.pyd', opts=['PYTHON']) # # DIRECTORY: panda/src/p3awesomium @@ -3588,19 +3613,22 @@ if PkgSkip("AWESOMIUM") == 0 and not RUNTIME: TargetAdd('pandaawesomium_composite1.obj', opts=OPTS, input='pandaawesomium_composite1.cxx') IGATEFILES=GetDirectoryContents('panda/src/awesomium', ["*.h", "*_composite1.cxx"]) TargetAdd('libp3awesomium.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3awesomium.in', opts=['IMOD:p3awesomium', 'ILIB:libp3awesomium', 'SRCDIR:panda/src/awesomium']) + TargetAdd('libp3awesomium.in', opts=['IMOD:awesomium', 'ILIB:libp3awesomium', 'SRCDIR:panda/src/awesomium']) TargetAdd('libp3awesomium_igate.obj', input='libp3awesomium.in', opts=["DEPENDENCYONLY"]) - TargetAdd('libp3awesomium_module.obj', input='libp3awesomium.in') - TargetAdd('libp3awesomium_module.obj', opts=OPTS) - TargetAdd('libp3awesomium_module.obj', opts=['IMOD:p3awesomium', 'ILIB:libp3awesomium']) - TargetAdd('libp3awesomium.dll', input='pandaawesomium_composite1.obj') - TargetAdd('libp3awesomium.dll', input='libp3awesomium_igate.obj') - TargetAdd('libp3awesomium.dll', input='libp3awesomium_module.obj') TargetAdd('libp3awesomium.dll', input=COMMON_PANDA_LIBS) TargetAdd('libp3awesomium.dll', opts=OPTS) + TargetAdd('awesomium_module.obj', input='libp3awesomium.in') + TargetAdd('awesomium_module.obj', opts=OPTS) + TargetAdd('awesomium_module.obj', opts=['IMOD:awesomium', 'ILIB:awesomium']) + + TargetAdd('awesomium.pyd', input='awesomium_module.obj') + TargetAdd('awesomium.pyd', input='libp3awesomium_igate.obj') + TargetAdd('awesomium.pyd', input='libp3awesomium.dll') + TargetAdd('awesomium.pyd', opts=['PYTHON']) + # # DIRECTORY: panda/src/p3skel # @@ -3610,7 +3638,7 @@ if (PkgSkip('SKEL')==0) and (not RUNTIME): TargetAdd('p3skel_composite1.obj', opts=OPTS, input='p3skel_composite1.cxx') IGATEFILES=GetDirectoryContents("panda/src/skel", ["*.h", "*_composite*.cxx"]) TargetAdd('libp3skel.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3skel.in', opts=['IMOD:pandaskel', 'ILIB:libp3skel', 'SRCDIR:panda/src/skel']) + TargetAdd('libp3skel.in', opts=['IMOD:skel', 'ILIB:libp3skel', 'SRCDIR:panda/src/skel']) TargetAdd('libp3skel_igate.obj', input='libp3skel.in', opts=["DEPENDENCYONLY"]) # @@ -3620,16 +3648,19 @@ if (PkgSkip('SKEL')==0) and (not RUNTIME): if (PkgSkip('SKEL')==0) and (not RUNTIME): OPTS=['BUILDING:PANDASKEL', 'ADVAPI'] - TargetAdd('libpandaskel_module.obj', input='libp3skel.in') - TargetAdd('libpandaskel_module.obj', opts=OPTS) - TargetAdd('libpandaskel_module.obj', opts=['IMOD:pandaskel', 'ILIB:libpandaskel']) - TargetAdd('libpandaskel.dll', input='p3skel_composite1.obj') - TargetAdd('libpandaskel.dll', input='libp3skel_igate.obj') - TargetAdd('libpandaskel.dll', input='libpandaskel_module.obj') TargetAdd('libpandaskel.dll', input=COMMON_PANDA_LIBS) TargetAdd('libpandaskel.dll', opts=OPTS) + TargetAdd('skel_module.obj', input='libp3skel.in') + TargetAdd('skel_module.obj', opts=OPTS) + TargetAdd('skel_module.obj', opts=['IMOD:skel', 'ILIB:skel']) + + TargetAdd('skel.pyd', input='skel_module.obj') + TargetAdd('skel.pyd', input='libp3skel_igate.obj') + TargetAdd('skel.pyd', input='libpandaskel.dll') + TargetAdd('skel.pyd', opts=['PYTHON']) + # # DIRECTORY: panda/src/distort/ # @@ -3639,7 +3670,7 @@ if (PkgSkip('PANDAFX')==0) and (not RUNTIME): TargetAdd('p3distort_composite1.obj', opts=OPTS, input='p3distort_composite1.cxx') IGATEFILES=GetDirectoryContents('panda/src/distort', ["*.h", "*_composite*.cxx"]) TargetAdd('libp3distort.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3distort.in', opts=['IMOD:pandafx', 'ILIB:libp3distort', 'SRCDIR:panda/src/distort']) + TargetAdd('libp3distort.in', opts=['IMOD:fx', 'ILIB:libp3distort', 'SRCDIR:panda/src/distort']) TargetAdd('libp3distort_igate.obj', input='libp3distort.in', opts=["DEPENDENCYONLY"]) # @@ -3650,38 +3681,44 @@ if (PkgSkip('PANDAFX')==0) and (not RUNTIME): OPTS=['DIR:panda/metalibs/pandafx', 'DIR:panda/src/distort', 'BUILDING:PANDAFX', 'NVIDIACG'] TargetAdd('pandafx_pandafx.obj', opts=OPTS, input='pandafx.cxx') - TargetAdd('libpandafx_module.obj', input='libp3distort.in') - TargetAdd('libpandafx_module.obj', opts=OPTS) - TargetAdd('libpandafx_module.obj', opts=['IMOD:pandafx', 'ILIB:libpandafx']) - TargetAdd('libpandafx.dll', input='pandafx_pandafx.obj') - TargetAdd('libpandafx.dll', input='libpandafx_module.obj') TargetAdd('libpandafx.dll', input='p3distort_composite1.obj') - TargetAdd('libpandafx.dll', input='libp3distort_igate.obj') TargetAdd('libpandafx.dll', input=COMMON_PANDA_LIBS) TargetAdd('libpandafx.dll', opts=['ADVAPI', 'NVIDIACG']) + TargetAdd('fx_module.obj', input='libp3distort.in') + TargetAdd('fx_module.obj', opts=OPTS) + TargetAdd('fx_module.obj', opts=['IMOD:fx', 'ILIB:fx']) + + TargetAdd('fx.pyd', input='fx_module.obj') + TargetAdd('fx.pyd', input='libp3distort_igate.obj') + TargetAdd('fx.pyd', input='libpandafx.dll') + TargetAdd('fx.pyd', opts=['PYTHON']) + # # DIRECTORY: panda/src/vrpn/ # if (PkgSkip("VRPN")==0 and not RUNTIME): + TargetAdd('libp3vrpn.dll', input='p3vrpn_composite1.obj') + TargetAdd('libp3vrpn.dll', input=COMMON_PANDA_LIBS) + TargetAdd('libp3vrpn.dll', opts=['VRPN']) + OPTS=['DIR:panda/src/vrpn', 'BUILDING:VRPN', 'VRPN'] TargetAdd('p3vrpn_composite1.obj', opts=OPTS, input='p3vrpn_composite1.cxx') IGATEFILES=GetDirectoryContents('panda/src/vrpn', ["*.h", "*_composite*.cxx"]) TargetAdd('libp3vrpn.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3vrpn.in', opts=['IMOD:p3vrpn', 'ILIB:libp3vrpn', 'SRCDIR:panda/src/vrpn']) + TargetAdd('libp3vrpn.in', opts=['IMOD:vrpn', 'ILIB:libp3vrpn', 'SRCDIR:panda/src/vrpn']) TargetAdd('libp3vrpn_igate.obj', input='libp3vrpn.in', opts=["DEPENDENCYONLY"]) - TargetAdd('libp3vrpn_module.obj', input='libp3vrpn.in') - TargetAdd('libp3vrpn_module.obj', opts=OPTS) - TargetAdd('libp3vrpn_module.obj', opts=['IMOD:p3vrpn', 'ILIB:libp3vrpn']) + TargetAdd('vrpn_module.obj', input='libp3vrpn.in') + TargetAdd('vrpn_module.obj', opts=OPTS) + TargetAdd('vrpn_module.obj', opts=['IMOD:vrpn', 'ILIB:vrpn']) - TargetAdd('libp3vrpn.dll', input='libp3vrpn_module.obj') - TargetAdd('libp3vrpn.dll', input='p3vrpn_composite1.obj') - TargetAdd('libp3vrpn.dll', input='libp3vrpn_igate.obj') - TargetAdd('libp3vrpn.dll', input=COMMON_PANDA_LIBS) - TargetAdd('libp3vrpn.dll', opts=['VRPN']) + TargetAdd('vrpn.pyd', input='vrpn_module.obj') + TargetAdd('vrpn.pyd', input='libp3vrpn_igate.obj') + TargetAdd('vrpn.pyd', input='libp3vrpn.dll') + TargetAdd('vrpn.pyd', opts=['PYTHON']) # # DIRECTORY: panda/src/ffmpeg @@ -3843,8 +3880,9 @@ if (not RUNTIME): IGATEFILES=GetDirectoryContents('panda/src/egg', ["*.h", "*_composite*.cxx"]) if "parser.h" in IGATEFILES: IGATEFILES.remove("parser.h") TargetAdd('libp3egg.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3egg.in', opts=['IMOD:pandaegg', 'ILIB:libp3egg', 'SRCDIR:panda/src/egg']) + TargetAdd('libp3egg.in', opts=['IMOD:egg', 'ILIB:libp3egg', 'SRCDIR:panda/src/egg']) TargetAdd('libp3egg_igate.obj', input='libp3egg.in', opts=["DEPENDENCYONLY"]) + TargetAdd('p3egg_eggGroupNode_ext.obj', opts=OPTS, input='eggGroupNode_ext.cxx') # # DIRECTORY: panda/src/egg2pg/ @@ -3856,7 +3894,7 @@ if (not RUNTIME): TargetAdd('p3egg2pg_composite2.obj', opts=OPTS, input='p3egg2pg_composite2.cxx') IGATEFILES=['load_egg_file.h'] TargetAdd('libp3egg2pg.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3egg2pg.in', opts=['IMOD:pandaegg', 'ILIB:libp3egg2pg', 'SRCDIR:panda/src/egg2pg']) + TargetAdd('libp3egg2pg.in', opts=['IMOD:egg', 'ILIB:libp3egg2pg', 'SRCDIR:panda/src/egg2pg']) TargetAdd('libp3egg2pg_igate.obj', input='libp3egg2pg.in', opts=["DEPENDENCYONLY"]) # @@ -3905,24 +3943,28 @@ if (not RUNTIME): OPTS=['DIR:panda/metalibs/pandaegg', 'DIR:panda/src/egg', 'BUILDING:PANDAEGG'] TargetAdd('pandaegg_pandaegg.obj', opts=OPTS, input='pandaegg.cxx') - TargetAdd('libpandaegg_module.obj', input='libp3egg2pg.in') - TargetAdd('libpandaegg_module.obj', input='libp3egg.in') - TargetAdd('libpandaegg_module.obj', opts=OPTS) - TargetAdd('libpandaegg_module.obj', opts=['IMOD:pandaegg', 'ILIB:libpandaegg']) - TargetAdd('libpandaegg.dll', input='pandaegg_pandaegg.obj') - TargetAdd('libpandaegg.dll', input='libpandaegg_module.obj') TargetAdd('libpandaegg.dll', input='p3egg2pg_composite1.obj') TargetAdd('libpandaegg.dll', input='p3egg2pg_composite2.obj') - TargetAdd('libpandaegg.dll', input='libp3egg2pg_igate.obj') TargetAdd('libpandaegg.dll', input='p3egg_composite1.obj') TargetAdd('libpandaegg.dll', input='p3egg_composite2.obj') TargetAdd('libpandaegg.dll', input='p3egg_parser.obj') TargetAdd('libpandaegg.dll', input='p3egg_lexer.obj') - TargetAdd('libpandaegg.dll', input='libp3egg_igate.obj') TargetAdd('libpandaegg.dll', input=COMMON_PANDA_LIBS) TargetAdd('libpandaegg.dll', opts=['ADVAPI']) + TargetAdd('egg_module.obj', input='libp3egg2pg.in') + TargetAdd('egg_module.obj', input='libp3egg.in') + TargetAdd('egg_module.obj', opts=OPTS) + TargetAdd('egg_module.obj', opts=['IMOD:egg', 'ILIB:egg']) + + TargetAdd('egg.pyd', input='egg_module.obj') + TargetAdd('egg.pyd', input='p3egg_eggGroupNode_ext.obj') + TargetAdd('egg.pyd', input='libp3egg_igate.obj') + TargetAdd('egg.pyd', input='libp3egg2pg_igate.obj') + TargetAdd('egg.pyd', input='libpandaegg.dll') + TargetAdd('egg.pyd', opts=['PYTHON']) + # # DIRECTORY: panda/src/mesadisplay/ # @@ -4065,12 +4107,15 @@ if (PkgSkip("ODE")==0 and not RUNTIME): TargetAdd('p3ode_composite1.obj', opts=OPTS, input='p3ode_composite1.cxx') TargetAdd('p3ode_composite2.obj', opts=OPTS, input='p3ode_composite2.cxx') TargetAdd('p3ode_composite3.obj', opts=OPTS, input='p3ode_composite3.cxx') + TargetAdd('p3ode_ext_composite.obj', opts=OPTS, input='p3ode_ext_composite.cxx') + + OPTS=['DIR:panda/src/ode', 'ODE'] IGATEFILES=GetDirectoryContents('panda/src/ode', ["*.h", "*_composite*.cxx"]) IGATEFILES.remove("odeConvexGeom.h") IGATEFILES.remove("odeHeightFieldGeom.h") IGATEFILES.remove("odeHelperStructs.h") TargetAdd('libpandaode.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libpandaode.in', opts=['IMOD:pandaode', 'ILIB:libpandaode', 'SRCDIR:panda/src/ode']) + TargetAdd('libpandaode.in', opts=['IMOD:ode', 'ILIB:libpandaode', 'SRCDIR:panda/src/ode']) TargetAdd('libpandaode_igate.obj', input='libpandaode.in', opts=["DEPENDENCYONLY"]) # @@ -4080,19 +4125,24 @@ if (PkgSkip("ODE")==0 and not RUNTIME): OPTS=['DIR:panda/metalibs/pandaode', 'BUILDING:PANDAODE', 'ODE'] TargetAdd('pandaode_pandaode.obj', opts=OPTS, input='pandaode.cxx') - TargetAdd('libpandaode_module.obj', input='libpandaode.in') - TargetAdd('libpandaode_module.obj', opts=OPTS) - TargetAdd('libpandaode_module.obj', opts=['IMOD:pandaode', 'ILIB:libpandaode']) - TargetAdd('libpandaode.dll', input='pandaode_pandaode.obj') - TargetAdd('libpandaode.dll', input='libpandaode_module.obj') TargetAdd('libpandaode.dll', input='p3ode_composite1.obj') TargetAdd('libpandaode.dll', input='p3ode_composite2.obj') TargetAdd('libpandaode.dll', input='p3ode_composite3.obj') - TargetAdd('libpandaode.dll', input='libpandaode_igate.obj') TargetAdd('libpandaode.dll', input=COMMON_PANDA_LIBS) TargetAdd('libpandaode.dll', opts=['WINUSER', 'ODE']) + OPTS=['DIR:panda/metalibs/pandaode', 'ODE'] + TargetAdd('ode_module.obj', input='libpandaode.in') + TargetAdd('ode_module.obj', opts=OPTS) + TargetAdd('ode_module.obj', opts=['IMOD:ode', 'ILIB:ode']) + + TargetAdd('ode.pyd', input='ode_module.obj') + TargetAdd('ode.pyd', input='p3ode_ext_composite.obj') + TargetAdd('ode.pyd', input='libpandaode_igate.obj') + TargetAdd('ode.pyd', input='libpandaode.dll') + TargetAdd('ode.pyd', opts=['PYTHON']) + # # DIRECTORY: panda/src/bullet/ # @@ -4101,7 +4151,7 @@ if (PkgSkip("BULLET")==0 and not RUNTIME): TargetAdd('p3bullet_composite.obj', opts=OPTS, input='p3bullet_composite.cxx') IGATEFILES=GetDirectoryContents('panda/src/bullet', ["*.h", "*_composite*.cxx"]) TargetAdd('libpandabullet.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libpandabullet.in', opts=['IMOD:pandabullet', 'ILIB:libpandabullet', 'SRCDIR:panda/src/bullet']) + TargetAdd('libpandabullet.in', opts=['IMOD:bullet', 'ILIB:libpandabullet', 'SRCDIR:panda/src/bullet']) TargetAdd('libpandabullet_igate.obj', input='libpandabullet.in', opts=["DEPENDENCYONLY"]) # @@ -4111,17 +4161,20 @@ if (PkgSkip("BULLET")==0 and not RUNTIME): OPTS=['DIR:panda/metalibs/pandabullet', 'BUILDING:PANDABULLET', 'BULLET'] TargetAdd('pandabullet_pandabullet.obj', opts=OPTS, input='pandabullet.cxx') - TargetAdd('libpandabullet_module.obj', input='libpandabullet.in') - TargetAdd('libpandabullet_module.obj', opts=OPTS) - TargetAdd('libpandabullet_module.obj', opts=['IMOD:pandabullet', 'ILIB:libpandabullet']) - TargetAdd('libpandabullet.dll', input='pandabullet_pandabullet.obj') - TargetAdd('libpandabullet.dll', input='libpandabullet_module.obj') TargetAdd('libpandabullet.dll', input='p3bullet_composite.obj') - TargetAdd('libpandabullet.dll', input='libpandabullet_igate.obj') TargetAdd('libpandabullet.dll', input=COMMON_PANDA_LIBS) TargetAdd('libpandabullet.dll', opts=['WINUSER', 'BULLET']) + TargetAdd('bullet_module.obj', input='libpandabullet.in') + TargetAdd('bullet_module.obj', opts=OPTS) + TargetAdd('bullet_module.obj', opts=['IMOD:bullet', 'ILIB:bullet']) + + TargetAdd('bullet.pyd', input='bullet_module.obj') + TargetAdd('bullet.pyd', input='libpandabullet_igate.obj') + TargetAdd('bullet.pyd', input='libpandabullet.dll') + TargetAdd('bullet.pyd', opts=['PYTHON']) + # # DIRECTORY: panda/src/physx/ # @@ -4131,7 +4184,7 @@ if (PkgSkip("PHYSX")==0): TargetAdd('p3physx_composite.obj', opts=OPTS, input='p3physx_composite.cxx') IGATEFILES=GetDirectoryContents('panda/src/physx', ["*.h", "*_composite*.cxx"]) TargetAdd('libpandaphysx.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libpandaphysx.in', opts=['IMOD:pandaphysx', 'ILIB:libpandaphysx', 'SRCDIR:panda/src/physx']) + TargetAdd('libpandaphysx.in', opts=['IMOD:physx', 'ILIB:libpandaphysx', 'SRCDIR:panda/src/physx']) TargetAdd('libpandaphysx_igate.obj', input='libpandaphysx.in', opts=["DEPENDENCYONLY"]) # @@ -4142,17 +4195,20 @@ if (PkgSkip("PHYSX")==0): OPTS=['DIR:panda/metalibs/pandaphysx', 'BUILDING:PANDAPHYSX', 'PHYSX', 'NOPPC'] TargetAdd('pandaphysx_pandaphysx.obj', opts=OPTS, input='pandaphysx.cxx') - TargetAdd('libpandaphysx_module.obj', input='libpandaphysx.in') - TargetAdd('libpandaphysx_module.obj', opts=OPTS) - TargetAdd('libpandaphysx_module.obj', opts=['IMOD:pandaphysx', 'ILIB:libpandaphysx']) - TargetAdd('libpandaphysx.dll', input='pandaphysx_pandaphysx.obj') - TargetAdd('libpandaphysx.dll', input='libpandaphysx_module.obj') TargetAdd('libpandaphysx.dll', input='p3physx_composite.obj') - TargetAdd('libpandaphysx.dll', input='libpandaphysx_igate.obj') TargetAdd('libpandaphysx.dll', input=COMMON_PANDA_LIBS) TargetAdd('libpandaphysx.dll', opts=['WINUSER', 'PHYSX', 'NOPPC']) + TargetAdd('physx_module.obj', input='libpandaphysx.in') + TargetAdd('physx_module.obj', opts=OPTS) + TargetAdd('physx_module.obj', opts=['IMOD:physx', 'ILIB:physx']) + + TargetAdd('physx.pyd', input='physx_module.obj') + TargetAdd('physx.pyd', input='libpandaphysx_igate.obj') + TargetAdd('physx.pyd', input='libpandaphysx.dll') + TargetAdd('physx.pyd', opts=['PYTHON']) + # # DIRECTORY: panda/src/physics/ # @@ -4164,7 +4220,7 @@ if (PkgSkip("PANDAPHYSICS")==0) and (not RUNTIME): IGATEFILES=GetDirectoryContents('panda/src/physics', ["*.h", "*_composite*.cxx"]) IGATEFILES.remove("forces.h") TargetAdd('libp3physics.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3physics.in', opts=['IMOD:pandaphysics', 'ILIB:libp3physics', 'SRCDIR:panda/src/physics']) + TargetAdd('libp3physics.in', opts=['IMOD:physics', 'ILIB:libp3physics', 'SRCDIR:panda/src/physics']) TargetAdd('libp3physics_igate.obj', input='libp3physics.in', opts=["DEPENDENCYONLY"]) # @@ -4182,7 +4238,7 @@ if (PkgSkip("PANDAPHYSICS")==0) and (PkgSkip("PANDAPARTICLESYSTEM")==0) and (not IGATEFILES.remove('emitters.h') IGATEFILES.remove('particles.h') TargetAdd('libp3particlesystem.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3particlesystem.in', opts=['IMOD:pandaphysics', 'ILIB:libp3particlesystem', 'SRCDIR:panda/src/particlesystem']) + TargetAdd('libp3particlesystem.in', opts=['IMOD:physics', 'ILIB:libp3particlesystem', 'SRCDIR:panda/src/particlesystem']) # # DIRECTORY: panda/metalibs/pandaphysics/ @@ -4192,23 +4248,27 @@ if (PkgSkip("PANDAPHYSICS")==0) and (not RUNTIME): OPTS=['DIR:panda/metalibs/pandaphysics', 'BUILDING:PANDAPHYSICS'] TargetAdd('pandaphysics_pandaphysics.obj', opts=OPTS, input='pandaphysics.cxx') - TargetAdd('libpandaphysics_module.obj', input='libp3physics.in') - if (PkgSkip("PANDAPARTICLESYSTEM")==0): - TargetAdd('libpandaphysics_module.obj', input='libp3particlesystem.in') - TargetAdd('libpandaphysics_module.obj', opts=OPTS) - TargetAdd('libpandaphysics_module.obj', opts=['IMOD:pandaphysics', 'ILIB:libpandaphysics']) - TargetAdd('libpandaphysics.dll', input='pandaphysics_pandaphysics.obj') - TargetAdd('libpandaphysics.dll', input='libpandaphysics_module.obj') TargetAdd('libpandaphysics.dll', input='p3physics_composite1.obj') TargetAdd('libpandaphysics.dll', input='p3physics_composite2.obj') - TargetAdd('libpandaphysics.dll', input='libp3physics_igate.obj') TargetAdd('libpandaphysics.dll', input='p3particlesystem_composite1.obj') TargetAdd('libpandaphysics.dll', input='p3particlesystem_composite2.obj') - TargetAdd('libpandaphysics.dll', input='libp3particlesystem_igate.obj') TargetAdd('libpandaphysics.dll', input=COMMON_PANDA_LIBS) TargetAdd('libpandaphysics.dll', opts=['ADVAPI']) + TargetAdd('physics_module.obj', input='libp3physics.in') + if (PkgSkip("PANDAPARTICLESYSTEM")==0): + TargetAdd('physics_module.obj', input='libp3particlesystem.in') + TargetAdd('physics_module.obj', opts=OPTS) + TargetAdd('physics_module.obj', opts=['IMOD:physics', 'ILIB:physics']) + + TargetAdd('physics.pyd', input='physics_module.obj') + TargetAdd('physics.pyd', input='libp3physics_igate.obj') + if (PkgSkip("PANDAPARTICLESYSTEM")==0): + TargetAdd('physics.pyd', input='libp3particlesystem_igate.obj') + TargetAdd('physics.pyd', input='libpandaphysics.dll') + TargetAdd('physics.pyd', opts=['PYTHON']) + # # DIRECTORY: panda/src/speedtree/ # @@ -4358,7 +4418,7 @@ if (PkgSkip("DIRECT")==0): if "dcParser.h" in IGATEFILES: IGATEFILES.remove("dcParser.h") if "dcmsgtypes.h" in IGATEFILES: IGATEFILES.remove('dcmsgtypes.h') TargetAdd('libp3dcparser.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3dcparser.in', opts=['IMOD:p3direct', 'ILIB:libp3dcparser', 'SRCDIR:direct/src/dcparser']) + TargetAdd('libp3dcparser.in', opts=['IMOD:direct', 'ILIB:libp3dcparser', 'SRCDIR:direct/src/dcparser']) TargetAdd('libp3dcparser_igate.obj', input='libp3dcparser.in', opts=["DEPENDENCYONLY"]) # @@ -4370,7 +4430,7 @@ if (PkgSkip("DIRECT")==0): TargetAdd('p3deadrec_composite1.obj', opts=OPTS, input='p3deadrec_composite1.cxx') IGATEFILES=GetDirectoryContents('direct/src/deadrec', ["*.h", "*_composite*.cxx"]) TargetAdd('libp3deadrec.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3deadrec.in', opts=['IMOD:p3direct', 'ILIB:libp3deadrec', 'SRCDIR:direct/src/deadrec']) + TargetAdd('libp3deadrec.in', opts=['IMOD:direct', 'ILIB:libp3deadrec', 'SRCDIR:direct/src/deadrec']) TargetAdd('libp3deadrec_igate.obj', input='libp3deadrec.in', opts=["DEPENDENCYONLY"]) # @@ -4384,7 +4444,7 @@ if (PkgSkip("DIRECT")==0): TargetAdd('p3distributed_cDistributedSmoothNodeBase.obj', opts=OPTS, input='cDistributedSmoothNodeBase.cxx') IGATEFILES=GetDirectoryContents('direct/src/distributed', ["*.h", "*.cxx"]) TargetAdd('libp3distributed.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3distributed.in', opts=['IMOD:p3direct', 'ILIB:libp3distributed', 'SRCDIR:direct/src/distributed']) + TargetAdd('libp3distributed.in', opts=['IMOD:direct', 'ILIB:libp3distributed', 'SRCDIR:direct/src/distributed']) TargetAdd('libp3distributed_igate.obj', input='libp3distributed.in', opts=["DEPENDENCYONLY"]) # @@ -4396,7 +4456,7 @@ if (PkgSkip("DIRECT")==0): TargetAdd('p3interval_composite1.obj', opts=OPTS, input='p3interval_composite1.cxx') IGATEFILES=GetDirectoryContents('direct/src/interval', ["*.h", "*_composite*.cxx"]) TargetAdd('libp3interval.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3interval.in', opts=['IMOD:p3direct', 'ILIB:libp3interval', 'SRCDIR:direct/src/interval']) + TargetAdd('libp3interval.in', opts=['IMOD:direct', 'ILIB:libp3interval', 'SRCDIR:direct/src/interval']) TargetAdd('libp3interval_igate.obj', input='libp3interval.in', opts=["DEPENDENCYONLY"]) # @@ -4410,7 +4470,7 @@ if (PkgSkip("DIRECT")==0): TargetAdd('p3showbase_showBase_assist.obj', opts=OPTS, input='showBase_assist.mm') IGATEFILES=GetDirectoryContents('direct/src/showbase', ["*.h", "showBase.cxx"]) TargetAdd('libp3showbase.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3showbase.in', opts=['IMOD:p3direct', 'ILIB:libp3showbase', 'SRCDIR:direct/src/showbase']) + TargetAdd('libp3showbase.in', opts=['IMOD:direct', 'ILIB:libp3showbase', 'SRCDIR:direct/src/showbase']) TargetAdd('libp3showbase_igate.obj', input='libp3showbase.in', opts=["DEPENDENCYONLY"]) # @@ -4421,16 +4481,7 @@ if (PkgSkip("DIRECT")==0): OPTS=['DIR:direct/metalibs/direct', 'BUILDING:DIRECT'] TargetAdd('p3direct_direct.obj', opts=OPTS, input='direct.cxx') - TargetAdd('libp3direct_module.obj', input='libp3dcparser.in') - TargetAdd('libp3direct_module.obj', input='libp3showbase.in') - TargetAdd('libp3direct_module.obj', input='libp3deadrec.in') - TargetAdd('libp3direct_module.obj', input='libp3interval.in') - TargetAdd('libp3direct_module.obj', input='libp3distributed.in') - TargetAdd('libp3direct_module.obj', opts=OPTS) - TargetAdd('libp3direct_module.obj', opts=['IMOD:p3direct', 'ILIB:libp3direct']) - TargetAdd('libp3direct.dll', input='p3direct_direct.obj') - TargetAdd('libp3direct.dll', input='libp3direct_module.obj') TargetAdd('libp3direct.dll', input='p3directbase_directbase.obj') TargetAdd('libp3direct.dll', input='p3dcparser_composite1.obj') TargetAdd('libp3direct.dll', input='p3dcparser_composite2.obj') @@ -4452,6 +4503,18 @@ if (PkgSkip("DIRECT")==0): TargetAdd('libp3direct.dll', input=COMMON_PANDA_LIBS) TargetAdd('libp3direct.dll', opts=['ADVAPI', 'OPENSSL', 'WINUSER', 'WINGDI']) + TargetAdd('direct_module.obj', input='libp3dcparser.in') + TargetAdd('direct_module.obj', input='libp3showbase.in') + TargetAdd('direct_module.obj', input='libp3deadrec.in') + TargetAdd('direct_module.obj', input='libp3interval.in') + TargetAdd('direct_module.obj', input='libp3distributed.in') + TargetAdd('direct_module.obj', opts=OPTS) + TargetAdd('direct_module.obj', opts=['IMOD:direct', 'ILIB:direct']) + + TargetAdd('direct.pyd', input='direct_module.obj') + TargetAdd('direct.pyd', input='libp3direct.dll') + TargetAdd('direct.pyd', opts=['PYTHON']) + # # DIRECTORY: direct/src/dcparse/ # @@ -5628,18 +5691,20 @@ if (PkgSkip("CONTRIB")==0 and not RUNTIME): TargetAdd('p3ai_composite1.obj', opts=OPTS, input='p3ai_composite1.cxx') IGATEFILES=GetDirectoryContents('contrib/src/ai', ["*.h", "*_composite*.cxx"]) TargetAdd('libpandaai.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libpandaai.in', opts=['IMOD:pandaai', 'ILIB:libpandaai', 'SRCDIR:contrib/src/ai']) + TargetAdd('libpandaai.in', opts=['IMOD:ai', 'ILIB:libpandaai', 'SRCDIR:contrib/src/ai']) TargetAdd('libpandaai_igate.obj', input='libpandaai.in', opts=["DEPENDENCYONLY"]) - TargetAdd('libpandaai_module.obj', input='libpandaai.in') - TargetAdd('libpandaai_module.obj', opts=OPTS) - TargetAdd('libpandaai_module.obj', opts=['IMOD:pandaai', 'ILIB:libpandaai']) - - TargetAdd('libpandaai.dll', input='libpandaai_module.obj') TargetAdd('libpandaai.dll', input='p3ai_composite1.obj') - TargetAdd('libpandaai.dll', input='libpandaai_igate.obj') TargetAdd('libpandaai.dll', input=COMMON_PANDA_LIBS) + TargetAdd('ai_module.obj', input='libpandaai.in') + TargetAdd('ai_module.obj', opts=OPTS) + TargetAdd('ai_module.obj', opts=['IMOD:ai', 'ILIB:ai']) + + TargetAdd('ai.pyd', input='ai_module.obj') + TargetAdd('ai.pyd', input='libpandaai_igate.obj') + TargetAdd('ai.pyd', opts=['PYTHON']) + # # Run genpycode # @@ -5650,25 +5715,24 @@ if (PkgSkip("PYTHON")==0 and not RUNTIME): # split them out of libpanda and need to maintain backward # compatibility with old imports. See direct/src/ffi/panda3d.py - TargetAdd('PandaModules.py', input='libpandaexpress.dll') - TargetAdd('PandaModules.py', input='libpanda.dll') + TargetAdd('PandaModules.py', input='core.pyd') if (PkgSkip("PANDAPHYSICS")==0): - TargetAdd('PandaModules.py', input='libpandaphysics.dll') + TargetAdd('PandaModules.py', input='physics.pyd') if (PkgSkip('PANDAFX')==0): - TargetAdd('PandaModules.py', input='libpandafx.dll') + TargetAdd('PandaModules.py', input='fx.pyd') if (PkgSkip("DIRECT")==0): - TargetAdd('PandaModules.py', input='libp3direct.dll') + TargetAdd('PandaModules.py', input='direct.pyd') if (PkgSkip("VISION")==0): - TargetAdd('PandaModules.py', input='libp3vision.dll') + TargetAdd('PandaModules.py', input='vision.pyd') if (PkgSkip("SKEL")==0): - TargetAdd('PandaModules.py', input='libpandaskel.dll') - TargetAdd('PandaModules.py', input='libpandaegg.dll') + TargetAdd('PandaModules.py', input='skel.pyd') + TargetAdd('PandaModules.py', input='egg.pyd') if (PkgSkip("AWESOMIUM")==0): - TargetAdd('PandaModules.py', input='libp3awesomium.dll') + TargetAdd('PandaModules.py', input='awesomium.pyd') if (PkgSkip("ODE")==0): - TargetAdd('PandaModules.py', input='libpandaode.dll') + TargetAdd('PandaModules.py', input='ode.pyd') if (PkgSkip("VRPN")==0): - TargetAdd('PandaModules.py', input='libp3vrpn.dll') + TargetAdd('PandaModules.py', input='vrpn.pyd') # # Generate the models directory and samples directory @@ -5762,8 +5826,12 @@ for target in TARGET_LIST: DEPENDENCYQUEUE.append([CompileAnything, [name, inputs, opts], [name], deps, []]) def BuildWorker(taskqueue, donequeue): - while (1): - task = taskqueue.get() + while True: + try: + task = taskqueue.get(timeout=1) + except: + ProgressOutput(None, "Waiting for tasks...") + task = taskqueue.get() sys.stdout.flush() if (task == 0): return try: diff --git a/makepanda/makepandacore.py b/makepanda/makepandacore.py index 50b079bcde..e39f5b213b 100644 --- a/makepanda/makepandacore.py +++ b/makepanda/makepandacore.py @@ -9,13 +9,15 @@ ## ######################################################################## -import sys,os,time,stat,string,re,getopt,fnmatch,threading,signal,shutil,platform,glob,getpass,signal,thread +import sys,os,time,stat,string,re,getopt,fnmatch,threading,signal,shutil,platform,glob,getpass,signal from distutils import sysconfig if sys.version_info >= (3, 0): import pickle + import _thread as thread else: import cPickle as pickle + import thread SUFFIX_INC = [".cxx",".c",".h",".I",".yxx",".lxx",".mm",".rc",".r"] SUFFIX_DLL = [".dll",".dlo",".dle",".dli",".dlm",".mll",".exe",".pyd",".ocx"] @@ -694,11 +696,16 @@ def CxxGetIncludes(path): except: exit("Cannot open source file \""+path+"\" for reading.") include = [] - for line in sfile: - match = CxxIncludeRegex.match(line,0) - if (match): - incname = match.group(1) - include.append(incname) + try: + for line in sfile: + match = CxxIncludeRegex.match(line,0) + if (match): + incname = match.group(1) + include.append(incname) + except: + print("Failed to determine dependencies of \""+path+"\".") + raise + sfile.close() CXXINCLUDECACHE[path] = [date, include] return include @@ -1062,6 +1069,8 @@ def MakeBuildTree(): MakeDirectory(OUTPUTDIR + "/models/gui") MakeDirectory(OUTPUTDIR + "/pandac") MakeDirectory(OUTPUTDIR + "/pandac/input") + MakeDirectory(OUTPUTDIR + "/panda3d") + CreateFile(OUTPUTDIR + "/panda3d/__init__.py") if GetTarget() == 'android': MakeDirectory(OUTPUTDIR + "/libs") @@ -2412,10 +2421,10 @@ def ParsePandaVersion(fn): f = open(fn, "r") pattern = re.compile('^[ \t]*[#][ \t]*define[ \t]+PANDA_VERSION[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)') for line in f: - match = pattern.match(line,0) + match = pattern.match(line, 0) if (match): f.close() - return match.group(1)+"."+match.group(2)+"."+match.group(3) + return match.group(1) + "." + match.group(2) + "." + match.group(3) f.close() except: pass return "0.0.0" @@ -2428,7 +2437,7 @@ def ParsePluginVersion(fn): match = pattern.match(line,0) if (match): f.close() - return match.group(1)+"."+match.group(2)+"."+match.group(3) + return match.group(1) + "." + match.group(2) + "." + match.group(3) f.close() except: pass return "0.0.0" @@ -2522,7 +2531,7 @@ def WriteResourceFile(basename, **kwargs): ## ######################################################################## -ORIG_EXT={} +ORIG_EXT = {} def GetOrigExt(x): return ORIG_EXT[x] @@ -2556,7 +2565,7 @@ def CalcLocation(fn, ipath): if (fn.endswith(".res")): return OUTPUTDIR+"/tmp/"+fn if (fn.endswith(".tlb")): return OUTPUTDIR+"/tmp/"+fn if (fn.endswith(".dll")): return OUTPUTDIR+"/bin/"+fn[:-4]+dllext+".dll" - if (fn.endswith(".pyd")): return OUTPUTDIR+"/bin/"+fn[:-4]+dllext+".pyd" + if (fn.endswith(".pyd")): return OUTPUTDIR+"/panda3d/"+fn[:-4]+dllext+".pyd" if (fn.endswith(".ocx")): return OUTPUTDIR+"/plugins/"+fn[:-4]+dllext+".ocx" if (fn.endswith(".mll")): return OUTPUTDIR+"/plugins/"+fn[:-4]+dllext+".mll" if (fn.endswith(".dlo")): return OUTPUTDIR+"/plugins/"+fn[:-4]+dllext+".dlo" @@ -2572,7 +2581,7 @@ def CalcLocation(fn, ipath): if (fn.endswith(".plist")): return CxxFindSource(fn, ipath) if (fn.endswith(".obj")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".o" if (fn.endswith(".dll")): return OUTPUTDIR+"/lib/"+fn[:-4]+".dylib" - if (fn.endswith(".pyd")): return OUTPUTDIR+"/lib/"+fn[:-4]+".so" + if (fn.endswith(".pyd")): return OUTPUTDIR+"/panda3d/"+fn[:-4]+".so" if (fn.endswith(".mll")): return OUTPUTDIR+"/plugins/"+fn if (fn.endswith(".exe")): return OUTPUTDIR+"/bin/"+fn[:-4] if (fn.endswith(".lib")): return OUTPUTDIR+"/lib/"+fn[:-4]+".a" @@ -2593,7 +2602,7 @@ def CalcLocation(fn, ipath): else: if (fn.endswith(".obj")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".o" if (fn.endswith(".dll")): return OUTPUTDIR+"/lib/"+fn[:-4]+".so" - if (fn.endswith(".pyd")): return OUTPUTDIR+"/lib/"+fn[:-4]+".so" + if (fn.endswith(".pyd")): return OUTPUTDIR+"/panda3d/"+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+"/bin/"+fn[:-4] @@ -2669,6 +2678,11 @@ def TargetAdd(target, dummy=0, opts=0, input=0, dep=0, ipath=0, winrc=0): if (ipath == 0): ipath = [] if (type(input) == str): input = [input] if (type(dep) == str): dep = [dep] + + if os.path.splitext(target)[1] == '.pyd' and PkgSkip("PYTHON"): + # It makes no sense to build Python modules with python disabled. + return + full = FindLocation(target, [OUTPUTDIR + "/include"]) if (full not in TARGET_TABLE): diff --git a/panda/src/display/Sources.pp b/panda/src/display/Sources.pp index 99a8cca70b..b2f093957a 100644 --- a/panda/src/display/Sources.pp +++ b/panda/src/display/Sources.pp @@ -27,8 +27,8 @@ graphicsDevice.h graphicsDevice.I \ graphicsPipe.I graphicsPipe.h \ graphicsPipeSelection.I graphicsPipeSelection.h \ - graphicsStateGuardian.I \ - graphicsStateGuardian.h \ + graphicsStateGuardian.I graphicsStateGuardian.h \ + graphicsStateGuardian_ext.cxx graphicsStateGuardian_ext.h \ graphicsThreadingModel.I graphicsThreadingModel.h \ graphicsWindow.I graphicsWindow.h \ graphicsWindowInputDevice.I \ diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index 5e67cb81e7..4aea74171a 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/panda/src/display/graphicsStateGuardian.cxx @@ -59,13 +59,6 @@ #include #include -#ifdef HAVE_PYTHON -#include "py_panda.h" -#ifndef CPPPARSER -IMPORT_THIS struct Dtool_PyTypedObject Dtool_Texture; -#endif -#endif // HAVE_PYTHON - PStatCollector GraphicsStateGuardian::_vertex_buffer_switch_pcollector("Vertex buffer switch:Vertex"); PStatCollector GraphicsStateGuardian::_index_buffer_switch_pcollector("Vertex buffer switch:Index"); PStatCollector GraphicsStateGuardian::_load_vertex_buffer_pcollector("Draw:Transfer data:Vertex buffer"); @@ -247,10 +240,10 @@ GraphicsStateGuardian(CoordinateSystem internal_coordinate_system, // The default is no shader support. _auto_detect_shader_model = SM_00; _shader_model = SM_00; - + _gamma = 1.0f; _texture_quality_override = Texture::QL_default; - + _shader_generator = NULL; } @@ -435,41 +428,6 @@ void GraphicsStateGuardian:: restore_gamma() { } -#ifdef HAVE_PYTHON -//////////////////////////////////////////////////////////////////// -// Function: GraphicsStateGuardian::get_prepared_textures -// Access: Published -// Description: Returns a Python list of all of the -// currently-prepared textures within the GSG. -//////////////////////////////////////////////////////////////////// -PyObject *GraphicsStateGuardian:: -get_prepared_textures() const { - ReMutexHolder holder(_prepared_objects->_lock); - size_t num_textures = _prepared_objects->_prepared_textures.size(); - PyObject *list = PyList_New(num_textures); - - size_t i = 0; - PreparedGraphicsObjects::Textures::const_iterator ti; - for (ti = _prepared_objects->_prepared_textures.begin(); - ti != _prepared_objects->_prepared_textures.end(); - ++ti) { - PT(Texture) tex = (*ti)->get_texture(); - - PyObject *element = - DTool_CreatePyInstanceTyped(tex, Dtool_Texture, - true, false, tex->get_type_index()); - tex->ref(); - - nassertr(i < num_textures, NULL); - PyList_SetItem(list, i, element); - ++i; - } - nassertr(i == num_textures, NULL); - - return list; -} -#endif // HAVE_PYTHON - //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::traverse_prepared_textures // Access: Public @@ -485,7 +443,7 @@ traverse_prepared_textures(GraphicsStateGuardian::TextureCallback *func, for (ti = _prepared_objects->_prepared_textures.begin(); ti != _prepared_objects->_prepared_textures.end(); ++ti) { - bool result = (*func)(*ti,callback_arg); + bool result = (*func)(*ti, callback_arg); if (!result) { return; } @@ -2473,11 +2431,11 @@ determine_target_texture() { target_tex_gen != (TexGenAttrib *)NULL); _target_texture = target_texture; _target_tex_gen = target_tex_gen; - + if (_has_texture_alpha_scale) { PT(TextureStage) stage = get_alpha_scale_texture_stage(); PT(Texture) texture = TexturePool::get_alpha_scale_map(); - + _target_texture = DCAST(TextureAttrib, _target_texture->add_on_stage(stage, texture)); _target_tex_gen = DCAST(TexGenAttrib, _target_tex_gen->add_stage (stage, TexGenAttrib::M_constant, LTexCoord3(_current_color_scale[3], 0.0f, 0.0f))); @@ -2801,6 +2759,9 @@ string GraphicsStateGuardian::get_driver_renderer() { // Function: GraphicsStateGuardian::get_driver_version // Access: Public, Virtual // Description: Returns driver version +// This has an implementation-defined meaning, and may +// be "0" if the particular graphics implementation +// does not provide a way to query this information. //////////////////////////////////////////////////////////////////// string GraphicsStateGuardian:: get_driver_version() { @@ -2810,7 +2771,10 @@ get_driver_version() { //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::get_driver_version_major // Access: Public, Virtual -// Description: Returns major version of the video driver +// Description: Returns major version of the video driver. +// This has an implementation-defined meaning, and may +// be -1 if the particular graphics implementation +// does not provide a way to query this information. //////////////////////////////////////////////////////////////////// int GraphicsStateGuardian:: get_driver_version_major() { @@ -2820,7 +2784,10 @@ get_driver_version_major() { //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::get_driver_version_minor // Access: Public, Virtual -// Description: Returns the minor version of the video driver +// Description: Returns the minor version of the video driver. +// This has an implementation-defined meaning, and may +// be -1 if the particular graphics implementation +// does not provide a way to query this information. //////////////////////////////////////////////////////////////////// int GraphicsStateGuardian:: get_driver_version_minor() { @@ -2830,7 +2797,7 @@ get_driver_version_minor() { //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::get_driver_shader_version_major // Access: Public, Virtual -// Description: Returns the major version of the shader model +// Description: Returns the major version of the shader model. //////////////////////////////////////////////////////////////////// int GraphicsStateGuardian:: get_driver_shader_version_major() { @@ -2840,7 +2807,7 @@ get_driver_shader_version_major() { //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::get_driver_shader_version_minor // Access: Public, Virtual -// Description: Returns the minor version of the shader model +// Description: Returns the minor version of the shader model. //////////////////////////////////////////////////////////////////// int GraphicsStateGuardian:: get_driver_shader_version_minor() { diff --git a/panda/src/display/graphicsStateGuardian.h b/panda/src/display/graphicsStateGuardian.h index ce2c41bc93..d5a5f08eec 100644 --- a/panda/src/display/graphicsStateGuardian.h +++ b/panda/src/display/graphicsStateGuardian.h @@ -46,9 +46,9 @@ #include "occlusionQueryContext.h" #include "stencilRenderStates.h" #include "loader.h" -#include "textureAttrib.h" -#include "texGenAttrib.h" #include "shaderAttrib.h" +#include "texGenAttrib.h" +#include "textureAttrib.h" class DrawableRegion; class GraphicsEngine; @@ -179,9 +179,7 @@ PUBLISHED: INLINE void set_texture_quality_override(Texture::QualityLevel quality_level); INLINE Texture::QualityLevel get_texture_quality_override() const; -#ifdef HAVE_PYTHON - PyObject *get_prepared_textures() const; -#endif + EXTENSION(PyObject *get_prepared_textures() const); typedef bool TextureCallback(TextureContext *tc, void *callback_arg); void traverse_prepared_textures(TextureCallback *func, void *callback_arg); diff --git a/panda/src/egg/Sources.pp b/panda/src/egg/Sources.pp index 49757317ae..cc68e22b52 100644 --- a/panda/src/egg/Sources.pp +++ b/panda/src/egg/Sources.pp @@ -24,6 +24,7 @@ eggCurve.I eggCurve.h eggData.I eggData.h \ eggExternalReference.I eggExternalReference.h \ eggFilenameNode.I eggFilenameNode.h eggGroup.I eggGroup.h \ + eggGroupNode_ext.h \ eggGroupNode.I eggGroupNode.h eggGroupUniquifier.h \ eggLine.I eggLine.h \ eggMaterial.I eggMaterial.h eggMaterialCollection.I \ @@ -69,7 +70,8 @@ eggCompositePrimitive.cxx \ eggCoordinateSystem.cxx \ eggCurve.cxx eggData.cxx eggExternalReference.cxx \ - eggFilenameNode.cxx eggGroup.cxx eggGroupNode.cxx \ + eggFilenameNode.cxx eggGroup.cxx \ + eggGroupNode_ext.cxx eggGroupNode.cxx \ eggGroupUniquifier.cxx eggLine.cxx eggMaterial.cxx \ eggMaterialCollection.cxx \ eggMesher.cxx \ @@ -106,7 +108,8 @@ eggCoordinateSystem.I eggCoordinateSystem.h eggCurve.I \ eggCurve.h eggData.I eggData.h eggExternalReference.I \ eggExternalReference.h eggFilenameNode.I eggFilenameNode.h \ - eggGroup.I eggGroup.h eggGroupNode.I eggGroupNode.h \ + eggGroup.I eggGroup.h \ + eggGroupNode_ext.h eggGroupNode.I eggGroupNode.h \ eggGroupUniquifier.h \ eggLine.I eggLine.h \ eggMaterial.I \ diff --git a/panda/src/egg/eggGroupNode.h b/panda/src/egg/eggGroupNode.h index 7e6bc358f9..ffdfcb304c 100644 --- a/panda/src/egg/eggGroupNode.h +++ b/panda/src/egg/eggGroupNode.h @@ -113,6 +113,8 @@ PUBLISHED: EggNode *get_first_child(); EggNode *get_next_child(); + EXTENSION(PyObject *get_children() const); + EggNode *add_child(EggNode *node); PT(EggNode) remove_child(EggNode *node); void steal_children(EggGroupNode &other); diff --git a/panda/src/event/pythonTask.cxx b/panda/src/event/pythonTask.cxx index 66d3d6bfa6..ec18eff5ec 100644 --- a/panda/src/event/pythonTask.cxx +++ b/panda/src/event/pythonTask.cxx @@ -158,7 +158,7 @@ get_args() { } this->ref(); - PyObject *self = + PyObject *self = DTool_CreatePyInstanceTyped(this, Dtool_TypedReferenceCount, true, false, get_type_index()); PyTuple_SET_ITEM(with_task, num_args, self); diff --git a/panda/src/express/Sources.pp b/panda/src/express/Sources.pp index e64467fbba..dcec931471 100644 --- a/panda/src/express/Sources.pp +++ b/panda/src/express/Sources.pp @@ -6,7 +6,7 @@ #define TARGET p3express #define USE_PACKAGES zlib openssl tar - #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx + #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx $[TARGET]_ext_composite.cxx #define SOURCES \ buffer.I buffer.h \ @@ -31,6 +31,7 @@ memoryUsage.I memoryUsage.h \ memoryUsagePointerCounts.I memoryUsagePointerCounts.h \ memoryUsagePointers.I memoryUsagePointers.h \ + memoryUsagePointers_ext.h \ multifile.I multifile.h \ namable.I \ namable.h \ @@ -51,10 +52,11 @@ pta_int.h \ pta_uchar.h pta_double.h pta_float.h \ pta_stdfloat.h \ - ramfile.I ramfile.h \ + ramfile.I ramfile.h ramfile_ext.h \ referenceCount.I referenceCount.h \ subStream.I subStream.h subStreamBuf.h \ subfileInfo.h subfileInfo.I \ + streamReader_ext.h \ temporaryFile.h temporaryFile.I \ threadSafePointerTo.I threadSafePointerTo.h \ threadSafePointerToBase.I threadSafePointerToBase.h \ @@ -70,7 +72,7 @@ virtualFileMountSystem.h virtualFileMountSystem.I \ virtualFileSimple.h virtualFileSimple.I \ virtualFileSystem.h virtualFileSystem.I \ - virtualFileSystem_ext.h virtualFileSystem_ext.cxx \ + virtualFileSystem_ext.h \ weakPointerCallback.I weakPointerCallback.h \ weakPointerTo.I weakPointerTo.h \ weakPointerToBase.I weakPointerToBase.h \ @@ -92,6 +94,7 @@ fileReference.cxx \ hashGeneratorBase.cxx hashVal.cxx \ memoryInfo.cxx memoryUsage.cxx memoryUsagePointerCounts.cxx \ + memoryUsagePointers_ext.cxx \ memoryUsagePointers.cxx multifile.cxx \ namable.cxx \ nodePointerTo.cxx \ @@ -109,8 +112,10 @@ profileTimer.cxx \ pta_int.cxx \ pta_uchar.cxx pta_double.cxx pta_float.cxx \ + ramfile_ext.cxx \ ramfile.cxx \ referenceCount.cxx \ + streamReader_ext.cxx \ subStream.cxx subStreamBuf.cxx \ subfileInfo.cxx \ temporaryFile.cxx \ @@ -125,6 +130,7 @@ virtualFileMountRamdisk.cxx \ virtualFileMountSystem.cxx \ virtualFileSimple.cxx virtualFileSystem.cxx \ + virtualFileSystem_ext.cxx \ weakPointerCallback.cxx \ weakPointerTo.cxx \ weakPointerToBase.cxx \ diff --git a/panda/src/express/memoryUsagePointers.cxx b/panda/src/express/memoryUsagePointers.cxx index 3fffa0d898..4a590f3661 100644 --- a/panda/src/express/memoryUsagePointers.cxx +++ b/panda/src/express/memoryUsagePointers.cxx @@ -20,20 +20,6 @@ #include "referenceCount.h" #include "typedReferenceCount.h" -#ifdef HAVE_PYTHON -// Pick up a few declarations so we can create Python wrappers in -// get_python_pointer(), below. - -#include "py_panda.h" - -#ifndef CPPPARSER -extern EXPCL_PANDAEXPRESS Dtool_PyTypedObject Dtool_TypedObject; -extern EXPCL_PANDAEXPRESS Dtool_PyTypedObject Dtool_TypedReferenceCount; -extern EXPCL_PANDAEXPRESS Dtool_PyTypedObject Dtool_ReferenceCount; -#endif // CPPPARSER - -#endif // HAVE_PYTHON - //////////////////////////////////////////////////////////////////// // Function: MemoryUsagePointers::Constructor // Access: Published @@ -151,65 +137,6 @@ get_age(int n) const { return _entries[n]._age; } -#ifdef HAVE_PYTHON -//////////////////////////////////////////////////////////////////// -// Function: MemoryUsagePointers::get_python_pointer -// Access: Published -// Description: Returns the nth object, represented as a Python -// object of the appropriate type. Reference counting -// will be properly set on the Python object. -// -// get_typed_pointer() is almost as good as this, but -// (a) it does not set the reference count, and (b) it -// does not work for objects that do not inherit from -// TypedObject. This will work for any object whose -// type is known, which has a Python representation. -//////////////////////////////////////////////////////////////////// -PyObject *MemoryUsagePointers:: -get_python_pointer(int n) const { - nassertr(n >= 0 && n < get_num_pointers(), NULL); - TypedObject *typed_ptr = _entries[n]._typed_ptr; - ReferenceCount *ref_ptr = _entries[n]._ref_ptr; - - bool memory_rules = false; - if (ref_ptr != (ReferenceCount *)NULL) { - memory_rules = true; - ref_ptr->ref(); - } - - if (typed_ptr != (TypedObject *)NULL) { - return DTool_CreatePyInstanceTyped(typed_ptr, Dtool_TypedObject, - memory_rules, false, - typed_ptr->get_type_index()); - } - - if (ref_ptr == (ReferenceCount *)NULL) { - return Py_BuildValue(""); - } - - TypeHandle type = _entries[n]._type; - if (type != TypeHandle::none()) { - // Use TypedReferenceCount if we have it. - if (type.is_derived_from(TypedReferenceCount::get_class_type())) { - TypedReferenceCount *typed_ref_ptr = (TypedReferenceCount *)ref_ptr; - - return DTool_CreatePyInstanceTyped(typed_ref_ptr, Dtool_TypedReferenceCount, - memory_rules, false, - type.get_index()); - } - - // Otherwise, trust that there is a downcast path to the actual type. - return DTool_CreatePyInstanceTyped(ref_ptr, Dtool_ReferenceCount, - memory_rules, false, - type.get_index()); - } - - // If worse comes to worst, just return a ReferenceCount wrapper. - return DTool_CreatePyInstance(ref_ptr, Dtool_ReferenceCount, - memory_rules, false); -} -#endif - //////////////////////////////////////////////////////////////////// // Function: MemoryUsagePointers::clear // Access: Published diff --git a/panda/src/express/memoryUsagePointers.h b/panda/src/express/memoryUsagePointers.h index 7192cea41f..ceab784752 100644 --- a/panda/src/express/memoryUsagePointers.h +++ b/panda/src/express/memoryUsagePointers.h @@ -67,9 +67,7 @@ PUBLISHED: string get_type_name(int n) const; double get_age(int n) const; -#ifdef HAVE_PYTHON - PyObject *get_python_pointer(int n) const; -#endif + EXTENSION(PyObject *get_python_pointer(int n) const); void clear(); diff --git a/panda/src/express/memoryUsagePointers_ext.cxx b/panda/src/express/memoryUsagePointers_ext.cxx new file mode 100644 index 0000000000..eee3b5d0e6 --- /dev/null +++ b/panda/src/express/memoryUsagePointers_ext.cxx @@ -0,0 +1,72 @@ +// Filename: memoryUsagePointers_ext.cxx +// Created by: rdb (10Dec13) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +#include "memoryUsagePointers_ext.h" + +#if defined(HAVE_PYTHON) && defined(DO_MEMORY_USAGE) + +#ifndef CPPPARSER +extern EXPCL_PANDAEXPRESS Dtool_PyTypedObject Dtool_TypedObject; +extern EXPCL_PANDAEXPRESS Dtool_PyTypedObject Dtool_TypedReferenceCount; +extern EXPCL_PANDAEXPRESS Dtool_PyTypedObject Dtool_ReferenceCount; +#endif // CPPPARSER + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsagePointers::get_python_pointer +// Access: Published +// Description: Returns the nth object, represented as a Python +// object of the appropriate type. Reference counting +// will be properly set on the Python object. +// +// get_typed_pointer() is almost as good as this, but +// (a) it does not set the reference count, and (b) it +// does not work for objects that do not inherit from +// TypedObject. This will work for any object whose +// type is known, which has a Python representation. +//////////////////////////////////////////////////////////////////// +PyObject *Extension:: +get_python_pointer(int n) const { + TypedObject *typed_ptr = _this->get_typed_pointer(n); + ReferenceCount *ref_ptr = _this->get_pointer(n); + + bool memory_rules = false; + if (ref_ptr != (ReferenceCount *)NULL) { + memory_rules = true; + ref_ptr->ref(); + } + + if (typed_ptr != (TypedObject *)NULL) { + return DTool_CreatePyInstanceTyped(typed_ptr, Dtool_TypedObject, + memory_rules, false, + typed_ptr->get_type_index()); + } + + if (ref_ptr == (ReferenceCount *)NULL) { + return Py_BuildValue(""); + } + + TypeHandle type = _this->get_type(n); + if (type != TypeHandle::none()) { + // Trust that there is a downcast path to the actual type. + return DTool_CreatePyInstanceTyped(ref_ptr, Dtool_ReferenceCount, + memory_rules, false, + type.get_index()); + } + + // If worse comes to worst, just return a ReferenceCount wrapper. + return DTool_CreatePyInstance(ref_ptr, Dtool_ReferenceCount, + memory_rules, false); +} + +#endif // HAVE_PYTHON && DO_MEMORY_USAGE diff --git a/panda/src/express/memoryUsagePointers_ext.h b/panda/src/express/memoryUsagePointers_ext.h new file mode 100644 index 0000000000..3b4e9c7478 --- /dev/null +++ b/panda/src/express/memoryUsagePointers_ext.h @@ -0,0 +1,40 @@ +// Filename: memoryUsagePointers_ext.h +// Created by: rdb (10Dec13) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +#ifndef MEMORYUSAGEPOINTERS_EXT_H +#define MEMORYUSAGEPOINTERS_EXT_H + +#include "dtoolbase.h" + +#if defined(HAVE_PYTHON) && defined(DO_MEMORY_USAGE) + +#include "extension.h" +#include "memoryUsagePointers.h" +#include "py_panda.h" + +//////////////////////////////////////////////////////////////////// +// Class : Extension +// Description : This class defines the extension methods for +// VirtualFileSystem, which are called instead of +// any C++ methods with the same prototype. +//////////////////////////////////////////////////////////////////// +template<> +class Extension : public ExtensionBase { +public: + PyObject *get_python_pointer(int n) const; +}; + +#endif // HAVE_PYTHON && DO_MEMORY_USAGE + +#endif // MEMORYUSAGEPOINTERS_EXT_H diff --git a/panda/src/express/p3express_ext_composite.cxx b/panda/src/express/p3express_ext_composite.cxx new file mode 100644 index 0000000000..88dd271158 --- /dev/null +++ b/panda/src/express/p3express_ext_composite.cxx @@ -0,0 +1,4 @@ +#include "memoryUsagePointers_ext.cxx" +#include "ramfile_ext.cxx" +#include "streamReader_ext.cxx" +#include "virtualFileSystem_ext.cxx" diff --git a/panda/src/express/ramfile.h b/panda/src/express/ramfile.h index 8f3dafe8cf..d8006400a4 100644 --- a/panda/src/express/ramfile.h +++ b/panda/src/express/ramfile.h @@ -32,6 +32,7 @@ PUBLISHED: INLINE size_t tell() const; string read(size_t length); string readline(); + EXTENSION(PyObject *readlines()); INLINE const string &get_data() const; INLINE size_t get_data_size() const; diff --git a/panda/src/express/ramfile_ext.cxx b/panda/src/express/ramfile_ext.cxx new file mode 100644 index 0000000000..366395df8d --- /dev/null +++ b/panda/src/express/ramfile_ext.cxx @@ -0,0 +1,48 @@ +// Filename: ramfile_ext.cxx +// Created by: rdb (10Dec13) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +#include "ramfile_ext.h" + +#ifdef HAVE_PYTHON + +//////////////////////////////////////////////////////////////////// +// Function: Ramfile::readlines +// Access: Published +// Description: Reads all the lines at once and returns a list. +// Also see the documentation for readline(). +//////////////////////////////////////////////////////////////////// +PyObject *Extension:: +readlines() { + PyObject *lst = PyList_New(0); + if (lst == NULL) { + return NULL; + } + + string line = _this->readline(); + while (!line.empty()) { +#if PY_MAJOR_VERSION >= 3 + PyObject *py_line = PyBytes_FromStringAndSize(line.data(), line.size()); +#else + PyObject *py_line = PyString_FromStringAndSize(line.data(), line.size()); +#endif + + PyList_Append(lst, py_line); + Py_DECREF(py_line); + } + + return lst; +} + +#endif + diff --git a/panda/src/express/ramfile_ext.h b/panda/src/express/ramfile_ext.h new file mode 100644 index 0000000000..8f90eb52ad --- /dev/null +++ b/panda/src/express/ramfile_ext.h @@ -0,0 +1,40 @@ +// Filename: ramfile_ext.h +// Created by: rdb (10Dec13) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +#ifndef RAMFILE_EXT_H +#define RAMFILE_EXT_H + +#include "dtoolbase.h" + +#ifdef HAVE_PYTHON + +#include "extension.h" +#include "ramfile.h" +#include "py_panda.h" + +//////////////////////////////////////////////////////////////////// +// Class : Extension +// Description : This class defines the extension methods for +// Ramfile, which are called instead of +// any C++ methods with the same prototype. +//////////////////////////////////////////////////////////////////// +template<> +class Extension : public ExtensionBase { +public: + BLOCKING PyObject *readlines(); +}; + +#endif // HAVE_PYTHON + +#endif // RAMFILE_EXT_H diff --git a/panda/src/express/streamReader_ext.cxx b/panda/src/express/streamReader_ext.cxx new file mode 100644 index 0000000000..c0aa00f26a --- /dev/null +++ b/panda/src/express/streamReader_ext.cxx @@ -0,0 +1,48 @@ +// Filename: streamReader_ext.cxx +// Created by: rdb (09Dec13) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +#include "streamReader_ext.h" + +#ifdef HAVE_PYTHON + +//////////////////////////////////////////////////////////////////// +// Function: StreamReader::readlines +// Access: Published +// Description: Reads all the lines at once and returns a list. +// Also see the documentation for readline(). +//////////////////////////////////////////////////////////////////// +PyObject *Extension:: +readlines() { + PyObject *lst = PyList_New(0); + if (lst == NULL) { + return NULL; + } + + string line = _this->readline(); + while (!line.empty()) { +#if PY_MAJOR_VERSION >= 3 + PyObject *py_line = PyBytes_FromStringAndSize(line.data(), line.size()); +#else + PyObject *py_line = PyString_FromStringAndSize(line.data(), line.size()); +#endif + + PyList_Append(lst, py_line); + Py_DECREF(py_line); + } + + return lst; +} + +#endif + diff --git a/panda/src/express/streamReader_ext.h b/panda/src/express/streamReader_ext.h new file mode 100644 index 0000000000..dfc2e9f0b1 --- /dev/null +++ b/panda/src/express/streamReader_ext.h @@ -0,0 +1,40 @@ +// Filename: streamReader_ext.h +// Created by: rdb (09Dec13) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +#ifndef STREAMREADER_EXT_H +#define STREAMREADER_EXT_H + +#include "dtoolbase.h" + +#ifdef HAVE_PYTHON + +#include "extension.h" +#include "streamReader.h" +#include "py_panda.h" + +//////////////////////////////////////////////////////////////////// +// Class : Extension +// Description : This class defines the extension methods for +// StreamReader, which are called instead of +// any C++ methods with the same prototype. +//////////////////////////////////////////////////////////////////// +template<> +class Extension : public ExtensionBase { +public: + BLOCKING PyObject *readlines(); +}; + +#endif // HAVE_PYTHON + +#endif // STREAMREADER_EXT_H diff --git a/panda/src/express/virtualFileSystem_ext.cxx b/panda/src/express/virtualFileSystem_ext.cxx index c5981b347a..3f1c3f40f8 100644 --- a/panda/src/express/virtualFileSystem_ext.cxx +++ b/panda/src/express/virtualFileSystem_ext.cxx @@ -79,4 +79,4 @@ write_file(const Filename &filename, PyObject *data, bool auto_wrap) { return PyBool_FromLong(result); } -#endif // HAVE_PYTHOS +#endif // HAVE_PYTHON diff --git a/panda/src/linmath/lpoint2_ext_src.I b/panda/src/linmath/lpoint2_ext_src.I index b5716fa071..195c1b38d9 100644 --- a/panda/src/linmath/lpoint2_ext_src.I +++ b/panda/src/linmath/lpoint2_ext_src.I @@ -82,5 +82,5 @@ __getattr__(const string &attr_name) const { INLINE_LINMATH int Extension:: __setattr__(PyObject *self, const string &attr_name, PyObject *assign) { // Upcall to LVecBase2. - return invoke_extension(_this, _self).__setattr__(self, attr_name, assign); + return invoke_extension(_this).__setattr__(self, attr_name, assign); } diff --git a/panda/src/linmath/lpoint3_ext_src.I b/panda/src/linmath/lpoint3_ext_src.I index 92178755e6..19f32de4e7 100644 --- a/panda/src/linmath/lpoint3_ext_src.I +++ b/panda/src/linmath/lpoint3_ext_src.I @@ -83,5 +83,5 @@ __getattr__(const string &attr_name) const { INLINE_LINMATH int Extension:: __setattr__(PyObject *self, const string &attr_name, PyObject *assign) { // Upcall to LVecBase2. - return invoke_extension(_this, _self).__setattr__(self, attr_name, assign); + return invoke_extension(_this).__setattr__(self, attr_name, assign); } diff --git a/panda/src/linmath/lpoint4_ext_src.I b/panda/src/linmath/lpoint4_ext_src.I index 708150f52b..841e1271b2 100644 --- a/panda/src/linmath/lpoint4_ext_src.I +++ b/panda/src/linmath/lpoint4_ext_src.I @@ -88,5 +88,5 @@ __getattr__(const string &attr_name) const { INLINE_LINMATH int Extension:: __setattr__(PyObject *self, const string &attr_name, PyObject *assign) { // Upcall to LVecBase4. - return invoke_extension(_this, _self).__setattr__(self, attr_name, assign); + return invoke_extension(_this).__setattr__(self, attr_name, assign); } diff --git a/panda/src/linmath/lvector2_ext_src.I b/panda/src/linmath/lvector2_ext_src.I index 39fb074fce..091c68760a 100644 --- a/panda/src/linmath/lvector2_ext_src.I +++ b/panda/src/linmath/lvector2_ext_src.I @@ -82,5 +82,5 @@ __getattr__(const string &attr_name) const { INLINE_LINMATH int Extension:: __setattr__(PyObject *self, const string &attr_name, PyObject *assign) { // Upcall to LVecBase2. - return invoke_extension(_this, _self).__setattr__(self, attr_name, assign); + return invoke_extension(_this).__setattr__(self, attr_name, assign); } diff --git a/panda/src/linmath/lvector3_ext_src.I b/panda/src/linmath/lvector3_ext_src.I index 9706adfea3..a0046c513a 100644 --- a/panda/src/linmath/lvector3_ext_src.I +++ b/panda/src/linmath/lvector3_ext_src.I @@ -83,5 +83,5 @@ __getattr__(const string &attr_name) const { INLINE_LINMATH int Extension:: __setattr__(PyObject *self, const string &attr_name, PyObject *assign) { // Upcall to LVecBase3. - return invoke_extension(_this, _self).__setattr__(self, attr_name, assign); + return invoke_extension(_this).__setattr__(self, attr_name, assign); } diff --git a/panda/src/linmath/lvector4_ext_src.I b/panda/src/linmath/lvector4_ext_src.I index a9ae9eb8d0..8b84439228 100644 --- a/panda/src/linmath/lvector4_ext_src.I +++ b/panda/src/linmath/lvector4_ext_src.I @@ -88,5 +88,5 @@ __getattr__(const string &attr_name) const { INLINE_LINMATH int Extension:: __setattr__(PyObject *self, const string &attr_name, PyObject *assign) { // Upcall to LVecBase4. - return invoke_extension(_this, _self).__setattr__(self, attr_name, assign); + return invoke_extension(_this).__setattr__(self, attr_name, assign); } diff --git a/panda/src/ode/Sources.pp b/panda/src/ode/Sources.pp index 1006da1815..6d1393f815 100755 --- a/panda/src/ode/Sources.pp +++ b/panda/src/ode/Sources.pp @@ -12,18 +12,21 @@ #define USE_PACKAGES ode #define COMBINED_SOURCES $[TARGET]_composite1.cxx \ - $[TARGET]_composite2.cxx $[TARGET]_composite3.cxx + $[TARGET]_composite2.cxx $[TARGET]_composite3.cxx \ + $[TARGET]_ext_composite.cxx #define SOURCES \ ode_includes.h config_ode.h \ odeWorld.I odeWorld.h \ odeMass.I odeMass.h \ - odeBody.I odeBody.h \ + odeBody.I odeBody.h odeBody_ext.h \ odeJointGroup.I odeJointGroup.h \ - odeJoint.I odeJoint.h \ - odeUtil.h \ + odeJoint.I odeJoint.h odeJoint_ext.h \ + odeUtil.h odeUtil_ext.h \ odeSpace.I odeSpace.h \ + odeSpace_ext.I odeSpace_ext.h \ odeGeom.I odeGeom.h \ + odeGeom_ext.I odeGeom_ext.h \ odeSurfaceParameters.I odeSurfaceParameters.h \ odeContactGeom.I odeContactGeom.h \ odeContact.I odeContact.h \ @@ -55,11 +58,13 @@ #define INCLUDED_SOURCES \ config_ode.cxx \ - odeWorld.cxx odeMass.cxx odeBody.cxx \ - odeJointGroup.cxx odeJoint.cxx \ - odeUtil.cxx \ - odeSpace.cxx \ - odeGeom.cxx \ + odeWorld.cxx odeMass.cxx \ + odeBody.cxx odeBody_ext.cxx \ + odeJointGroup.cxx \ + odeJoint.cxx odeJoint_ext.cxx \ + odeUtil.cxx odeUtil_ext.cxx \ + odeSpace.cxx odeSpace_ext.cxx \ + odeGeom.cxx odeGeom_ext.cxx \ odeSurfaceParameters.cxx \ odeContactGeom.cxx odeContact.cxx \ odeAMotorJoint.cxx odeBallJoint.cxx \ @@ -118,4 +123,3 @@ #define IGATESCAN all #end lib_target - diff --git a/panda/src/ode/odeAMotorJoint.h b/panda/src/ode/odeAMotorJoint.h index 20bd0d3c99..c3b41a677d 100644 --- a/panda/src/ode/odeAMotorJoint.h +++ b/panda/src/ode/odeAMotorJoint.h @@ -16,7 +16,7 @@ class EXPCL_PANDAODE OdeAMotorJoint : public OdeJoint { friend class OdeJoint; -private: +public: OdeAMotorJoint(dJointID id); PUBLISHED: diff --git a/panda/src/ode/odeBallJoint.h b/panda/src/ode/odeBallJoint.h index 7fb9c104c3..be6100bf76 100644 --- a/panda/src/ode/odeBallJoint.h +++ b/panda/src/ode/odeBallJoint.h @@ -16,7 +16,7 @@ class EXPCL_PANDAODE OdeBallJoint : public OdeJoint { friend class OdeJoint; -private: +public: OdeBallJoint(dJointID id); PUBLISHED: diff --git a/panda/src/ode/odeBody.h b/panda/src/ode/odeBody.h index 51c5394d35..7a6fa3ffd7 100755 --- a/panda/src/ode/odeBody.h +++ b/panda/src/ode/odeBody.h @@ -39,7 +39,7 @@ class EXPCL_PANDAODE OdeBody : public TypedObject { friend class OdeGeom; friend class OdeCollisionEntry; -protected: +public: OdeBody(dBodyID id); PUBLISHED: @@ -140,6 +140,8 @@ PUBLISHED: INLINE int get_num_joints() const; OdeJoint get_joint(int index) const; MAKE_SEQ(get_joints, get_num_joints, get_joint); + EXTENSION(INLINE PyObject *get_converted_joint(int i) const); + INLINE void enable(); INLINE void disable(); INLINE int is_enabled() const; diff --git a/panda/src/ode/odeBoxGeom.h b/panda/src/ode/odeBoxGeom.h index a4336eb93d..9991124a59 100644 --- a/panda/src/ode/odeBoxGeom.h +++ b/panda/src/ode/odeBoxGeom.h @@ -29,7 +29,7 @@ class EXPCL_PANDAODE OdeBoxGeom : public OdeGeom { friend class OdeGeom; -private: +public: OdeBoxGeom(dGeomID id); PUBLISHED: diff --git a/panda/src/ode/odeCappedCylinderGeom.h b/panda/src/ode/odeCappedCylinderGeom.h index ce56ab5816..2e48cb36c0 100755 --- a/panda/src/ode/odeCappedCylinderGeom.h +++ b/panda/src/ode/odeCappedCylinderGeom.h @@ -29,7 +29,7 @@ class EXPCL_PANDAODE OdeCappedCylinderGeom : public OdeGeom { friend class OdeGeom; -private: +public: OdeCappedCylinderGeom(dGeomID id); PUBLISHED: diff --git a/panda/src/ode/odeContactJoint.h b/panda/src/ode/odeContactJoint.h index 963996a9fc..f2edc91de2 100644 --- a/panda/src/ode/odeContactJoint.h +++ b/panda/src/ode/odeContactJoint.h @@ -17,7 +17,7 @@ class EXPCL_PANDAODE OdeContactJoint : public OdeJoint { friend class OdeJoint; -private: +public: OdeContactJoint(dJointID id); PUBLISHED: diff --git a/panda/src/ode/odeConvexGeom.h b/panda/src/ode/odeConvexGeom.h index 7a7819e7eb..bc4104855a 100755 --- a/panda/src/ode/odeConvexGeom.h +++ b/panda/src/ode/odeConvexGeom.h @@ -29,7 +29,7 @@ class EXPCL_PANDAODE OdeConvexGeom : public OdeGeom { friend class OdeGeom; -private: +public: OdeConvexGeom(dGeomID id); PUBLISHED: diff --git a/panda/src/ode/odeCylinderGeom.h b/panda/src/ode/odeCylinderGeom.h index 9d4bbbd2c7..1b847c908c 100755 --- a/panda/src/ode/odeCylinderGeom.h +++ b/panda/src/ode/odeCylinderGeom.h @@ -29,7 +29,7 @@ class EXPCL_PANDAODE OdeCylinderGeom : public OdeGeom { friend class OdeGeom; -private: +public: OdeCylinderGeom(dGeomID id); PUBLISHED: diff --git a/panda/src/ode/odeFixedJoint.h b/panda/src/ode/odeFixedJoint.h index 5c074303cb..a1d8a6d59e 100644 --- a/panda/src/ode/odeFixedJoint.h +++ b/panda/src/ode/odeFixedJoint.h @@ -16,7 +16,7 @@ class EXPCL_PANDAODE OdeFixedJoint : public OdeJoint { friend class OdeJoint; -private: +public: OdeFixedJoint(dJointID id); PUBLISHED: diff --git a/panda/src/ode/odeGeom.h b/panda/src/ode/odeGeom.h index daea43908a..dff9eddeed 100755 --- a/panda/src/ode/odeGeom.h +++ b/panda/src/ode/odeGeom.h @@ -50,7 +50,7 @@ class EXPCL_PANDAODE OdeGeom : public TypedObject { friend class OdeUtil; friend class OdeCollisionEntry; -protected: +public: OdeGeom(dGeomID id); PUBLISHED: @@ -87,6 +87,7 @@ PUBLISHED: INLINE LMatrix3f get_rotation() const; INLINE LQuaternionf get_quaternion() const; INLINE void get_AABB(LVecBase3f &min, LVecBase3f &max) const; + EXTENSION(INLINE PyObject *get_AA_bounds() const); INLINE int is_space(); INLINE int get_class() const; INLINE void set_category_bits(const BitMask32 &bits); @@ -109,21 +110,22 @@ PUBLISHED: INLINE LPoint3f get_offset_position() const; INLINE LMatrix3f get_offset_rotation() const; INLINE LQuaternionf get_offset_quaternion() const; - + //int get_surface_type() ; //int get_collide_id() ; //int set_collide_id( int collide_id); //void set_surface_type( int surface_type); - + //int test_collide_id( int collide_id); - OdeSpace get_space() const; + EXTENSION(INLINE PyObject *get_converted_space() const); virtual void write(ostream &out = cout, unsigned int indent=0) const; operator bool () const; INLINE int compare_to(const OdeGeom &other) const; + EXTENSION(PyObject *convert() const); OdeBoxGeom convert_to_box() const; OdeCappedCylinderGeom convert_to_capped_cylinder() const; // OdeConvexGeom convert_to_convex() const; @@ -136,8 +138,6 @@ PUBLISHED: OdeSimpleSpace convert_to_simple_space() const; OdeHashSpace convert_to_hash_space() const; OdeQuadTreeSpace convert_to_quad_tree_space() const; - - public: INLINE static int get_geom_class() { return -1; }; diff --git a/panda/src/ode/odeHashSpace.h b/panda/src/ode/odeHashSpace.h index d66a99d43b..03e2983689 100755 --- a/panda/src/ode/odeHashSpace.h +++ b/panda/src/ode/odeHashSpace.h @@ -31,7 +31,7 @@ class EXPCL_PANDAODE OdeHashSpace : public OdeSpace { friend class OdeSpace; friend class OdeGeom; -private: +public: OdeHashSpace(dSpaceID id); PUBLISHED: diff --git a/panda/src/ode/odeHeightFieldGeom.h b/panda/src/ode/odeHeightFieldGeom.h index 7b648a3539..00fd6593a1 100644 --- a/panda/src/ode/odeHeightFieldGeom.h +++ b/panda/src/ode/odeHeightFieldGeom.h @@ -29,7 +29,7 @@ class EXPCL_PANDAODE OdeHeightfieldGeom : public OdeGeom { friend class OdeGeom; -private: +public: OdeHeightfieldGeom(dGeomID id); PUBLISHED: diff --git a/panda/src/ode/odeHinge2Joint.h b/panda/src/ode/odeHinge2Joint.h index dcb8e732e9..2e6e5dd487 100644 --- a/panda/src/ode/odeHinge2Joint.h +++ b/panda/src/ode/odeHinge2Joint.h @@ -16,7 +16,7 @@ class EXPCL_PANDAODE OdeHinge2Joint : public OdeJoint { friend class OdeJoint; -private: +public: OdeHinge2Joint(dJointID id); PUBLISHED: diff --git a/panda/src/ode/odeHingeJoint.h b/panda/src/ode/odeHingeJoint.h index c622d7996e..0cc9fefadc 100755 --- a/panda/src/ode/odeHingeJoint.h +++ b/panda/src/ode/odeHingeJoint.h @@ -15,7 +15,7 @@ class EXPCL_PANDAODE OdeHingeJoint : public OdeJoint { friend class OdeJoint; -private: +public: OdeHingeJoint(dJointID id); PUBLISHED: diff --git a/panda/src/ode/odeJoint.cxx b/panda/src/ode/odeJoint.cxx index b1d235efcb..8aa287e30a 100755 --- a/panda/src/ode/odeJoint.cxx +++ b/panda/src/ode/odeJoint.cxx @@ -52,7 +52,7 @@ destroy() { void OdeJoint:: attach_bodies(const OdeBody &body1, const OdeBody &body2) { nassertv(_id); - nassertv(body1.get_id() != 0 && body2.get_id() != 0); + nassertv(body1.get_id() != 0 || body2.get_id() != 0); dJointAttach(_id, body1.get_id(), body2.get_id()); } diff --git a/panda/src/ode/odeJoint.h b/panda/src/ode/odeJoint.h index b3fcead63b..0eb44b61bf 100755 --- a/panda/src/ode/odeJoint.h +++ b/panda/src/ode/odeJoint.h @@ -32,7 +32,7 @@ PUBLISHED: }; // Strange, we should be forced to include this by get_body() -class OdeBody; +class OdeBody; class OdeBallJoint; class OdeHingeJoint; @@ -48,7 +48,7 @@ class OdePlane2dJoint; //////////////////////////////////////////////////////////////////// // Class : OdeJoint -// Description : +// Description : //////////////////////////////////////////////////////////////////// class EXPCL_PANDAODE OdeJoint : public TypedObject { friend class OdeBody; @@ -56,8 +56,6 @@ class EXPCL_PANDAODE OdeJoint : public TypedObject { public: OdeJoint(); - -protected: OdeJoint(dJointID id); PUBLISHED: @@ -78,7 +76,7 @@ PUBLISHED: void destroy(); INLINE bool is_empty() const; INLINE dJointID get_id() const; - + /* INLINE void set_data(void *data); */ /* INLINE void *get_data(); */ INLINE int get_joint_type() const; @@ -86,7 +84,8 @@ PUBLISHED: INLINE void set_feedback(OdeJointFeedback *); INLINE void set_feedback(bool flag = true); INLINE OdeJointFeedback *get_feedback(); - + + EXTENSION(void attach(const OdeBody *body1, const OdeBody *body2)); void attach_bodies(const OdeBody &body1, const OdeBody &body2); void attach_body(const OdeBody &body, int index); void detach(); @@ -96,6 +95,7 @@ PUBLISHED: INLINE bool operator == (const OdeJoint &other) const; operator bool () const; + EXTENSION(PyObject *convert() const); OdeBallJoint convert_to_ball() const; OdeHingeJoint convert_to_hinge() const; OdeSliderJoint convert_to_slider() const; diff --git a/panda/src/ode/odeLMotorJoint.h b/panda/src/ode/odeLMotorJoint.h index 3b91cbbeb7..541afcd904 100755 --- a/panda/src/ode/odeLMotorJoint.h +++ b/panda/src/ode/odeLMotorJoint.h @@ -15,7 +15,7 @@ class EXPCL_PANDAODE OdeLMotorJoint : public OdeJoint { friend class OdeJoint; -private: +public: OdeLMotorJoint(dJointID id); PUBLISHED: diff --git a/panda/src/ode/odeNullJoint.h b/panda/src/ode/odeNullJoint.h index 94c659ab92..61f3912866 100755 --- a/panda/src/ode/odeNullJoint.h +++ b/panda/src/ode/odeNullJoint.h @@ -15,7 +15,7 @@ class EXPCL_PANDAODE OdeNullJoint : public OdeJoint { friend class OdeJoint; -private: +public: OdeNullJoint(dJointID id); PUBLISHED: diff --git a/panda/src/ode/odePlane2dJoint.h b/panda/src/ode/odePlane2dJoint.h index e70e75181d..cb9e7eb6de 100755 --- a/panda/src/ode/odePlane2dJoint.h +++ b/panda/src/ode/odePlane2dJoint.h @@ -15,7 +15,7 @@ class EXPCL_PANDAODE OdePlane2dJoint : public OdeJoint { friend class OdeJoint; -private: +public: OdePlane2dJoint(dJointID id); PUBLISHED: diff --git a/panda/src/ode/odePlaneGeom.h b/panda/src/ode/odePlaneGeom.h index 1412d472bc..5131f55f78 100755 --- a/panda/src/ode/odePlaneGeom.h +++ b/panda/src/ode/odePlaneGeom.h @@ -28,7 +28,7 @@ class EXPCL_PANDAODE OdePlaneGeom : public OdeGeom { friend class OdeGeom; -private: +public: OdePlaneGeom(dGeomID id); PUBLISHED: diff --git a/panda/src/ode/odeQuadTreeSpace.h b/panda/src/ode/odeQuadTreeSpace.h index adb05d7d09..0a57a7668f 100755 --- a/panda/src/ode/odeQuadTreeSpace.h +++ b/panda/src/ode/odeQuadTreeSpace.h @@ -30,7 +30,7 @@ class EXPCL_PANDAODE OdeQuadTreeSpace : public OdeSpace { friend class OdeSpace; friend class OdeGeom; -private: +public: OdeQuadTreeSpace(dSpaceID id); PUBLISHED: diff --git a/panda/src/ode/odeRayGeom.h b/panda/src/ode/odeRayGeom.h index d1d16b0027..d8c4f64d98 100755 --- a/panda/src/ode/odeRayGeom.h +++ b/panda/src/ode/odeRayGeom.h @@ -28,7 +28,7 @@ class EXPCL_PANDAODE OdeRayGeom : public OdeGeom { friend class OdeGeom; -private: +public: OdeRayGeom(dGeomID id); PUBLISHED: diff --git a/panda/src/ode/odeSimpleSpace.h b/panda/src/ode/odeSimpleSpace.h index af8edc03d0..63c16a8901 100755 --- a/panda/src/ode/odeSimpleSpace.h +++ b/panda/src/ode/odeSimpleSpace.h @@ -30,7 +30,7 @@ class EXPCL_PANDAODE OdeSimpleSpace : public OdeSpace { friend class OdeSpace; friend class OdeGeom; -private: +public: OdeSimpleSpace(dSpaceID id); PUBLISHED: diff --git a/panda/src/ode/odeSliderJoint.h b/panda/src/ode/odeSliderJoint.h index 3ee063daf2..a4d8117525 100755 --- a/panda/src/ode/odeSliderJoint.h +++ b/panda/src/ode/odeSliderJoint.h @@ -15,7 +15,7 @@ class EXPCL_PANDAODE OdeSliderJoint : public OdeJoint { friend class OdeJoint; -private: +public: OdeSliderJoint(dJointID id); PUBLISHED: diff --git a/panda/src/ode/odeSpace.cxx b/panda/src/ode/odeSpace.cxx index fecb33d877..e98086c711 100755 --- a/panda/src/ode/odeSpace.cxx +++ b/panda/src/ode/odeSpace.cxx @@ -17,23 +17,12 @@ #include "throw_event.h" -#ifdef HAVE_PYTHON - #include "py_panda.h" - #include "typedReferenceCount.h" - #ifndef CPPPARSER - extern EXPCL_PANDAODE Dtool_PyTypedObject Dtool_OdeGeom; - #endif -#endif - TypeHandle OdeSpace::_type_handle; // this data is used in auto_collide const int OdeSpace::MAX_CONTACTS = 16; OdeWorld* OdeSpace::_static_auto_collide_world; OdeSpace* OdeSpace::_static_auto_collide_space; dJointGroupID OdeSpace::_static_auto_collide_joint_group; -#ifdef HAVE_PYTHON -PyObject* OdeSpace::_python_callback = NULL; -#endif OdeSpace:: OdeSpace(dSpaceID id) : @@ -142,23 +131,23 @@ auto_callback(void *data, dGeomID o1, dGeomID o2) { dBodyID b2 = dGeomGetBody(o2); dContact contact[OdeSpace::MAX_CONTACTS]; - + int surface1 = _static_auto_collide_space->get_surface_type(o1); int surface2 = _static_auto_collide_space->get_surface_type(o2); - + nassertv(_static_auto_collide_world != NULL); sSurfaceParams collide_params; collide_params = _static_auto_collide_world->get_surface(surface1, surface2); - + for (i=0; i < OdeSpace::MAX_CONTACTS; i++) { - contact[i].surface.mode = collide_params.colparams.mode; - contact[i].surface.mu = collide_params.colparams.mu; - contact[i].surface.mu2 = collide_params.colparams.mu2; - contact[i].surface.bounce = collide_params.colparams.bounce; - contact[i].surface.bounce_vel = collide_params.colparams.bounce_vel; - contact[i].surface.soft_cfm = collide_params.colparams.soft_cfm; + contact[i].surface.mode = collide_params.colparams.mode; + contact[i].surface.mu = collide_params.colparams.mu; + contact[i].surface.mu2 = collide_params.colparams.mu2; + contact[i].surface.bounce = collide_params.colparams.bounce; + contact[i].surface.bounce_vel = collide_params.colparams.bounce_vel; + contact[i].surface.soft_cfm = collide_params.colparams.soft_cfm; } - + static int numc = 0; numc = dCollide(o1, o2, OdeSpace::MAX_CONTACTS, &contact[0].geom, sizeof(dContact)); @@ -177,7 +166,7 @@ auto_callback(void *data, dGeomID o1, dGeomID o2) { entry->_num_contacts = numc; entry->_contact_geoms = new OdeContactGeom[numc]; } - + for(i=0; i < numc; i++) { dJointID c = dJointCreateContact(_static_auto_collide_world->get_id(), _static_auto_collide_joint_group, contact + i); if ((_static_auto_collide_space->get_collide_id(o1) >= 0) && (_static_auto_collide_space->get_collide_id(o2) >= 0)) { @@ -188,51 +177,13 @@ auto_callback(void *data, dGeomID o1, dGeomID o2) { } } _static_auto_collide_world->set_dampen_on_bodies(b1, b2, collide_params.dampen); - + if (!_static_auto_collide_space->_collision_event.empty()) { throw_event(_static_auto_collide_space->_collision_event, EventParameter(entry)); } } } -#ifdef HAVE_PYTHON -int OdeSpace:: -collide(PyObject* arg, PyObject* callback) { - nassertr(callback != NULL, -1); - if (!PyCallable_Check(callback)) { - PyErr_Format(PyExc_TypeError, "'%s' object is not callable", callback->ob_type->tp_name); - return -1; - } else if (_id == NULL) { - // Well, while we're in the mood of python exceptions, let's make this one too. - PyErr_Format(PyExc_TypeError, "OdeSpace is not valid!"); - return -1; - } else { - OdeSpace::_python_callback = (PyObject*) callback; - Py_XINCREF(OdeSpace::_python_callback); - dSpaceCollide(_id, (void*) arg, &near_callback); - Py_XDECREF(OdeSpace::_python_callback); - return 0; - } -} - -void OdeSpace:: -near_callback(void *data, dGeomID o1, dGeomID o2) { - OdeGeom *g1 = new OdeGeom(o1); - OdeGeom *g2 = new OdeGeom(o2); - PyObject *p1 = DTool_CreatePyInstanceTyped(g1, Dtool_OdeGeom, true, false, g1->get_type_index()); - PyObject *p2 = DTool_CreatePyInstanceTyped(g2, Dtool_OdeGeom, true, false, g2->get_type_index()); - PyObject *result = PyEval_CallFunction(_python_callback, "OOO", (PyObject*) data, p1, p2); - if (!result) { - odespace_cat.error() << "An error occurred while calling python function!\n"; - PyErr_Print(); - } else { - Py_DECREF(result); - } - Py_XDECREF(p2); - Py_XDECREF(p1); -} -#endif - OdeSimpleSpace OdeSpace:: convert_to_simple_space() const { nassertr(_id != 0, OdeSimpleSpace((dSpaceID)0)); @@ -309,4 +260,3 @@ get_collide_id(dGeomID id) { } return 0; } - diff --git a/panda/src/ode/odeSpace.h b/panda/src/ode/odeSpace.h index e61f4aa1e2..07b6c8f5d7 100755 --- a/panda/src/ode/odeSpace.h +++ b/panda/src/ode/odeSpace.h @@ -26,11 +26,6 @@ #include "ode_includes.h" -#ifdef HAVE_PYTHON - #include "py_panda.h" - #include "Python.h" -#endif - class OdeGeom; class OdeTriMeshGeom; class OdeSimpleSpace; @@ -39,15 +34,15 @@ class OdeQuadTreeSpace; //////////////////////////////////////////////////////////////////// // Class : OdeSpace -// Description : +// Description : //////////////////////////////////////////////////////////////////// class EXPCL_PANDAODE OdeSpace : public TypedObject { friend class OdeGeom; static const int MAX_CONTACTS; -protected: +public: OdeSpace(dSpaceID id); - + PUBLISHED: virtual ~OdeSpace(); void destroy(); @@ -59,6 +54,7 @@ PUBLISHED: int query(const OdeSpace& space) const; INLINE int get_num_geoms() const; INLINE void get_AABB(LVecBase3f &min, LVecBase3f &max) const; + EXTENSION(INLINE PyObject *get_AA_bounds() const); INLINE int is_space(); INLINE int get_class() const; INLINE void set_category_bits(const BitMask32 &bits); @@ -78,7 +74,7 @@ PUBLISHED: void clean(); OdeGeom get_geom(int i); // Not INLINE because of forward declaration //static int get_surface_type(OdeSpace * self, dGeomID o1); - + INLINE OdeSpace get_space() const; virtual void write(ostream &out = cout, unsigned int indent=0) const; @@ -87,11 +83,13 @@ PUBLISHED: OdeSimpleSpace convert_to_simple_space() const; OdeHashSpace convert_to_hash_space() const; OdeQuadTreeSpace convert_to_quad_tree_space() const; - + + EXTENSION(PyObject *convert() const); + EXTENSION(INLINE PyObject *get_converted_geom(int i) const); + EXTENSION(INLINE PyObject *get_converted_space() const); + void auto_collide(); -#ifdef HAVE_PYTHON - int collide(PyObject* arg, PyObject* near_callback); -#endif + EXTENSION(int collide(PyObject* arg, PyObject* near_callback)); int set_collide_id(int collide_id, dGeomID id); int set_collide_id(OdeGeom& geom, int collide_id); void set_surface_type( int surface_type, dGeomID id); @@ -106,17 +104,11 @@ PUBLISHED: public: static void auto_callback(void*, dGeomID, dGeomID); -#ifdef HAVE_PYTHON - static void near_callback(void*, dGeomID, dGeomID); -#endif - + INLINE dSpaceID get_id() const; static OdeWorld* _static_auto_collide_world; static OdeSpace* _static_auto_collide_space; static dJointGroupID _static_auto_collide_joint_group; -#ifdef HAVE_PYTHON - static PyObject* _python_callback; -#endif static int contactCount; string _collision_event; @@ -153,4 +145,3 @@ private: #include "odeSpace.I" #endif - diff --git a/panda/src/ode/odeSphereGeom.h b/panda/src/ode/odeSphereGeom.h index 5b02a1ff76..2c7e3157b4 100755 --- a/panda/src/ode/odeSphereGeom.h +++ b/panda/src/ode/odeSphereGeom.h @@ -28,7 +28,7 @@ class EXPCL_PANDAODE OdeSphereGeom : public OdeGeom { friend class OdeGeom; -private: +public: OdeSphereGeom(dGeomID id); PUBLISHED: diff --git a/panda/src/ode/odeTriMeshGeom.h b/panda/src/ode/odeTriMeshGeom.h index 8a3f5943ed..ad1dfe665a 100755 --- a/panda/src/ode/odeTriMeshGeom.h +++ b/panda/src/ode/odeTriMeshGeom.h @@ -29,7 +29,7 @@ class EXPCL_PANDAODE OdeTriMeshGeom : public OdeGeom { friend class OdeGeom; -private: +public: OdeTriMeshGeom(dGeomID id); PUBLISHED: @@ -56,9 +56,6 @@ public: INLINE dTriMeshDataID get_tri_mesh_data_id() const; INLINE dTriMeshDataID get_data_id() const; -private: - void operator = (const OdeTriMeshGeom ©); - public: static TypeHandle get_class_type() { return _type_handle; diff --git a/panda/src/ode/odeUniversalJoint.h b/panda/src/ode/odeUniversalJoint.h index 3f79d8ec48..d4e3e4c185 100644 --- a/panda/src/ode/odeUniversalJoint.h +++ b/panda/src/ode/odeUniversalJoint.h @@ -16,7 +16,7 @@ class EXPCL_PANDAODE OdeUniversalJoint : public OdeJoint { friend class OdeJoint; -private: +public: OdeUniversalJoint(dJointID id); PUBLISHED: diff --git a/panda/src/ode/odeUtil.cxx b/panda/src/ode/odeUtil.cxx index edc2f8236c..4bb6471881 100755 --- a/panda/src/ode/odeUtil.cxx +++ b/panda/src/ode/odeUtil.cxx @@ -14,20 +14,8 @@ #include "odeUtil.h" -#ifdef HAVE_PYTHON - #include "py_panda.h" - #include "typedReferenceCount.h" - #ifndef CPPPARSER - extern EXPCL_PANDAODE Dtool_PyTypedObject Dtool_OdeGeom; - #endif -#endif - dReal OdeUtil::OC_infinity = dInfinity; -#ifdef HAVE_PYTHON -PyObject* OdeUtil::_python_callback = NULL; -#endif - //////////////////////////////////////////////////////////////////// // Function: OdeUtil::get_connecting_joint // Access: Public, Static @@ -47,7 +35,7 @@ get_connecting_joint(const OdeBody &body1, const OdeBody &body2) { OdeJointCollection OdeUtil:: get_connecting_joint_list(const OdeBody &body1, const OdeBody &body2) { const int max_possible_joints = min(body1.get_num_joints(), body1.get_num_joints()); - + dJointID *joint_list = (dJointID *)PANDA_MALLOC_ARRAY(max_possible_joints * sizeof(dJointID)); int num_joints = dConnectingJointList(body1.get_id(), body2.get_id(), joint_list); @@ -55,7 +43,7 @@ get_connecting_joint_list(const OdeBody &body1, const OdeBody &body2) { for (int i = 0; i < num_joints; i++) { joints.add_joint(OdeJoint(joint_list[i])); } - + PANDA_FREE_ARRAY(joint_list); return joints; } @@ -113,51 +101,12 @@ collide(const OdeGeom &geom1, const OdeGeom &geom2, const short int max_contacts for (int i = 0; i < num_contacts; i++) { entry->_contact_geoms[i] = contact_list[i]; } - + PANDA_FREE_ARRAY(contact_list); return entry; } -#ifdef HAVE_PYTHON -//////////////////////////////////////////////////////////////////// -// Function: OdeUtil::collide2 -// Access: Public, Static -// Description: Calls the callback for all potentially intersecting -// pairs that contain one geom from geom1 and one geom -// from geom2. -//////////////////////////////////////////////////////////////////// -int OdeUtil:: -collide2(const OdeGeom &geom1, const OdeGeom &geom2, PyObject* arg, PyObject* callback) { - nassertr(callback != NULL, -1); - if (!PyCallable_Check(callback)) { - PyErr_Format(PyExc_TypeError, "'%s' object is not callable", callback->ob_type->tp_name); - return -1; - } else { - _python_callback = (PyObject*) callback; - Py_XINCREF(_python_callback); - dSpaceCollide2(geom1.get_id(), geom2.get_id(), (void*) arg, &near_callback); - Py_XDECREF(_python_callback); - return 0; - } -} - -void OdeUtil:: -near_callback(void *data, dGeomID o1, dGeomID o2) { - ode_cat.spam() << "near_callback called, data: " << data << ", dGeomID1: " << o1 << ", dGeomID2: " << o2 << "\n"; - OdeGeom g1 (o1); - OdeGeom g2 (o2); - PyObject* p1 = DTool_CreatePyInstanceTyped(&g1, Dtool_OdeGeom, true, false, g1.get_type_index()); - PyObject* p2 = DTool_CreatePyInstanceTyped(&g2, Dtool_OdeGeom, true, false, g2.get_type_index()); - PyObject* result = PyEval_CallFunction(_python_callback, "OOO", (PyObject*) data, p1, p2); - if (!result) { - ode_cat.error() << "An error occurred while calling python function!\n"; - PyErr_Print(); - } -} -#endif - OdeGeom OdeUtil:: space_to_geom(const OdeSpace &space) { return OdeGeom((dGeomID)space.get_id()); } - diff --git a/panda/src/ode/odeUtil.h b/panda/src/ode/odeUtil.h index 654b7f1008..d24844ac89 100755 --- a/panda/src/ode/odeUtil.h +++ b/panda/src/ode/odeUtil.h @@ -23,10 +23,6 @@ #include "odeJointCollection.h" #include "odeCollisionEntry.h" -#ifdef HAVE_PYTHON - #include "py_panda.h" -#endif - class OdeBody; class OdeJoint; class OdeGeom; @@ -48,10 +44,10 @@ PUBLISHED: const int joint_type); static PT(OdeCollisionEntry) collide(const OdeGeom &geom1, const OdeGeom &geom2, const short int max_contacts = 150); -#ifdef HAVE_PYTHON - static int collide2(const OdeGeom &geom1, const OdeGeom &geom2, - PyObject* arg, PyObject* callback); -#endif + + EXTENSION(static int collide2(const OdeGeom &geom1, const OdeGeom &geom2, + PyObject* arg, PyObject* callback)); + static OdeGeom space_to_geom(const OdeSpace &space); static dReal OC_infinity; @@ -62,12 +58,6 @@ PUBLISHED: static int rand_get_seed() {return dRandGetSeed();}; static void rand_set_seed(int s) {dRandSetSeed(s);}; - -private: -#ifdef HAVE_PYTHON - static void near_callback(void*, dGeomID, dGeomID); - static PyObject* _python_callback; -#endif }; #endif diff --git a/panda/src/ode/p3ode_ext_composite.cxx b/panda/src/ode/p3ode_ext_composite.cxx new file mode 100644 index 0000000000..02ebaec64c --- /dev/null +++ b/panda/src/ode/p3ode_ext_composite.cxx @@ -0,0 +1,4 @@ +#include "odeGeom_ext.cxx" +#include "odeJoint_ext.cxx" +#include "odeSpace_ext.cxx" +#include "odeUtil_ext.cxx" diff --git a/panda/src/pgraph/Sources.pp b/panda/src/pgraph/Sources.pp index ab3d0fe9b9..d0ddb0e0df 100644 --- a/panda/src/pgraph/Sources.pp +++ b/panda/src/pgraph/Sources.pp @@ -72,7 +72,10 @@ modelPool.I modelPool.h \ modelRoot.I modelRoot.h \ nodePath.I nodePath.h nodePath.cxx \ + nodePath_ext.I nodePath_ext.h nodePath_ext.cxx \ nodePathCollection.I nodePathCollection.h \ + nodePathCollection_ext.I nodePathCollection_ext.h \ + nodePathCollection_ext.cxx \ nodePathComponent.I nodePathComponent.h \ occluderEffect.I occluderEffect.h \ occluderNode.I occluderNode.h \ diff --git a/panda/src/pgraph/nodePath.I b/panda/src/pgraph/nodePath.I index d7eced9bf0..c5a0ae25ee 100644 --- a/panda/src/pgraph/nodePath.I +++ b/panda/src/pgraph/nodePath.I @@ -2197,57 +2197,6 @@ get_tag_keys(vector_string &keys) const { node()->get_tag_keys(keys); } -#ifdef HAVE_PYTHON -//////////////////////////////////////////////////////////////////// -// Function: NodePath::get_python_tag_keys -// Access: Published -// Description: Fills the given vector up with the -// list of Python tags on this PandaNode. -// -// It is the user's responsibility to ensure that the -// keys vector is empty before making this call; -// otherwise, the new files will be appended to it. -//////////////////////////////////////////////////////////////////// -INLINE void NodePath:: -get_python_tag_keys(vector_string &keys) const { - nassertv_always(!is_empty()); - node()->get_python_tag_keys(keys); -} - -//////////////////////////////////////////////////////////////////// -// Function: Filename::get_tag_keys -// Access: Published -// Description: This variant on get_tag_keys returns a Python list -// of strings. Returns None if the NodePath is empty. -//////////////////////////////////////////////////////////////////// -INLINE PyObject *NodePath:: -get_tag_keys() const { - // An empty NodePath returns None - if (is_empty()) { - Py_INCREF(Py_None); - return Py_None; - } - return node()->get_tag_keys(); -} - -//////////////////////////////////////////////////////////////////// -// Function: Filename::get_python_tag_keys -// Access: Published -// Description: This variant on get_python_tag_keys returns a -// Python list of strings. -// Returns None if the NodePath is empty. -//////////////////////////////////////////////////////////////////// -INLINE PyObject *NodePath:: -get_python_tag_keys() const { - // An empty NodePath returns None - if (is_empty()) { - Py_INCREF(Py_None); - return Py_None; - } - return node()->get_python_tag_keys(); -} -#endif // HAVE_PYTHON - //////////////////////////////////////////////////////////////////// // Function: NodePath::has_tag // Access: Published @@ -2305,114 +2254,6 @@ has_net_tag(const string &key) const { return !find_net_tag(key).is_empty(); } -#ifdef HAVE_PYTHON -//////////////////////////////////////////////////////////////////// -// Function: NodePath::set_python_tag -// Access: Published -// Description: Associates an arbitrary Python object with a -// user-defined key which is stored on the node. This -// object has no meaning to Panda; but it is stored -// indefinitely on the node until it is requested again. -// -// Each unique key stores a different Python object. -// There is no effective limit on the number of -// different keys that may be stored or on the nature of -// any one key's object. -//////////////////////////////////////////////////////////////////// -INLINE void NodePath:: -set_python_tag(const string &key, PyObject *value) { - nassertv_always(!is_empty()); - node()->set_python_tag(key, value); -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -//////////////////////////////////////////////////////////////////// -// Function: NodePath::get_python_tag -// Access: Published -// Description: Retrieves the Python object that was previously -// set on this node for the particular key, if any. If -// no object has been previously set, returns None. -// See also get_net_python_tag(). -//////////////////////////////////////////////////////////////////// -INLINE PyObject *NodePath:: -get_python_tag(const string &key) const { - // An empty NodePath quietly returns no tags. This makes - // get_net_python_tag() easier to implement. - if (is_empty()) { - Py_INCREF(Py_None); - return Py_None; - } - return node()->get_python_tag(key); -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -//////////////////////////////////////////////////////////////////// -// Function: NodePath::has_python_tag -// Access: Published -// Description: Returns true if a Python object has been defined on -// this node for the particular key (even if that value -// is the empty string), or false if no value has been -// set. See also has_net_python_tag(). -//////////////////////////////////////////////////////////////////// -INLINE bool NodePath:: -has_python_tag(const string &key) const { - // An empty NodePath quietly has no tags. This makes has_net_python_tag() - // easier to implement. - if (is_empty()) { - return false; - } - return node()->has_python_tag(key); -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -//////////////////////////////////////////////////////////////////// -// Function: NodePath::clear_python_tag -// Access: Published -// Description: Removes the Python object defined for this key on this -// particular node. After a call to clear_python_tag(), -// has_python_tag() will return false for the indicated -// key. -//////////////////////////////////////////////////////////////////// -INLINE void NodePath:: -clear_python_tag(const string &key) { - nassertv_always(!is_empty()); - node()->clear_python_tag(key); -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -//////////////////////////////////////////////////////////////////// -// Function: NodePath::get_net_python_tag -// Access: Published -// Description: Returns the Python object that has been defined on -// this node, or the nearest ancestor node, for the -// indicated key. If no value has been defined for the -// indicated key on any ancestor node, returns None. -// See also get_python_tag(). -//////////////////////////////////////////////////////////////////// -INLINE PyObject *NodePath:: -get_net_python_tag(const string &key) const { - return find_net_python_tag(key).get_python_tag(key); -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -//////////////////////////////////////////////////////////////////// -// Function: NodePath::has_net_python_tag -// Access: Published -// Description: Returns true if the indicated Python object has been -// defined on this node or on any ancestor node, or -// false otherwise. See also has_python_tag(). -//////////////////////////////////////////////////////////////////// -INLINE bool NodePath:: -has_net_python_tag(const string &key) const { - return !find_net_python_tag(key).is_empty(); -} -#endif // HAVE_PYTHON - //////////////////////////////////////////////////////////////////// // Function: NodePath::list_tags // Access: Published diff --git a/panda/src/pgraph/nodePath.cxx b/panda/src/pgraph/nodePath.cxx index 579d1157f2..216c7deb4b 100644 --- a/panda/src/pgraph/nodePath.cxx +++ b/panda/src/pgraph/nodePath.cxx @@ -177,166 +177,6 @@ NodePath(const NodePath &parent, PandaNode *child_node, _backup_key = 0; } -#ifdef HAVE_PYTHON -//////////////////////////////////////////////////////////////////// -// Function: NodePath::__copy__ -// Access: Published -// Description: A special Python method that is invoked by -// copy.copy(node). Unlike the NodePath copy -// constructor, this makes a duplicate copy of the -// underlying PandaNode (but shares children, instead of -// copying them or omitting them). -//////////////////////////////////////////////////////////////////// -NodePath NodePath:: -__copy__() const { - if (is_empty()) { - // Invoke the copy constructor if we have no node. - return *this; - } - - // If we do have a node, duplicate it, and wrap it in a new - // NodePath. - return NodePath(node()->__copy__()); -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -//////////////////////////////////////////////////////////////////// -// Function: NodePath::__deepcopy__ -// Access: Published -// Description: A special Python method that is invoked by -// copy.deepcopy(np). This calls copy_to() unless the -// NodePath is already present in the provided -// dictionary. -//////////////////////////////////////////////////////////////////// -PyObject *NodePath:: -__deepcopy__(PyObject *self, PyObject *memo) const { - IMPORT_THIS struct Dtool_PyTypedObject Dtool_NodePath; - - // Borrowed reference. - PyObject *dupe = PyDict_GetItem(memo, self); - if (dupe != NULL) { - // Already in the memo dictionary. - Py_INCREF(dupe); - return dupe; - } - - NodePath *np_dupe; - if (is_empty()) { - np_dupe = new NodePath(*this); - } else { - np_dupe = new NodePath(copy_to(NodePath())); - } - - dupe = DTool_CreatePyInstance((void *)np_dupe, Dtool_NodePath, - true, false); - if (PyDict_SetItem(memo, self, dupe) != 0) { - Py_DECREF(dupe); - return NULL; - } - - return dupe; -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -//////////////////////////////////////////////////////////////////// -// Function: NodePath::__reduce__ -// Access: Published -// Description: This special Python method is implement to provide -// support for the pickle module. -// -// This hooks into the native pickle and cPickle -// modules, but it cannot properly handle -// self-referential BAM objects. -//////////////////////////////////////////////////////////////////// -PyObject *NodePath:: -__reduce__(PyObject *self) const { - return __reduce_persist__(self, NULL); -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -//////////////////////////////////////////////////////////////////// -// Function: NodePath::__reduce_persist__ -// Access: Published -// Description: This special Python method is implement to provide -// support for the pickle module. -// -// This is similar to __reduce__, but it provides -// additional support for the missing persistent-state -// object needed to properly support self-referential -// BAM objects written to the pickle stream. This hooks -// into the pickle and cPickle modules implemented in -// direct/src/stdpy. -//////////////////////////////////////////////////////////////////// -PyObject *NodePath:: -__reduce_persist__(PyObject *self, PyObject *pickler) const { - // We should return at least a 2-tuple, (Class, (args)): the - // necessary class object whose constructor we should call - // (e.g. this), and the arguments necessary to reconstruct this - // object. - - BamWriter *writer = NULL; - if (pickler != NULL) { - PyObject *py_writer = PyObject_GetAttrString(pickler, "bamWriter"); - if (py_writer == NULL) { - // It's OK if there's no bamWriter. - PyErr_Clear(); - } else { - DTOOL_Call_ExtractThisPointerForType(py_writer, &Dtool_BamWriter, (void **)&writer); - Py_DECREF(py_writer); - } - } - - // We have a non-empty NodePath. - - string bam_stream; - if (!encode_to_bam_stream(bam_stream, writer)) { - ostringstream stream; - stream << "Could not bamify " << this; - string message = stream.str(); - PyErr_SetString(PyExc_TypeError, message.c_str()); - return NULL; - } - - // Start by getting this class object. - PyObject *this_class = PyObject_Type(self); - if (this_class == NULL) { - return NULL; - } - - PyObject *func; - if (writer != NULL) { - // The modified pickle support: call the "persistent" version of - // this function, which receives the unpickler itself as an - // additional parameter. - func = TypedWritable::find_global_decode(this_class, "py_decode_NodePath_from_bam_stream_persist"); - if (func == NULL) { - PyErr_SetString(PyExc_TypeError, "Couldn't find py_decode_NodePath_from_bam_stream_persist()"); - Py_DECREF(this_class); - return NULL; - } - - } else { - // The traditional pickle support: call the non-persistent version - // of this function. - - func = TypedWritable::find_global_decode(this_class, "py_decode_NodePath_from_bam_stream"); - if (func == NULL) { - PyErr_SetString(PyExc_TypeError, "Couldn't find py_decode_NodePath_from_bam_stream()"); - Py_DECREF(this_class); - return NULL; - } - } - - PyObject *result = Py_BuildValue("(O(s#))", func, bam_stream.data(), bam_stream.size()); - Py_DECREF(func); - Py_DECREF(this_class); - return result; -} -#endif // HAVE_PYTHON - //////////////////////////////////////////////////////////////////// // Function: NodePath::operator bool // Access: Published @@ -6908,27 +6748,6 @@ find_net_tag(const string &key) const { return get_parent().find_net_tag(key); } -#ifdef HAVE_PYTHON -//////////////////////////////////////////////////////////////////// -// Function: NodePath::find_net_python_tag -// Access: Published -// Description: Returns the lowest ancestor of this node that -// contains a tag definition with the indicated key, if -// any, or an empty NodePath if no ancestor of this node -// contains this tag definition. See set_python_tag(). -//////////////////////////////////////////////////////////////////// -NodePath NodePath:: -find_net_python_tag(const string &key) const { - if (is_empty()) { - return NodePath::not_found(); - } - if (has_python_tag(key)) { - return *this; - } - return get_parent().find_net_python_tag(key); -} -#endif // HAVE_PYTHON - //////////////////////////////////////////////////////////////////// // Function: NodePath::write_bam_file // Access: Published @@ -8053,42 +7872,3 @@ r_find_all_materials(PandaNode *node, const RenderState *state, } } -#ifdef HAVE_PYTHON -//////////////////////////////////////////////////////////////////// -// Function: py_decode_NodePath_from_bam_stream -// Access: Published -// Description: This wrapper is defined as a global function to suit -// pickle's needs. -//////////////////////////////////////////////////////////////////// -NodePath -py_decode_NodePath_from_bam_stream(const string &data) { - return py_decode_NodePath_from_bam_stream_persist(NULL, data); -} -#endif // HAVE_PYTHON - - -#ifdef HAVE_PYTHON -//////////////////////////////////////////////////////////////////// -// Function: py_decode_NodePath_from_bam_stream_persist -// Access: Published -// Description: This wrapper is defined as a global function to suit -// pickle's needs. -//////////////////////////////////////////////////////////////////// -NodePath -py_decode_NodePath_from_bam_stream_persist(PyObject *unpickler, const string &data) { - BamReader *reader = NULL; - if (unpickler != NULL) { - PyObject *py_reader = PyObject_GetAttrString(unpickler, "bamReader"); - if (py_reader == NULL) { - // It's OK if there's no bamReader. - PyErr_Clear(); - } else { - DTOOL_Call_ExtractThisPointerForType(py_reader, &Dtool_BamReader, (void **)&reader); - Py_DECREF(py_reader); - } - } - - return NodePath::decode_from_bam_stream(data, reader); -} -#endif // HAVE_PYTHON - diff --git a/panda/src/pgraph/nodePath.h b/panda/src/pgraph/nodePath.h index c12ea43ce5..4bdbba41ff 100644 --- a/panda/src/pgraph/nodePath.h +++ b/panda/src/pgraph/nodePath.h @@ -179,12 +179,10 @@ PUBLISHED: INLINE NodePath(const NodePath ©); INLINE void operator = (const NodePath ©); -#ifdef HAVE_PYTHON - NodePath __copy__() const; - PyObject *__deepcopy__(PyObject *self, PyObject *memo) const; - PyObject *__reduce__(PyObject *self) const; - PyObject *__reduce_persist__(PyObject *self, PyObject *pickler) const; -#endif + EXTENSION(NodePath __copy__() const); + EXTENSION(PyObject *__deepcopy__(PyObject *self, PyObject *memo) const); + EXTENSION(PyObject *__reduce__(PyObject *self) const); + EXTENSION(PyObject *__reduce_persist__(PyObject *self, PyObject *pickler) const); INLINE static NodePath not_found(); INLINE static NodePath removed(); @@ -881,6 +879,8 @@ PUBLISHED: bool calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, Thread *current_thread = Thread::get_current_thread()) const; + EXTENSION(PyObject *get_tight_bounds() const); + // void analyze() const; int flatten_light(); @@ -898,18 +898,16 @@ PUBLISHED: INLINE bool has_net_tag(const string &key) const; NodePath find_net_tag(const string &key) const; -#ifdef HAVE_PYTHON - INLINE PyObject *get_tag_keys() const; - INLINE void set_python_tag(const string &key, PyObject *value); - INLINE PyObject *get_python_tag(const string &key) const; - INLINE void get_python_tag_keys(vector_string &keys) const; - INLINE PyObject *get_python_tag_keys() const; - INLINE bool has_python_tag(const string &key) const; - INLINE void clear_python_tag(const string &key); - INLINE PyObject *get_net_python_tag(const string &key) const; - INLINE bool has_net_python_tag(const string &key) const; - NodePath find_net_python_tag(const string &key) const; -#endif // HAVE_PYTHON + EXTENSION(INLINE PyObject *get_tag_keys() const); + EXTENSION(INLINE void set_python_tag(const string &key, PyObject *value)); + EXTENSION(INLINE PyObject *get_python_tag(const string &key) const); + EXTENSION(INLINE void get_python_tag_keys(vector_string &keys) const); + EXTENSION(INLINE PyObject *get_python_tag_keys() const); + EXTENSION(INLINE bool has_python_tag(const string &key) const); + EXTENSION(INLINE void clear_python_tag(const string &key)); + EXTENSION(INLINE PyObject *get_net_python_tag(const string &key) const); + EXTENSION(INLINE bool has_net_python_tag(const string &key) const); + EXTENSION(NodePath find_net_python_tag(const string &key) const); INLINE void list_tags() const; @@ -1015,13 +1013,6 @@ private: INLINE ostream &operator << (ostream &out, const NodePath &node_path); -#ifdef HAVE_PYTHON -BEGIN_PUBLISH -NodePath py_decode_NodePath_from_bam_stream(const string &data); -NodePath py_decode_NodePath_from_bam_stream_persist(PyObject *unpickler, const string &data); -END_PUBLISH -#endif - #include "nodePath.I" #endif diff --git a/panda/src/pgraph/nodePathCollection.cxx b/panda/src/pgraph/nodePathCollection.cxx index 2cebc6fa44..bd3ddfbcc6 100644 --- a/panda/src/pgraph/nodePathCollection.cxx +++ b/panda/src/pgraph/nodePathCollection.cxx @@ -96,37 +96,6 @@ NodePathCollection(PyObject *self, PyObject *sequence) { } #endif // HAVE_PYTHON -#ifdef HAVE_PYTHON -//////////////////////////////////////////////////////////////////// -// Function: NodePathCollection::__reduce__ -// Access: Published -// Description: This special Python method is implement to provide -// support for the pickle module. -//////////////////////////////////////////////////////////////////// -PyObject *NodePathCollection:: -__reduce__(PyObject *self) const { - // Here we will return a 4-tuple: (Class, (args), None, iterator), - // where iterator is an iterator that will yield successive - // NodePaths. - - // We should return at least a 2-tuple, (Class, (args)): the - // necessary class object whose constructor we should call - // (e.g. this), and the arguments necessary to reconstruct this - // object. - - PyObject *this_class = PyObject_Type(self); - if (this_class == NULL) { - return NULL; - } - - // Since a NodePathCollection is itself an iterator, we can simply - // pass it as the fourth tuple component. - PyObject *result = Py_BuildValue("(O()OO)", this_class, Py_None, self); - Py_DECREF(this_class); - return result; -} -#endif // HAVE_PYTHON - //////////////////////////////////////////////////////////////////// // Function: NodePathCollection::add_path // Access: Published @@ -490,7 +459,7 @@ get_collide_mask() const { // Access: Published // Description: Recursively applies the indicated CollideMask to the // into_collide_masks for all nodes at this level and -// below. Only nodes +// below. // // The default is to change all bits, but if // bits_to_change is not all bits on, then only the bits @@ -506,6 +475,49 @@ set_collide_mask(CollideMask new_mask, CollideMask bits_to_change, } } +//////////////////////////////////////////////////////////////////// +// Function: NodePathCollection::calc_tight_bounds +// Access: Published +// Description: Calculates the minimum and maximum vertices of all +// Geoms at these NodePath's bottom nodes and below +// This is a tight bounding box; it will generally be +// tighter than the bounding volume returned by +// get_bounds() (but it is more expensive to compute). +// +// The return value is true if any points are within the +// bounding volume, or false if none are. +//////////////////////////////////////////////////////////////////// +bool NodePathCollection:: +calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point) const { + bool have_bounds = false; + + for (int i = 0; i < get_num_paths(); i++) { + LPoint3 tmp_min; + LPoint3 tmp_max; + + if (get_path(i).is_empty()) { + continue; + } + + if (get_path(i).calc_tight_bounds(tmp_min, tmp_max)) { + if (!have_bounds) { + min_point = tmp_min; + max_point = tmp_max; + have_bounds = true; + } else { + min_point.set(min(min_point._v(0), tmp_min._v(0)), + min(min_point._v(1), tmp_min._v(1)), + min(min_point._v(2), tmp_min._v(2))); + max_point.set(max(max_point._v(0), tmp_max._v(0)), + max(max_point._v(1), tmp_max._v(1)), + max(max_point._v(2), tmp_max._v(2))); + } + } + } + + return have_bounds; +} + //////////////////////////////////////////////////////////////////// // Function: NodePathCollection::set_texture // Access: Published diff --git a/panda/src/pgraph/nodePathCollection.h b/panda/src/pgraph/nodePathCollection.h index 24cfe3f208..6265f85d16 100644 --- a/panda/src/pgraph/nodePathCollection.h +++ b/panda/src/pgraph/nodePathCollection.h @@ -35,7 +35,7 @@ PUBLISHED: #ifdef HAVE_PYTHON NodePathCollection(PyObject *self, PyObject *sequence); - PyObject *__reduce__(PyObject *self) const; + EXTENSION(PyObject *__reduce__(PyObject *self) const); #endif void add_path(const NodePath &node_path); @@ -77,6 +77,10 @@ PUBLISHED: void set_collide_mask(CollideMask new_mask, CollideMask bits_to_change = CollideMask::all_on(), TypeHandle node_type = TypeHandle::none()); + bool calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point) const; + + EXTENSION(PyObject *get_tight_bounds() const); + void set_texture(Texture *tex, int priority = 0); void set_texture(TextureStage *stage, Texture *tex, int priority = 0); void set_texture_off(int priority = 0); diff --git a/panda/src/putil/Sources.pp b/panda/src/putil/Sources.pp index f20e4ea9e8..6134d73f7c 100644 --- a/panda/src/putil/Sources.pp +++ b/panda/src/putil/Sources.pp @@ -64,7 +64,8 @@ sparseArray.I sparseArray.h \ string_utils.I string_utils.N string_utils.h \ timedCycle.I timedCycle.h typedWritable.I \ - typedWritable.h typedWritableReferenceCount.I \ + typedWritable.h typedWritable_ext.h typedWritable_ext.cxx \ + typedWritableReferenceCount.I \ typedWritableReferenceCount.h updateSeq.I updateSeq.h \ uniqueIdAllocator.h \ vector_typedWritable.h \ @@ -174,7 +175,7 @@ sparseArray.I sparseArray.h \ string_utils.I string_utils.h \ timedCycle.I timedCycle.h typedWritable.I \ - typedWritable.h typedWritableReferenceCount.I \ + typedWritable.h typedWritable_ext.h typedWritableReferenceCount.I \ typedWritableReferenceCount.h updateSeq.I updateSeq.h \ uniqueIdAllocator.h \ vector_typedWritable.h \ diff --git a/panda/src/putil/typedWritable.cxx b/panda/src/putil/typedWritable.cxx index 44c3ffa67b..b829b27126 100644 --- a/panda/src/putil/typedWritable.cxx +++ b/panda/src/putil/typedWritable.cxx @@ -25,13 +25,6 @@ LightMutex TypedWritable::_bam_writers_lock; TypeHandle TypedWritable::_type_handle; TypedWritable* const TypedWritable::Null = (TypedWritable*)0L; -#ifdef HAVE_PYTHON -#include "py_panda.h" -#ifndef CPPPARSER -extern EXPCL_PANDA_PUTIL Dtool_PyTypedObject Dtool_BamWriter; -#endif // CPPPARSER -#endif // HAVE_PYTHON - //////////////////////////////////////////////////////////////////// // Function: TypedWritable::Destructor // Access: Public, Virtual @@ -156,115 +149,6 @@ as_reference_count() { return NULL; } -#ifdef HAVE_PYTHON -//////////////////////////////////////////////////////////////////// -// Function: TypedWritable::__reduce__ -// Access: Published -// Description: This special Python method is implement to provide -// support for the pickle module. -// -// This hooks into the native pickle and cPickle -// modules, but it cannot properly handle -// self-referential BAM objects. -//////////////////////////////////////////////////////////////////// -PyObject *TypedWritable:: -__reduce__(PyObject *self) const { - return __reduce_persist__(self, NULL); -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -//////////////////////////////////////////////////////////////////// -// Function: TypedWritable::__reduce_persist__ -// Access: Published -// Description: This special Python method is implement to provide -// support for the pickle module. -// -// This is similar to __reduce__, but it provides -// additional support for the missing persistent-state -// object needed to properly support self-referential -// BAM objects written to the pickle stream. This hooks -// into the pickle and cPickle modules implemented in -// direct/src/stdpy. -//////////////////////////////////////////////////////////////////// -PyObject *TypedWritable:: -__reduce_persist__(PyObject *self, PyObject *pickler) const { - // We should return at least a 2-tuple, (Class, (args)): the - // necessary class object whose constructor we should call - // (e.g. this), and the arguments necessary to reconstruct this - // object. - - // Check that we have a decode_from_bam_stream python method. If not, - // we can't use this interface. - PyObject *method = PyObject_GetAttrString(self, "decode_from_bam_stream"); - if (method == NULL) { - ostringstream stream; - stream << "Cannot pickle objects of type " << get_type() << "\n"; - string message = stream.str(); - PyErr_SetString(PyExc_TypeError, message.c_str()); - return NULL; - } - Py_DECREF(method); - - BamWriter *writer = NULL; - if (pickler != NULL) { - PyObject *py_writer = PyObject_GetAttrString(pickler, "bamWriter"); - if (py_writer == NULL) { - // It's OK if there's no bamWriter. - PyErr_Clear(); - } else { - DTOOL_Call_ExtractThisPointerForType(py_writer, &Dtool_BamWriter, (void **)&writer); - Py_DECREF(py_writer); - } - } - - // First, streamify the object, if possible. - string bam_stream; - if (!encode_to_bam_stream(bam_stream, writer)) { - ostringstream stream; - stream << "Could not bamify object of type " << get_type() << "\n"; - string message = stream.str(); - PyErr_SetString(PyExc_TypeError, message.c_str()); - return NULL; - } - - // Start by getting this class object. - PyObject *this_class = PyObject_Type(self); - if (this_class == NULL) { - return NULL; - } - - PyObject *func; - if (writer != NULL) { - // The modified pickle support: call the "persistent" version of - // this function, which receives the unpickler itself as an - // additional parameter. - func = find_global_decode(this_class, "py_decode_TypedWritable_from_bam_stream_persist"); - if (func == NULL) { - PyErr_SetString(PyExc_TypeError, "Couldn't find py_decode_TypedWritable_from_bam_stream_persist()"); - Py_DECREF(this_class); - return NULL; - } - - } else { - // The traditional pickle support: call the non-persistent version - // of this function. - - func = find_global_decode(this_class, "py_decode_TypedWritable_from_bam_stream"); - if (func == NULL) { - PyErr_SetString(PyExc_TypeError, "Couldn't find py_decode_TypedWritable_from_bam_stream()"); - Py_DECREF(this_class); - return NULL; - } - } - - PyObject *result = Py_BuildValue("(O(Os#))", func, this_class, bam_stream.data(), bam_stream.size()); - Py_DECREF(func); - Py_DECREF(this_class); - return result; -} -#endif // HAVE_PYTHON - //////////////////////////////////////////////////////////////////// // Function: TypedWritable::encode_to_bam_stream // Access: Published @@ -291,10 +175,10 @@ encode_to_bam_stream(string &data, BamWriter *writer) const { if (!dout.open(stream)) { return false; } - + if (writer == NULL) { // Create our own writer. - + if (!dout.write_header(_bam_header)) { return false; } @@ -303,7 +187,7 @@ encode_to_bam_stream(string &data, BamWriter *writer) const { if (!writer.init()) { return false; } - + if (!writer.write_object(this)) { return false; } @@ -361,12 +245,11 @@ decode_raw_from_bam_stream(TypedWritable *&ptr, ReferenceCount *&ref_ptr, if (reader == NULL) { // Create a local reader. - string head; if (!din.read_header(head, _bam_header.size())) { return false; } - + if (head != _bam_header) { return false; } @@ -375,15 +258,15 @@ decode_raw_from_bam_stream(TypedWritable *&ptr, ReferenceCount *&ref_ptr, if (!reader.init()) { return false; } - + if (!reader.read_object(ptr, ref_ptr)) { return false; } - + if (!reader.resolve()) { return false; } - + if (ref_ptr == NULL) { // Can't support non-reference-counted objects. return false; @@ -400,12 +283,12 @@ decode_raw_from_bam_stream(TypedWritable *&ptr, ReferenceCount *&ref_ptr, reader->set_source(NULL); return false; } - + if (!reader->resolve()) { reader->set_source(NULL); return false; } - + if (ref_ptr == NULL) { // Can't support non-reference-counted objects. reader->set_source(NULL); @@ -425,139 +308,3 @@ decode_raw_from_bam_stream(TypedWritable *&ptr, ReferenceCount *&ref_ptr, ref_ptr->unref(); return true; } - -#ifdef HAVE_PYTHON -//////////////////////////////////////////////////////////////////// -// Function: TypedWritable::find_global_decode -// Access: Public, Static -// Description: This is a support function for __reduce__(). It -// searches for the global function -// py_decode_TypedWritable_from_bam_stream() in this -// class's module, or in the module for any base class. -// (It's really looking for the libpanda module, but we -// can't be sure what name that module was loaded under, -// so we search upwards this way.) -// -// Returns: new reference on success, or NULL on failure. -//////////////////////////////////////////////////////////////////// -PyObject *TypedWritable:: -find_global_decode(PyObject *this_class, const char *func_name) { - PyObject *module_name = PyObject_GetAttrString(this_class, "__module__"); - if (module_name != NULL) { - // borrowed reference - PyObject *sys_modules = PyImport_GetModuleDict(); - if (sys_modules != NULL) { - // borrowed reference - PyObject *module = PyDict_GetItem(sys_modules, module_name); - if (module != NULL){ - PyObject *func = PyObject_GetAttrString(module, (char *)func_name); - if (func != NULL) { - Py_DECREF(module_name); - return func; - } - } - } - } - Py_DECREF(module_name); - - PyObject *bases = PyObject_GetAttrString(this_class, "__bases__"); - if (bases != NULL) { - if (PySequence_Check(bases)) { - Py_ssize_t size = PySequence_Size(bases); - for (Py_ssize_t i = 0; i < size; ++i) { - PyObject *base = PySequence_GetItem(bases, i); - if (base != NULL) { - PyObject *func = find_global_decode(base, func_name); - Py_DECREF(base); - if (func != NULL) { - Py_DECREF(bases); - return func; - } - } - } - } - Py_DECREF(bases); - } - - return NULL; -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -//////////////////////////////////////////////////////////////////// -// Function: py_decode_TypedWritable_from_bam_stream -// Access: Published -// Description: This wrapper is defined as a global function to suit -// pickle's needs. -// -// This hooks into the native pickle and cPickle -// modules, but it cannot properly handle -// self-referential BAM objects. -//////////////////////////////////////////////////////////////////// -PyObject * -py_decode_TypedWritable_from_bam_stream(PyObject *this_class, const string &data) { - return py_decode_TypedWritable_from_bam_stream_persist(NULL, this_class, data); -} -#endif // HAVE_PYTHON - - -#ifdef HAVE_PYTHON -//////////////////////////////////////////////////////////////////// -// Function: py_decode_TypedWritable_from_bam_stream_persist -// Access: Published -// Description: This wrapper is defined as a global function to suit -// pickle's needs. -// -// This is similar to -// py_decode_TypedWritable_from_bam_stream, but it -// provides additional support for the missing -// persistent-state object needed to properly support -// self-referential BAM objects written to the pickle -// stream. This hooks into the pickle and cPickle -// modules implemented in direct/src/stdpy. -//////////////////////////////////////////////////////////////////// -PyObject * -py_decode_TypedWritable_from_bam_stream_persist(PyObject *pickler, PyObject *this_class, const string &data) { - - PyObject *py_reader = NULL; - if (pickler != NULL) { - py_reader = PyObject_GetAttrString(pickler, "bamReader"); - if (py_reader == NULL) { - // It's OK if there's no bamReader. - PyErr_Clear(); - } - } - - // We need the function PandaNode::decode_from_bam_stream or - // TypedWritableReferenceCount::decode_from_bam_stream, which - // invokes the BamReader to reconstruct this object. Since we use - // the specific object's class as the pointer, we get the particular - // instance of decode_from_bam_stream appropriate to this class. - - PyObject *func = PyObject_GetAttrString(this_class, "decode_from_bam_stream"); - if (func == NULL) { - return NULL; - } - - PyObject *result; - if (py_reader != NULL){ - result = PyObject_CallFunction(func, (char *)"(s#O)", data.data(), data.size(), py_reader); - Py_DECREF(py_reader); - } else { - result = PyObject_CallFunction(func, (char *)"(s#)", data.data(), data.size()); - } - - if (result == NULL) { - return NULL; - } - - if (result == Py_None) { - Py_DECREF(result); - PyErr_SetString(PyExc_ValueError, "Could not unpack bam stream"); - return NULL; - } - - return result; -} -#endif // HAVE_PYTHON - diff --git a/panda/src/putil/typedWritable.h b/panda/src/putil/typedWritable.h index df1a4a339f..e8a0dd65d4 100644 --- a/panda/src/putil/typedWritable.h +++ b/panda/src/putil/typedWritable.h @@ -12,8 +12,8 @@ // //////////////////////////////////////////////////////////////////// -#ifndef __TYPED_WRITABLE_ -#define __TYPED_WRITABLE_ +#ifndef TYPEDWRITABLE_H +#define TYPEDWRITABLE_H #include "typedObject.h" #include "vector_typedWritable.h" @@ -31,10 +31,9 @@ class ReferenceCount; // Class : TypedWritable // Description : Base class for objects that can be written to and // read from Bam files. -// +// // See also TypedObject for detailed instructions. //////////////////////////////////////////////////////////////////// - class EXPCL_PANDA_PUTIL TypedWritable : public TypedObject { public: static TypedWritable* const Null; @@ -60,23 +59,16 @@ PUBLISHED: INLINE void mark_bam_modified(); INLINE UpdateSeq get_bam_modified() const; -#ifdef HAVE_PYTHON - PyObject *__reduce__(PyObject *self) const; - PyObject *__reduce_persist__(PyObject *self, PyObject *pickler) const; -#endif + EXTENSION(PyObject *__reduce__(PyObject *self) const); + EXTENSION(PyObject *__reduce_persist__(PyObject *self, PyObject *pickler) const); INLINE string encode_to_bam_stream() const; bool encode_to_bam_stream(string &data, BamWriter *writer = NULL) const; - static bool decode_raw_from_bam_stream(TypedWritable *&ptr, + static bool decode_raw_from_bam_stream(TypedWritable *&ptr, ReferenceCount *&ref_ptr, const string &data, BamReader *reader = NULL); -public: -#ifdef HAVE_PYTHON - static PyObject *find_global_decode(PyObject *this_class, const char *func_name); -#endif - private: // We may need to store a list of the BamWriter(s) that have a // reference to this object, so that we can remove the object from @@ -111,15 +103,6 @@ private: friend class BamWriter; }; -#ifdef HAVE_PYTHON -BEGIN_PUBLISH -PyObject *py_decode_TypedWritable_from_bam_stream(PyObject *this_class, const string &data); -PyObject *py_decode_TypedWritable_from_bam_stream_persist(PyObject *unpickler, PyObject *this_class, const string &data); -END_PUBLISH -#endif - #include "typedWritable.I" #endif - - From 7e455e0fadd04d1fdb5624b380ed3fbefba6aef8 Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 20 Feb 2014 13:58:23 +0000 Subject: [PATCH 19/71] Add files that belonged to the previous commit --- .../src/display/graphicsStateGuardian_ext.cxx | 55 ++++ panda/src/display/graphicsStateGuardian_ext.h | 40 +++ panda/src/egg/eggGroupNode_ext.cxx | 51 ++++ panda/src/egg/eggGroupNode_ext.h | 40 +++ panda/src/pgraph/nodePathCollection_ext.I | 51 ++++ panda/src/pgraph/nodePathCollection_ext.cxx | 48 ++++ panda/src/pgraph/nodePathCollection_ext.h | 44 +++ panda/src/pgraph/nodePath_ext.I | 197 ++++++++++++++ panda/src/pgraph/nodePath_ext.cxx | 232 ++++++++++++++++ panda/src/pgraph/nodePath_ext.h | 63 +++++ panda/src/putil/typedWritable_ext.cxx | 256 ++++++++++++++++++ panda/src/putil/typedWritable_ext.h | 49 ++++ 12 files changed, 1126 insertions(+) create mode 100644 panda/src/display/graphicsStateGuardian_ext.cxx create mode 100644 panda/src/display/graphicsStateGuardian_ext.h create mode 100644 panda/src/egg/eggGroupNode_ext.cxx create mode 100644 panda/src/egg/eggGroupNode_ext.h create mode 100644 panda/src/pgraph/nodePathCollection_ext.I create mode 100644 panda/src/pgraph/nodePathCollection_ext.cxx create mode 100644 panda/src/pgraph/nodePathCollection_ext.h create mode 100644 panda/src/pgraph/nodePath_ext.I create mode 100644 panda/src/pgraph/nodePath_ext.cxx create mode 100644 panda/src/pgraph/nodePath_ext.h create mode 100644 panda/src/putil/typedWritable_ext.cxx create mode 100644 panda/src/putil/typedWritable_ext.h diff --git a/panda/src/display/graphicsStateGuardian_ext.cxx b/panda/src/display/graphicsStateGuardian_ext.cxx new file mode 100644 index 0000000000..2f1c2d916e --- /dev/null +++ b/panda/src/display/graphicsStateGuardian_ext.cxx @@ -0,0 +1,55 @@ +// Filename: graphicsStateGuardian_ext.cxx +// Created by: rdb (10Dec13) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +#include "graphicsStateGuardian_ext.h" +#include "textureContext.h" + +#ifdef HAVE_PYTHON + +#ifndef CPPPARSER +IMPORT_THIS struct Dtool_PyTypedObject Dtool_Texture; +#endif + +static bool traverse_callback(TextureContext *tc, void *data) { + PT(Texture) tex = tc->get_texture(); + PyObject *element = + DTool_CreatePyInstanceTyped(tex, Dtool_Texture, + true, false, tex->get_type_index()); + tex->ref(); + + PyObject *list = (PyObject *) data; + PyList_Append(list, element); + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::get_prepared_textures +// Access: Published +// Description: Returns a Python list of all of the +// currently-prepared textures within the GSG. +//////////////////////////////////////////////////////////////////// +PyObject *Extension:: +get_prepared_textures() const { + PyObject *list = PyList_New(0); + + if (list == NULL) { + return NULL; + } + + _this->traverse_prepared_textures(&traverse_callback, (void *)list); + return list; +} + +#endif diff --git a/panda/src/display/graphicsStateGuardian_ext.h b/panda/src/display/graphicsStateGuardian_ext.h new file mode 100644 index 0000000000..cc1d9a3fe5 --- /dev/null +++ b/panda/src/display/graphicsStateGuardian_ext.h @@ -0,0 +1,40 @@ +// Filename: graphicsStateGuardian_ext.h +// Created by: rdb (10Dec13) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +#ifndef GRAPHICSSTATEGUARDIAN_EXT_H +#define GRAPHICSSTATEGUARDIAN_EXT_H + +#include "dtoolbase.h" + +#ifdef HAVE_PYTHON + +#include "extension.h" +#include "graphicsStateGuardian.h" +#include "py_panda.h" + +//////////////////////////////////////////////////////////////////// +// Class : Extension +// Description : This class defines the extension methods for +// Ramfile, which are called instead of +// any C++ methods with the same prototype. +//////////////////////////////////////////////////////////////////// +template<> +class Extension : public ExtensionBase { +public: + PyObject *get_prepared_textures() const; +}; + +#endif // HAVE_PYTHON + +#endif // GRAPHICSSTATEGUARDIAN_EXT_H diff --git a/panda/src/egg/eggGroupNode_ext.cxx b/panda/src/egg/eggGroupNode_ext.cxx new file mode 100644 index 0000000000..457ea73ba3 --- /dev/null +++ b/panda/src/egg/eggGroupNode_ext.cxx @@ -0,0 +1,51 @@ +// Filename: eggGroupNode_ext.cxx +// Created by: rdb (09Dec13) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +#include "eggGroupNode_ext.h" + +#ifdef HAVE_PYTHON + +IMPORT_THIS struct Dtool_PyTypedObject Dtool_EggNode; + +//////////////////////////////////////////////////////////////////// +// Function: EggGroupNode::get_children +// Access: Published +// Description: Returns a Python list containing the node's children. +//////////////////////////////////////////////////////////////////// +PyObject *Extension:: +get_children() const { + EggGroupNode::iterator it; + + // Create the Python list object. + EggGroupNode::size_type len = _this->size(); + PyObject *lst = PyList_New(len); + if (lst == NULL) { + return NULL; + } + + // Fill in the list. + int i = 0; + for (it = _this->begin(); it != _this->end() && i < len; ++it) { + EggNode *node = *it; + node->ref(); + PyObject *item = + DTool_CreatePyInstanceTyped((void *)node, Dtool_EggNode, true, false, node->get_type_index()); + + PyList_SET_ITEM(lst, i++, item); + } + + return lst; +} + +#endif // HAVE_PYTHON diff --git a/panda/src/egg/eggGroupNode_ext.h b/panda/src/egg/eggGroupNode_ext.h new file mode 100644 index 0000000000..ae29cc54b4 --- /dev/null +++ b/panda/src/egg/eggGroupNode_ext.h @@ -0,0 +1,40 @@ +// Filename: eggGroupNode_ext.h +// Created by: rdb (09Dec13) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGGROUPNODE_EXT_H +#define EGGGROUPNODE_EXT_H + +#include "dtoolbase.h" + +#ifdef HAVE_PYTHON + +#include "extension.h" +#include "eggGroupNode.h" +#include "py_panda.h" + +//////////////////////////////////////////////////////////////////// +// Class : Extension +// Description : This class defines the extension methods for +// EggGroupNode, which are called instead of +// any C++ methods with the same prototype. +//////////////////////////////////////////////////////////////////// +template<> +class Extension : public ExtensionBase { +public: + PyObject *get_children() const; +}; + +#endif // HAVE_PYTHON + +#endif // EGGGROUPNODE_EXT_H diff --git a/panda/src/pgraph/nodePathCollection_ext.I b/panda/src/pgraph/nodePathCollection_ext.I new file mode 100644 index 0000000000..d88ba08295 --- /dev/null +++ b/panda/src/pgraph/nodePathCollection_ext.I @@ -0,0 +1,51 @@ +// Filename: nodePathCollection_ext.I +// Created by: rdb (09Dec13) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPPARSER +#ifdef STDFLOAT_DOUBLE +IMPORT_THIS struct Dtool_PyTypedObject Dtool_LPoint3d; +#else +IMPORT_THIS struct Dtool_PyTypedObject Dtool_LPoint3f; +#endif +#endif + +//////////////////////////////////////////////////////////////////// +// Function: Extension::get_tight_bounds +// Access: Published +// Description: Returns the tight bounds as a 2-tuple of LPoint3 +// objects. This is a convenience function for Python +// users, among which the use of calc_tight_bounds +// may be confusing. +// Returns None if calc_tight_bounds returned false. +//////////////////////////////////////////////////////////////////// +INLINE PyObject *Extension:: +get_tight_bounds() const { + LPoint3 *min_point = new LPoint3; + LPoint3 *max_point = new LPoint3; + + if (_this->calc_tight_bounds(*min_point, *max_point)) { +#ifdef STDFLOAT_DOUBLE + PyObject *min_inst = DTool_CreatePyInstance((void*) min_point, Dtool_LPoint3d, true, false); + PyObject *max_inst = DTool_CreatePyInstance((void*) max_point, Dtool_LPoint3d, true, false); +#else + PyObject *min_inst = DTool_CreatePyInstance((void*) min_point, Dtool_LPoint3f, true, false); + PyObject *max_inst = DTool_CreatePyInstance((void*) max_point, Dtool_LPoint3f, true, false); +#endif + return Py_BuildValue("NN", min_inst, max_inst); + + } else { + Py_INCREF(Py_None); + return Py_None; + } +} diff --git a/panda/src/pgraph/nodePathCollection_ext.cxx b/panda/src/pgraph/nodePathCollection_ext.cxx new file mode 100644 index 0000000000..324e746d47 --- /dev/null +++ b/panda/src/pgraph/nodePathCollection_ext.cxx @@ -0,0 +1,48 @@ +// Filename: nodePathCollection_ext.cxx +// Created by: rdb (09Dec13) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +#include "nodePathCollection_ext.h" + +#ifdef HAVE_PYTHON + +//////////////////////////////////////////////////////////////////// +// Function: NodePathCollection::__reduce__ +// Access: Published +// Description: This special Python method is implement to provide +// support for the pickle module. +//////////////////////////////////////////////////////////////////// +PyObject *Extension:: +__reduce__(PyObject *self) const { + // Here we will return a 4-tuple: (Class, (args), None, iterator), + // where iterator is an iterator that will yield successive + // NodePaths. + + // We should return at least a 2-tuple, (Class, (args)): the + // necessary class object whose constructor we should call + // (e.g. this), and the arguments necessary to reconstruct this + // object. + + PyObject *this_class = PyObject_Type(self); + if (this_class == NULL) { + return NULL; + } + + // Since a NodePathCollection is itself an iterator, we can simply + // pass it as the fourth tuple component. + PyObject *result = Py_BuildValue("(O()OO)", this_class, Py_None, self); + Py_DECREF(this_class); + return result; +} + +#endif diff --git a/panda/src/pgraph/nodePathCollection_ext.h b/panda/src/pgraph/nodePathCollection_ext.h new file mode 100644 index 0000000000..96dc6f9e19 --- /dev/null +++ b/panda/src/pgraph/nodePathCollection_ext.h @@ -0,0 +1,44 @@ +// Filename: nodePathCollection_ext.h +// Created by: rdb (09Dec13) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +#ifndef NODEPATHCOLLECTION_EXT_H +#define NODEPATHCOLLECTION_EXT_H + +#include "dtoolbase.h" + +#ifdef HAVE_PYTHON + +#include "extension.h" +#include "nodePathCollection.h" +#include "py_panda.h" + +//////////////////////////////////////////////////////////////////// +// Class : Extension +// Description : This class defines the extension methods for +// NodePathCollection, which are called instead of +// any C++ methods with the same prototype. +//////////////////////////////////////////////////////////////////// +template<> +class Extension : public ExtensionBase { +public: + PyObject *__reduce__(PyObject *self) const; + + INLINE PyObject *get_tight_bounds() const; +}; + +#include "nodePathCollection_ext.I" + +#endif // HAVE_PYTHON + +#endif // NODEPATHCOLLECTION_EXT_H diff --git a/panda/src/pgraph/nodePath_ext.I b/panda/src/pgraph/nodePath_ext.I new file mode 100644 index 0000000000..a97a29fb59 --- /dev/null +++ b/panda/src/pgraph/nodePath_ext.I @@ -0,0 +1,197 @@ +// Filename: nodePath_ext.I +// Created by: rdb (09Dec13) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPPARSER +#ifdef STDFLOAT_DOUBLE +IMPORT_THIS struct Dtool_PyTypedObject Dtool_LPoint3d; +#else +IMPORT_THIS struct Dtool_PyTypedObject Dtool_LPoint3f; +#endif +#endif + +//////////////////////////////////////////////////////////////////// +// Function: Extension::get_python_tag_keys +// Access: Published +// Description: Fills the given vector up with the +// list of Python tags on this PandaNode. +// +// It is the user's responsibility to ensure that the +// keys vector is empty before making this call; +// otherwise, the new files will be appended to it. +//////////////////////////////////////////////////////////////////// +INLINE void Extension:: +get_python_tag_keys(vector_string &keys) const { + nassertv_always(!_this->is_empty()); + _this->node()->get_python_tag_keys(keys); +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::get_tag_keys +// Access: Published +// Description: This variant on get_tag_keys returns a Python list +// of strings. Returns None if the NodePath is empty. +//////////////////////////////////////////////////////////////////// +INLINE PyObject *Extension:: +get_tag_keys() const { + // An empty NodePath returns None + if (_this->is_empty()) { + Py_INCREF(Py_None); + return Py_None; + } + return _this->node()->get_tag_keys(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::get_python_tag_keys +// Access: Published +// Description: This variant on get_python_tag_keys returns a +// Python list of strings. +// Returns None if the NodePath is empty. +//////////////////////////////////////////////////////////////////// +INLINE PyObject *Extension:: +get_python_tag_keys() const { + // An empty NodePath returns None + if (_this->is_empty()) { + Py_INCREF(Py_None); + return Py_None; + } + return _this->node()->get_python_tag_keys(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Extension::set_python_tag +// Access: Published +// Description: Associates an arbitrary Python object with a +// user-defined key which is stored on the node. This +// object has no meaning to Panda; but it is stored +// indefinitely on the node until it is requested again. +// +// Each unique key stores a different Python object. +// There is no effective limit on the number of +// different keys that may be stored or on the nature of +// any one key's object. +//////////////////////////////////////////////////////////////////// +INLINE void Extension:: +set_python_tag(const string &key, PyObject *value) { + nassertv_always(!_this->is_empty()); + _this->node()->set_python_tag(key, value); +} + +//////////////////////////////////////////////////////////////////// +// Function: Extension::get_python_tag +// Access: Published +// Description: Retrieves the Python object that was previously +// set on this node for the particular key, if any. If +// no object has been previously set, returns None. +// See also get_net_python_tag(). +//////////////////////////////////////////////////////////////////// +INLINE PyObject *Extension:: +get_python_tag(const string &key) const { + // An empty NodePath quietly returns no tags. This makes + // get_net_python_tag() easier to implement. + if (_this->is_empty()) { + Py_INCREF(Py_None); + return Py_None; + } + return _this->node()->get_python_tag(key); +} + +//////////////////////////////////////////////////////////////////// +// Function: Extension::has_python_tag +// Access: Published +// Description: Returns true if a Python object has been defined on +// this node for the particular key (even if that value +// is the empty string), or false if no value has been +// set. See also has_net_python_tag(). +//////////////////////////////////////////////////////////////////// +INLINE bool Extension:: +has_python_tag(const string &key) const { + // An empty NodePath quietly has no tags. This makes has_net_python_tag() + // easier to implement. + if (_this->is_empty()) { + return false; + } + return _this->node()->has_python_tag(key); +} + +//////////////////////////////////////////////////////////////////// +// Function: Extension::clear_python_tag +// Access: Published +// Description: Removes the Python object defined for this key on this +// particular node. After a call to clear_python_tag(), +// has_python_tag() will return false for the indicated +// key. +//////////////////////////////////////////////////////////////////// +INLINE void Extension:: +clear_python_tag(const string &key) { + nassertv_always(!_this->is_empty()); + _this->node()->clear_python_tag(key); +} + +//////////////////////////////////////////////////////////////////// +// Function: Extension::get_net_python_tag +// Access: Published +// Description: Returns the Python object that has been defined on +// this node, or the nearest ancestor node, for the +// indicated key. If no value has been defined for the +// indicated key on any ancestor node, returns None. +// See also get_python_tag(). +//////////////////////////////////////////////////////////////////// +INLINE PyObject *Extension:: +get_net_python_tag(const string &key) const { + NodePath tag_np = find_net_python_tag(key); + return invoke_extension(&tag_np).get_python_tag(key); +} + +//////////////////////////////////////////////////////////////////// +// Function: Extension::has_net_python_tag +// Access: Published +// Description: Returns true if the indicated Python object has been +// defined on this node or on any ancestor node, or +// false otherwise. See also has_python_tag(). +//////////////////////////////////////////////////////////////////// +INLINE bool Extension:: +has_net_python_tag(const string &key) const { + return !find_net_python_tag(key).is_empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Extension::get_tight_bounds +// Access: Published +// Description: Returns the tight bounds as a 2-tuple of LPoint3 +// objects. This is a convenience function for Python +// users, among which the use of calc_tight_bounds +// may be confusing. +// Returns None if calc_tight_bounds returned false. +//////////////////////////////////////////////////////////////////// +INLINE PyObject *Extension:: +get_tight_bounds() const { + LPoint3 *min_point = new LPoint3; + LPoint3 *max_point = new LPoint3; + + if (_this->calc_tight_bounds(*min_point, *max_point)) { +#ifdef STDFLOAT_DOUBLE + PyObject *min_inst = DTool_CreatePyInstance((void*) min_point, Dtool_LPoint3d, true, false); + PyObject *max_inst = DTool_CreatePyInstance((void*) max_point, Dtool_LPoint3d, true, false); +#else + PyObject *min_inst = DTool_CreatePyInstance((void*) min_point, Dtool_LPoint3f, true, false); + PyObject *max_inst = DTool_CreatePyInstance((void*) max_point, Dtool_LPoint3f, true, false); +#endif + return Py_BuildValue("NN", min_inst, max_inst); + + } else { + Py_INCREF(Py_None); + return Py_None; + } +} diff --git a/panda/src/pgraph/nodePath_ext.cxx b/panda/src/pgraph/nodePath_ext.cxx new file mode 100644 index 0000000000..da66754c42 --- /dev/null +++ b/panda/src/pgraph/nodePath_ext.cxx @@ -0,0 +1,232 @@ +// Filename: nodePath_ext.cxx +// Created by: rdb (09Dec13) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +#include "nodePath_ext.h" +#include "typedWritable_ext.h" + +#ifdef HAVE_PYTHON + +#ifndef CPPPARSER +extern EXPCL_PANDA_PUTIL Dtool_PyTypedObject Dtool_BamWriter; +extern EXPCL_PANDA_PUTIL Dtool_PyTypedObject Dtool_BamReader; +#endif // CPPPARSER + +//////////////////////////////////////////////////////////////////// +// Function: Extension::__copy__ +// Access: Published +// Description: A special Python method that is invoked by +// copy.copy(node). Unlike the NodePath copy +// constructor, this makes a duplicate copy of the +// underlying PandaNode (but shares children, instead of +// copying them or omitting them). +//////////////////////////////////////////////////////////////////// +NodePath Extension:: +__copy__() const { + if (_this->is_empty()) { + // Invoke the copy constructor if we have no node. + return *_this; + } + + // If we do have a node, duplicate it, and wrap it in a new + // NodePath. + return NodePath(_this->node()->__copy__()); +} + +//////////////////////////////////////////////////////////////////// +// Function: Extension::__deepcopy__ +// Access: Published +// Description: A special Python method that is invoked by +// copy.deepcopy(np). This calls copy_to() unless the +// NodePath is already present in the provided +// dictionary. +//////////////////////////////////////////////////////////////////// +PyObject *Extension:: +__deepcopy__(PyObject *self, PyObject *memo) const { + IMPORT_THIS struct Dtool_PyTypedObject Dtool_NodePath; + + // Borrowed reference. + PyObject *dupe = PyDict_GetItem(memo, self); + if (dupe != NULL) { + // Already in the memo dictionary. + Py_INCREF(dupe); + return dupe; + } + + NodePath *np_dupe; + if (_this->is_empty()) { + np_dupe = new NodePath(*_this); + } else { + np_dupe = new NodePath(_this->copy_to(NodePath())); + } + + dupe = DTool_CreatePyInstance((void *)np_dupe, Dtool_NodePath, + true, false); + if (PyDict_SetItem(memo, self, dupe) != 0) { + Py_DECREF(dupe); + return NULL; + } + + return dupe; +} + +//////////////////////////////////////////////////////////////////// +// Function: Extension::__reduce__ +// Access: Published +// Description: This special Python method is implement to provide +// support for the pickle module. +// +// This hooks into the native pickle and cPickle +// modules, but it cannot properly handle +// self-referential BAM objects. +//////////////////////////////////////////////////////////////////// +PyObject *Extension:: +__reduce__(PyObject *self) const { + return __reduce_persist__(self, NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: Extension::__reduce_persist__ +// Access: Published +// Description: This special Python method is implement to provide +// support for the pickle module. +// +// This is similar to __reduce__, but it provides +// additional support for the missing persistent-state +// object needed to properly support self-referential +// BAM objects written to the pickle stream. This hooks +// into the pickle and cPickle modules implemented in +// direct/src/stdpy. +//////////////////////////////////////////////////////////////////// +PyObject *Extension:: +__reduce_persist__(PyObject *self, PyObject *pickler) const { + // We should return at least a 2-tuple, (Class, (args)): the + // necessary class object whose constructor we should call + // (e.g. this), and the arguments necessary to reconstruct this + // object. + + BamWriter *writer = NULL; + if (pickler != NULL) { + PyObject *py_writer = PyObject_GetAttrString(pickler, "bamWriter"); + if (py_writer == NULL) { + // It's OK if there's no bamWriter. + PyErr_Clear(); + } else { + DTOOL_Call_ExtractThisPointerForType(py_writer, &Dtool_BamWriter, (void **)&writer); + Py_DECREF(py_writer); + } + } + + // We have a non-empty NodePath. + + string bam_stream; + if (!_this->encode_to_bam_stream(bam_stream, writer)) { + ostringstream stream; + stream << "Could not bamify " << _this; + string message = stream.str(); + PyErr_SetString(PyExc_TypeError, message.c_str()); + return NULL; + } + + // Start by getting this class object. + PyObject *this_class = PyObject_Type(self); + if (this_class == NULL) { + return NULL; + } + + PyObject *func; + if (writer != NULL) { + // The modified pickle support: call the "persistent" version of + // this function, which receives the unpickler itself as an + // additional parameter. + func = Extension::find_global_decode(this_class, "py_decode_NodePath_from_bam_stream_persist"); + if (func == NULL) { + PyErr_SetString(PyExc_TypeError, "Couldn't find py_decode_NodePath_from_bam_stream_persist()"); + Py_DECREF(this_class); + return NULL; + } + + } else { + // The traditional pickle support: call the non-persistent version + // of this function. + + func = Extension::find_global_decode(this_class, "py_decode_NodePath_from_bam_stream"); + if (func == NULL) { + PyErr_SetString(PyExc_TypeError, "Couldn't find py_decode_NodePath_from_bam_stream()"); + Py_DECREF(this_class); + return NULL; + } + } + + PyObject *result = Py_BuildValue("(O(s#))", func, bam_stream.data(), bam_stream.size()); + Py_DECREF(func); + Py_DECREF(this_class); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: Extension::find_net_python_tag +// Access: Published +// Description: Returns the lowest ancestor of this node that +// contains a tag definition with the indicated key, if +// any, or an empty NodePath if no ancestor of this node +// contains this tag definition. See set_python_tag(). +//////////////////////////////////////////////////////////////////// +NodePath Extension:: +find_net_python_tag(const string &key) const { + if (_this->is_empty()) { + return NodePath::not_found(); + } + if (has_python_tag(key)) { + return *_this; + } + NodePath parent = _this->get_parent(); + return invoke_extension(&parent).find_net_python_tag(key); +} + +//////////////////////////////////////////////////////////////////// +// Function: py_decode_NodePath_from_bam_stream +// Access: Published +// Description: This wrapper is defined as a global function to suit +// pickle's needs. +//////////////////////////////////////////////////////////////////// +NodePath +py_decode_NodePath_from_bam_stream(const string &data) { + return py_decode_NodePath_from_bam_stream_persist(NULL, data); +} + +//////////////////////////////////////////////////////////////////// +// Function: py_decode_NodePath_from_bam_stream_persist +// Access: Published +// Description: This wrapper is defined as a global function to suit +// pickle's needs. +//////////////////////////////////////////////////////////////////// +NodePath +py_decode_NodePath_from_bam_stream_persist(PyObject *unpickler, const string &data) { + BamReader *reader = NULL; + if (unpickler != NULL) { + PyObject *py_reader = PyObject_GetAttrString(unpickler, "bamReader"); + if (py_reader == NULL) { + // It's OK if there's no bamReader. + PyErr_Clear(); + } else { + DTOOL_Call_ExtractThisPointerForType(py_reader, &Dtool_BamReader, (void **)&reader); + Py_DECREF(py_reader); + } + } + + return NodePath::decode_from_bam_stream(data, reader); +} + +#endif // HAVE_PYTHON + diff --git a/panda/src/pgraph/nodePath_ext.h b/panda/src/pgraph/nodePath_ext.h new file mode 100644 index 0000000000..c91a462bff --- /dev/null +++ b/panda/src/pgraph/nodePath_ext.h @@ -0,0 +1,63 @@ +// Filename: nodePath_ext.h +// Created by: rdb (09Dec13) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +#ifndef NODEPATH_EXT_H +#define NODEPATH_EXT_H + +#include "dtoolbase.h" + +#ifdef HAVE_PYTHON + +#include "extension.h" +#include "nodePath.h" +#include "py_panda.h" + +//////////////////////////////////////////////////////////////////// +// Class : Extension +// Description : This class defines the extension methods for +// NodePath, which are called instead of +// any C++ methods with the same prototype. +//////////////////////////////////////////////////////////////////// +template<> +class Extension : public ExtensionBase { +public: + NodePath __copy__() const; + PyObject *__deepcopy__(PyObject *self, PyObject *memo) const; + PyObject *__reduce__(PyObject *self) const; + PyObject *__reduce_persist__(PyObject *self, PyObject *pickler) const; + + INLINE PyObject *get_tag_keys() const; + INLINE void set_python_tag(const string &key, PyObject *value); + INLINE PyObject *get_python_tag(const string &key) const; + INLINE void get_python_tag_keys(vector_string &keys) const; + INLINE PyObject *get_python_tag_keys() const; + INLINE bool has_python_tag(const string &key) const; + INLINE void clear_python_tag(const string &key); + INLINE PyObject *get_net_python_tag(const string &key) const; + INLINE bool has_net_python_tag(const string &key) const; + NodePath find_net_python_tag(const string &key) const; + + INLINE PyObject *get_tight_bounds() const; +}; + +BEGIN_PUBLISH +NodePath py_decode_NodePath_from_bam_stream(const string &data); +NodePath py_decode_NodePath_from_bam_stream_persist(PyObject *unpickler, const string &data); +END_PUBLISH + +#include "nodePath_ext.I" + +#endif // HAVE_PYTHON + +#endif // NODEPATH_EXT_H diff --git a/panda/src/putil/typedWritable_ext.cxx b/panda/src/putil/typedWritable_ext.cxx new file mode 100644 index 0000000000..ebe5f966d5 --- /dev/null +++ b/panda/src/putil/typedWritable_ext.cxx @@ -0,0 +1,256 @@ +// Filename: typedWritable_ext.cxx +// Created by: rdb (10Dec13) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +#include "typedWritable_ext.h" + +#ifdef HAVE_PYTHON + +#ifndef CPPPARSER +extern EXPCL_PANDA_PUTIL Dtool_PyTypedObject Dtool_BamWriter; +#endif // CPPPARSER + +//////////////////////////////////////////////////////////////////// +// Function: TypedWritable::__reduce__ +// Access: Published +// Description: This special Python method is implement to provide +// support for the pickle module. +// +// This hooks into the native pickle and cPickle +// modules, but it cannot properly handle +// self-referential BAM objects. +//////////////////////////////////////////////////////////////////// +PyObject *Extension:: +__reduce__(PyObject *self) const { + return __reduce_persist__(self, NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: TypedWritable::__reduce_persist__ +// Access: Published +// Description: This special Python method is implement to provide +// support for the pickle module. +// +// This is similar to __reduce__, but it provides +// additional support for the missing persistent-state +// object needed to properly support self-referential +// BAM objects written to the pickle stream. This hooks +// into the pickle and cPickle modules implemented in +// direct/src/stdpy. +//////////////////////////////////////////////////////////////////// +PyObject *Extension:: +__reduce_persist__(PyObject *self, PyObject *pickler) const { + // We should return at least a 2-tuple, (Class, (args)): the + // necessary class object whose constructor we should call + // (e.g. this), and the arguments necessary to reconstruct this + // object. + + // Check that we have a decode_from_bam_stream python method. If not, + // we can't use this interface. + PyObject *method = PyObject_GetAttrString(self, "decode_from_bam_stream"); + if (method == NULL) { + ostringstream stream; + stream << "Cannot pickle objects of type " << _this->get_type() << "\n"; + string message = stream.str(); + PyErr_SetString(PyExc_TypeError, message.c_str()); + return NULL; + } + Py_DECREF(method); + + BamWriter *writer = NULL; + if (pickler != NULL) { + PyObject *py_writer = PyObject_GetAttrString(pickler, "bamWriter"); + if (py_writer == NULL) { + // It's OK if there's no bamWriter. + PyErr_Clear(); + } else { + DTOOL_Call_ExtractThisPointerForType(py_writer, &Dtool_BamWriter, (void **)&writer); + Py_DECREF(py_writer); + } + } + + // First, streamify the object, if possible. + string bam_stream; + if (!_this->encode_to_bam_stream(bam_stream, writer)) { + ostringstream stream; + stream << "Could not bamify object of type " << _this->get_type() << "\n"; + string message = stream.str(); + PyErr_SetString(PyExc_TypeError, message.c_str()); + return NULL; + } + + // Start by getting this class object. + PyObject *this_class = PyObject_Type(self); + if (this_class == NULL) { + return NULL; + } + + PyObject *func; + if (writer != NULL) { + // The modified pickle support: call the "persistent" version of + // this function, which receives the unpickler itself as an + // additional parameter. + func = find_global_decode(this_class, "py_decode_TypedWritable_from_bam_stream_persist"); + if (func == NULL) { + PyErr_SetString(PyExc_TypeError, "Couldn't find py_decode_TypedWritable_from_bam_stream_persist()"); + Py_DECREF(this_class); + return NULL; + } + + } else { + // The traditional pickle support: call the non-persistent version + // of this function. + + func = find_global_decode(this_class, "py_decode_TypedWritable_from_bam_stream"); + if (func == NULL) { + PyErr_SetString(PyExc_TypeError, "Couldn't find py_decode_TypedWritable_from_bam_stream()"); + Py_DECREF(this_class); + return NULL; + } + } + + PyObject *result = Py_BuildValue("(O(Os#))", func, this_class, bam_stream.data(), bam_stream.size()); + Py_DECREF(func); + Py_DECREF(this_class); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: TypedWritable::find_global_decode +// Access: Public, Static +// Description: This is a support function for __reduce__(). It +// searches for the global function +// py_decode_TypedWritable_from_bam_stream() in this +// class's module, or in the module for any base class. +// (It's really looking for the libpanda module, but we +// can't be sure what name that module was loaded under, +// so we search upwards this way.) +// +// Returns: new reference on success, or NULL on failure. +//////////////////////////////////////////////////////////////////// +PyObject *Extension:: +find_global_decode(PyObject *this_class, const char *func_name) { + PyObject *module_name = PyObject_GetAttrString(this_class, "__module__"); + if (module_name != NULL) { + // borrowed reference + PyObject *sys_modules = PyImport_GetModuleDict(); + if (sys_modules != NULL) { + // borrowed reference + PyObject *module = PyDict_GetItem(sys_modules, module_name); + if (module != NULL) { + PyObject *func = PyObject_GetAttrString(module, (char *)func_name); + if (func != NULL) { + Py_DECREF(module_name); + return func; + } + } + } + } + Py_DECREF(module_name); + + PyObject *bases = PyObject_GetAttrString(this_class, "__bases__"); + if (bases != NULL) { + if (PySequence_Check(bases)) { + Py_ssize_t size = PySequence_Size(bases); + for (Py_ssize_t i = 0; i < size; ++i) { + PyObject *base = PySequence_GetItem(bases, i); + if (base != NULL) { + PyObject *func = find_global_decode(base, func_name); + Py_DECREF(base); + if (func != NULL) { + Py_DECREF(bases); + return func; + } + } + } + } + Py_DECREF(bases); + } + + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: py_decode_TypedWritable_from_bam_stream +// Access: Published +// Description: This wrapper is defined as a global function to suit +// pickle's needs. +// +// This hooks into the native pickle and cPickle +// modules, but it cannot properly handle +// self-referential BAM objects. +//////////////////////////////////////////////////////////////////// +PyObject * +py_decode_TypedWritable_from_bam_stream(PyObject *this_class, const string &data) { + return py_decode_TypedWritable_from_bam_stream_persist(NULL, this_class, data); +} + +//////////////////////////////////////////////////////////////////// +// Function: py_decode_TypedWritable_from_bam_stream_persist +// Access: Published +// Description: This wrapper is defined as a global function to suit +// pickle's needs. +// +// This is similar to +// py_decode_TypedWritable_from_bam_stream, but it +// provides additional support for the missing +// persistent-state object needed to properly support +// self-referential BAM objects written to the pickle +// stream. This hooks into the pickle and cPickle +// modules implemented in direct/src/stdpy. +//////////////////////////////////////////////////////////////////// +PyObject * +py_decode_TypedWritable_from_bam_stream_persist(PyObject *pickler, PyObject *this_class, const string &data) { + + PyObject *py_reader = NULL; + if (pickler != NULL) { + py_reader = PyObject_GetAttrString(pickler, "bamReader"); + if (py_reader == NULL) { + // It's OK if there's no bamReader. + PyErr_Clear(); + } + } + + // We need the function PandaNode::decode_from_bam_stream or + // TypedWritableReferenceCount::decode_from_bam_stream, which + // invokes the BamReader to reconstruct this object. Since we use + // the specific object's class as the pointer, we get the particular + // instance of decode_from_bam_stream appropriate to this class. + + PyObject *func = PyObject_GetAttrString(this_class, "decode_from_bam_stream"); + if (func == NULL) { + return NULL; + } + + PyObject *result; + if (py_reader != NULL){ + result = PyObject_CallFunction(func, (char *)"(s#O)", data.data(), data.size(), py_reader); + Py_DECREF(py_reader); + } else { + result = PyObject_CallFunction(func, (char *)"(s#)", data.data(), data.size()); + } + + if (result == NULL) { + return NULL; + } + + if (result == Py_None) { + Py_DECREF(result); + PyErr_SetString(PyExc_ValueError, "Could not unpack bam stream"); + return NULL; + } + + return result; +} + +#endif diff --git a/panda/src/putil/typedWritable_ext.h b/panda/src/putil/typedWritable_ext.h new file mode 100644 index 0000000000..b42092ab33 --- /dev/null +++ b/panda/src/putil/typedWritable_ext.h @@ -0,0 +1,49 @@ +// Filename: typedWritable_ext.h +// Created by: rdb (10Dec13) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +#ifndef TYPEDWRITABLE_EXT_H +#define TYPEDWRITABLE_EXT_H + +#include "dtoolbase.h" + +#ifdef HAVE_PYTHON + +#include "extension.h" +#include "typedWritable.h" +#include "py_panda.h" + +//////////////////////////////////////////////////////////////////// +// Class : Extension +// Description : This class defines the extension methods for +// StreamReader, which are called instead of +// any C++ methods with the same prototype. +//////////////////////////////////////////////////////////////////// +template<> +class Extension : public ExtensionBase { +public: + PyObject *__reduce__(PyObject *self) const; + PyObject *__reduce_persist__(PyObject *self, PyObject *pickler) const; + + static PyObject *find_global_decode(PyObject *this_class, const char *func_name); + +}; + +BEGIN_PUBLISH +PyObject *py_decode_TypedWritable_from_bam_stream(PyObject *this_class, const string &data); +PyObject *py_decode_TypedWritable_from_bam_stream_persist(PyObject *unpickler, PyObject *this_class, const string &data); +END_PUBLISH + +#endif // HAVE_PYTHON + +#endif // TYPEDWRITABLE_EXT_H From abbbb1d09fd097e62b0aef2aebfb9e03aaff6060 Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 20 Feb 2014 18:05:56 +0000 Subject: [PATCH 20/71] Let the installer copy the new 'panda3d' tree instead of the panda3d.py file. --- direct/src/directscripts/packpanda.nsi | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/direct/src/directscripts/packpanda.nsi b/direct/src/directscripts/packpanda.nsi index dad5e4beb5..a06ef84ce8 100755 --- a/direct/src/directscripts/packpanda.nsi +++ b/direct/src/directscripts/packpanda.nsi @@ -103,8 +103,6 @@ Section "${SMDIRECTORY}" SecCore File /r /x CVS /x Opt?-Win32 "${PSOURCE}\direct\filter\*.sha" SetOutPath $INSTDIR\direct File /r /x CVS /x Opt?-Win32 "${PSOURCE}\direct\*.py" - SetOutPath $INSTDIR - File "${PSOURCE}\panda3d.py" !else File /r /x CVS /x Opt?-Win32 "${PSOURCE}\direct\src\directscripts\*" SetOutPath $INSTDIR\direct\filter @@ -112,11 +110,12 @@ Section "${SMDIRECTORY}" SecCore SetOutPath $INSTDIR\direct File /r /x CVS /x Opt?-Win32 "${PSOURCE}\direct\src\*.py" File "${PANDA}\tmp\__init__.py" - SetOutPath $INSTDIR - File "${PSOURCE}\direct\src\ffi\panda3d.py" !endif SetOutPath $INSTDIR\pandac File /r "${PANDA}\pandac\*.py" + SetOutPath $INSTDIR\panda3d + File /r "${PANDA}\panda3d\*.py" + File /r "${PANDA}\panda3d\*.pyd" SetOutPath $INSTDIR\python File /r "${PANDA}\python\*" RMDir /r "$SMPROGRAMS\${SMDIRECTORY}" From c2137126007e4e37eef2c883ecaa4c7b5e80a392 Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 20 Feb 2014 18:38:35 +0000 Subject: [PATCH 21/71] fix windows build when the module is in different dll than the library defs --- dtool/src/interrogate/interfaceMakerPythonNative.cxx | 2 +- dtool/src/interrogate/interrogate_module.cxx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dtool/src/interrogate/interfaceMakerPythonNative.cxx b/dtool/src/interrogate/interfaceMakerPythonNative.cxx index 564fb41420..d8b1331efd 100755 --- a/dtool/src/interrogate/interfaceMakerPythonNative.cxx +++ b/dtool/src/interrogate/interfaceMakerPythonNative.cxx @@ -1148,7 +1148,7 @@ write_module_support(ostream &out, ostream *out_h, InterrogateModuleDef *def) { out << " {NULL, NULL, 0, NULL}\n" << "};\n\n"; - out << "struct LibraryDef " << def->library_name << "_moddef = {python_simple_funcs, BuildInstants};\n"; + out << "EXPORT_THIS struct LibraryDef " << def->library_name << "_moddef = {python_simple_funcs, BuildInstants};\n"; if (out_h != NULL) { *out_h << "extern struct LibraryDef " << def->library_name << "_moddef;\n"; } diff --git a/dtool/src/interrogate/interrogate_module.cxx b/dtool/src/interrogate/interrogate_module.cxx index 9292f04b02..fb25057007 100644 --- a/dtool/src/interrogate/interrogate_module.cxx +++ b/dtool/src/interrogate/interrogate_module.cxx @@ -112,7 +112,7 @@ int write_python_table_native(ostream &out) { pset::iterator ii; for(ii = libraries.begin(); ii != libraries.end(); ii++) { printf("Referencing Library %s\n", (*ii).c_str()); - out << "extern LibraryDef " << *ii << "_moddef;\n"; + out << "IMPORT_THIS LibraryDef " << *ii << "_moddef;\n"; } out << "\n" From 441c1722d8d62c903d24a3d7eee432c077e6e668 Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 20 Feb 2014 18:41:42 +0000 Subject: [PATCH 22/71] core.pyd needs explicit link to libp3dtoolconfig.dll --- makepanda/makepanda.py | 1 + 1 file changed, 1 insertion(+) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index 2b7564b94f..d86e614e3d 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -3546,6 +3546,7 @@ if (not RUNTIME): TargetAdd('core_module.obj', opts=['IMOD:core', 'ILIB:core']) TargetAdd('core.pyd', input='core_module.obj') + TargetAdd('core.pyd', input='libp3dtoolconfig.dll') TargetAdd('core.pyd', input='libpandaexpress.dll') TargetAdd('core.pyd', input='libpanda.dll') TargetAdd('core.pyd', opts=['PYTHON']) From 60fb5376f995209d30ff3e3adbe94bc10cf61856 Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 20 Feb 2014 19:52:47 +0000 Subject: [PATCH 23/71] fixes for building modules on windows --- makepanda/makepanda.py | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index d86e614e3d..3c90753183 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -3546,9 +3546,7 @@ if (not RUNTIME): TargetAdd('core_module.obj', opts=['IMOD:core', 'ILIB:core']) TargetAdd('core.pyd', input='core_module.obj') - TargetAdd('core.pyd', input='libp3dtoolconfig.dll') - TargetAdd('core.pyd', input='libpandaexpress.dll') - TargetAdd('core.pyd', input='libpanda.dll') + TargetAdd('core.pyd', input=COMMON_PANDA_LIBS) TargetAdd('core.pyd', opts=['PYTHON']) # @@ -3575,6 +3573,8 @@ if (PkgSkip("VISION") ==0) and (not RUNTIME): TargetAdd('vision.pyd', input='vision_module.obj') TargetAdd('vision.pyd', input='libp3vision_igate.obj') TargetAdd('vision.pyd', input='libp3vision.dll') + TargetAdd('vision.pyd', input='core.pyd') + TargetAdd('vision.pyd', input=COMMON_PANDA_LIBS) TargetAdd('vision.pyd', opts=['PYTHON']) # @@ -3604,6 +3604,8 @@ if (PkgSkip("ROCKET") ==0) and (not RUNTIME): TargetAdd('rocket.pyd', input='libp3rocket_igate.obj') TargetAdd('rocket.pyd', input='p3rocket_rocketRegion_ext.obj') TargetAdd('rocket.pyd', input='libp3rocket.dll') + TargetAdd('rocket.pyd', input='core.pyd') + TargetAdd('rocket.pyd', input=COMMON_PANDA_LIBS) TargetAdd('rocket.pyd', opts=['PYTHON']) # @@ -3628,6 +3630,8 @@ if PkgSkip("AWESOMIUM") == 0 and not RUNTIME: TargetAdd('awesomium.pyd', input='awesomium_module.obj') TargetAdd('awesomium.pyd', input='libp3awesomium_igate.obj') TargetAdd('awesomium.pyd', input='libp3awesomium.dll') + TargetAdd('awesomium.pyd', input='core.pyd') + TargetAdd('awesomium.pyd', input=COMMON_PANDA_LIBS) TargetAdd('awesomium.pyd', opts=['PYTHON']) # @@ -3660,6 +3664,8 @@ if (PkgSkip('SKEL')==0) and (not RUNTIME): TargetAdd('skel.pyd', input='skel_module.obj') TargetAdd('skel.pyd', input='libp3skel_igate.obj') TargetAdd('skel.pyd', input='libpandaskel.dll') + TargetAdd('skel.pyd', input='core.pyd') + TargetAdd('skel.pyd', input=COMMON_PANDA_LIBS) TargetAdd('skel.pyd', opts=['PYTHON']) # @@ -3694,6 +3700,8 @@ if (PkgSkip('PANDAFX')==0) and (not RUNTIME): TargetAdd('fx.pyd', input='fx_module.obj') TargetAdd('fx.pyd', input='libp3distort_igate.obj') TargetAdd('fx.pyd', input='libpandafx.dll') + TargetAdd('fx.pyd', input='core.pyd') + TargetAdd('fx.pyd', input=COMMON_PANDA_LIBS) TargetAdd('fx.pyd', opts=['PYTHON']) # @@ -3719,6 +3727,8 @@ if (PkgSkip("VRPN")==0 and not RUNTIME): TargetAdd('vrpn.pyd', input='vrpn_module.obj') TargetAdd('vrpn.pyd', input='libp3vrpn_igate.obj') TargetAdd('vrpn.pyd', input='libp3vrpn.dll') + TargetAdd('vrpn.pyd', input='core.pyd') + TargetAdd('vrpn.pyd', input=COMMON_PANDA_LIBS) TargetAdd('vrpn.pyd', opts=['PYTHON']) # @@ -3964,6 +3974,8 @@ if (not RUNTIME): TargetAdd('egg.pyd', input='libp3egg_igate.obj') TargetAdd('egg.pyd', input='libp3egg2pg_igate.obj') TargetAdd('egg.pyd', input='libpandaegg.dll') + TargetAdd('egg.pyd', input='core.pyd') + TargetAdd('egg.pyd', input=COMMON_PANDA_LIBS) TargetAdd('egg.pyd', opts=['PYTHON']) # @@ -4142,6 +4154,8 @@ if (PkgSkip("ODE")==0 and not RUNTIME): TargetAdd('ode.pyd', input='p3ode_ext_composite.obj') TargetAdd('ode.pyd', input='libpandaode_igate.obj') TargetAdd('ode.pyd', input='libpandaode.dll') + TargetAdd('ode.pyd', input='core.pyd') + TargetAdd('ode.pyd', input=COMMON_PANDA_LIBS) TargetAdd('ode.pyd', opts=['PYTHON']) # @@ -4174,6 +4188,8 @@ if (PkgSkip("BULLET")==0 and not RUNTIME): TargetAdd('bullet.pyd', input='bullet_module.obj') TargetAdd('bullet.pyd', input='libpandabullet_igate.obj') TargetAdd('bullet.pyd', input='libpandabullet.dll') + TargetAdd('bullet.pyd', input='core.pyd') + TargetAdd('bullet.pyd', input=COMMON_PANDA_LIBS) TargetAdd('bullet.pyd', opts=['PYTHON']) # @@ -4208,6 +4224,8 @@ if (PkgSkip("PHYSX")==0): TargetAdd('physx.pyd', input='physx_module.obj') TargetAdd('physx.pyd', input='libpandaphysx_igate.obj') TargetAdd('physx.pyd', input='libpandaphysx.dll') + TargetAdd('physx.pyd', input='core.pyd') + TargetAdd('physx.pyd', input=COMMON_PANDA_LIBS) TargetAdd('physx.pyd', opts=['PYTHON']) # @@ -4268,6 +4286,8 @@ if (PkgSkip("PANDAPHYSICS")==0) and (not RUNTIME): if (PkgSkip("PANDAPARTICLESYSTEM")==0): TargetAdd('physics.pyd', input='libp3particlesystem_igate.obj') TargetAdd('physics.pyd', input='libpandaphysics.dll') + TargetAdd('physics.pyd', input='core.pyd') + TargetAdd('physics.pyd', input=COMMON_PANDA_LIBS) TargetAdd('physics.pyd', opts=['PYTHON']) # @@ -4514,6 +4534,8 @@ if (PkgSkip("DIRECT")==0): TargetAdd('direct.pyd', input='direct_module.obj') TargetAdd('direct.pyd', input='libp3direct.dll') + TargetAdd('direct.pyd', input='core.pyd') + TargetAdd('direct.pyd', input=COMMON_PANDA_LIBS) TargetAdd('direct.pyd', opts=['PYTHON']) # @@ -5704,6 +5726,9 @@ if (PkgSkip("CONTRIB")==0 and not RUNTIME): TargetAdd('ai.pyd', input='ai_module.obj') TargetAdd('ai.pyd', input='libpandaai_igate.obj') + TargetAdd('ai.pyd', input='libpandaai.dll') + TargetAdd('ai.pyd', input='core.pyd') + TargetAdd('ai.pyd', input=COMMON_PANDA_LIBS) TargetAdd('ai.pyd', opts=['PYTHON']) # From 260cb66c546c37f5225e53d85964065d103e098c Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 20 Feb 2014 20:00:45 +0000 Subject: [PATCH 24/71] generate .lib for .pyd files on Windows. --- makepanda/makepanda.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index 3c90753183..5e34684449 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -1354,12 +1354,12 @@ def CompileLink(dll, obj, opts): cmd += ' /OUT:' + BracketNameWithQuotes(dll) subsystem = GetValueOption(opts, "SUBSYSTEM:") if (subsystem): cmd += " /SUBSYSTEM:" + subsystem - if (dll.endswith(".dll")): - cmd += ' /IMPLIB:' + GetOutputDir() + '/lib/'+os.path.splitext(os.path.basename(dll))[0]+".lib" + if (dll.endswith(".dll") or dll.endswith(".pyd")): + cmd += ' /IMPLIB:' + GetOutputDir() + '/lib/' + os.path.splitext(os.path.basename(dll))[0] + ".lib" for (opt, dir) in LIBDIRECTORIES: if (opt=="ALWAYS") or (opt in opts): cmd += ' /LIBPATH:' + BracketNameWithQuotes(dir) for x in obj: - if (x.endswith(".dll")): + if (x.endswith(".dll") or x.endswith(".pyd")): cmd += ' ' + GetOutputDir() + '/lib/' + os.path.splitext(os.path.basename(x))[0] + ".lib" elif (x.endswith(".lib")): dname = os.path.splitext(os.path.basename(x))[0] + ".dll" @@ -1401,12 +1401,12 @@ def CompileLink(dll, obj, opts): cmd += ' /OUT:' + BracketNameWithQuotes(dll) subsystem = GetValueOption(opts, "SUBSYSTEM:") if (subsystem): cmd += " /SUBSYSTEM:" + subsystem - if (dll.endswith(".dll")): + if (dll.endswith(".dll") or dll.endswith(".pyd")): cmd += ' /IMPLIB:' + GetOutputDir() + '/lib/'+os.path.splitext(os.path.basename(dll))[0]+".lib" for (opt, dir) in LIBDIRECTORIES: if (opt=="ALWAYS") or (opt in opts): cmd += ' /LIBPATH:' + BracketNameWithQuotes(dir) for x in obj: - if (x.endswith(".dll")): + if (x.endswith(".dll") or x.endswith(".pyd")): cmd += ' ' + GetOutputDir() + '/lib/' + os.path.splitext(os.path.basename(x))[0] + ".lib" elif (x.endswith(".lib")): dname = os.path.splitext(dll)[0]+".dll" From e1a91d28c543add74646be98741c4410847d5b17 Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 20 Feb 2014 20:25:11 +0000 Subject: [PATCH 25/71] more fixes for building of windows modules --- makepanda/makepanda.py | 90 +++++++++++++++++++++++++++++------------- 1 file changed, 63 insertions(+), 27 deletions(-) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index 5e34684449..4eaedaabf4 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -1352,29 +1352,42 @@ def CompileLink(dll, obj, opts): else: cmd += " /NOD:MSVCRT.LIB mfcs100.lib MSVCRT.lib" cmd += " /FIXED:NO /OPT:REF /STACK:4194304 /INCREMENTAL:NO " cmd += ' /OUT:' + BracketNameWithQuotes(dll) + subsystem = GetValueOption(opts, "SUBSYSTEM:") - if (subsystem): cmd += " /SUBSYSTEM:" + subsystem - if (dll.endswith(".dll") or dll.endswith(".pyd")): + if subsystem: + cmd += " /SUBSYSTEM:" + subsystem + + if dll.endswith(".dll"): cmd += ' /IMPLIB:' + GetOutputDir() + '/lib/' + os.path.splitext(os.path.basename(dll))[0] + ".lib" + for (opt, dir) in LIBDIRECTORIES: - if (opt=="ALWAYS") or (opt in opts): cmd += ' /LIBPATH:' + BracketNameWithQuotes(dir) + if (opt=="ALWAYS") or (opt in opts): + cmd += ' /LIBPATH:' + BracketNameWithQuotes(dir) + for x in obj: - if (x.endswith(".dll") or x.endswith(".pyd")): + if x.endswith(".dll"): cmd += ' ' + GetOutputDir() + '/lib/' + os.path.splitext(os.path.basename(x))[0] + ".lib" - elif (x.endswith(".lib")): + elif x.endswith(".pyd"): + cmd += ' ' + os.path.splitext(x)[0] + ".lib" + elif x.endswith(".lib"): dname = os.path.splitext(os.path.basename(x))[0] + ".dll" if (GetOrigExt(x) != ".ilb" and os.path.exists(GetOutputDir()+"/bin/" + dname)): exit("Error: in makepanda, specify "+dname+", not "+x) cmd += ' ' + BracketNameWithQuotes(x) - elif (x.endswith(".def")): + elif x.endswith(".def"): cmd += ' /DEF:' + BracketNameWithQuotes(x) - elif (x.endswith(".dat")): + elif x.endswith(".dat"): pass - else: cmd += ' ' + BracketNameWithQuotes(x) + else: + cmd += ' ' + BracketNameWithQuotes(x) + if (GetOrigExt(dll)==".exe" and "NOICON" not in opts): cmd += " " + GetOutputDir() + "/tmp/pandaIcon.res" + for (opt, name) in LIBNAMES: - if (opt=="ALWAYS") or (opt in opts): cmd += " " + BracketNameWithQuotes(name) + if (opt=="ALWAYS") or (opt in opts): + cmd += " " + BracketNameWithQuotes(name) + oscmd(cmd) else: cmd = "xilink" @@ -1399,29 +1412,42 @@ def CompileLink(dll, obj, opts): else: cmd += " /NOD:MSVCRT.LIB mfcs100.lib MSVCRT.lib" cmd += " /FIXED:NO /OPT:REF /STACK:4194304 /INCREMENTAL:NO " cmd += ' /OUT:' + BracketNameWithQuotes(dll) + subsystem = GetValueOption(opts, "SUBSYSTEM:") - if (subsystem): cmd += " /SUBSYSTEM:" + subsystem - if (dll.endswith(".dll") or dll.endswith(".pyd")): - cmd += ' /IMPLIB:' + GetOutputDir() + '/lib/'+os.path.splitext(os.path.basename(dll))[0]+".lib" + if subsystem: + cmd += " /SUBSYSTEM:" + subsystem + + if dll.endswith(".dll"): + cmd += ' /IMPLIB:' + GetOutputDir() + '/lib/' + os.path.splitext(os.path.basename(dll))[0] + ".lib" + for (opt, dir) in LIBDIRECTORIES: - if (opt=="ALWAYS") or (opt in opts): cmd += ' /LIBPATH:' + BracketNameWithQuotes(dir) + if (opt=="ALWAYS") or (opt in opts): + cmd += ' /LIBPATH:' + BracketNameWithQuotes(dir) + for x in obj: - if (x.endswith(".dll") or x.endswith(".pyd")): + if x.endswith(".dll"): cmd += ' ' + GetOutputDir() + '/lib/' + os.path.splitext(os.path.basename(x))[0] + ".lib" - elif (x.endswith(".lib")): + elif x.endswith(".pyd"): + cmd += ' ' + os.path.splitext(x)[0] + ".lib" + elif x.endswith(".lib"): dname = os.path.splitext(dll)[0]+".dll" if (GetOrigExt(x) != ".ilb" and os.path.exists(GetOutputDir()+"/bin/" + os.path.splitext(os.path.basename(x))[0] + ".dll")): exit("Error: in makepanda, specify "+dname+", not "+x) cmd += ' ' + BracketNameWithQuotes(x) - elif (x.endswith(".def")): + elif x.endswith(".def"): cmd += ' /DEF:' + BracketNameWithQuotes(x) - elif (x.endswith(".dat")): + elif x.endswith(".dat"): pass - else: cmd += ' ' + BracketNameWithQuotes(x) + else: + cmd += ' ' + BracketNameWithQuotes(x) + if (GetOrigExt(dll)==".exe" and "NOICON" not in opts): cmd += " " + GetOutputDir() + "/tmp/pandaIcon.res" + for (opt, name) in LIBNAMES: - if (opt=="ALWAYS") or (opt in opts): cmd += " " + BracketNameWithQuotes(name) + if (opt=="ALWAYS") or (opt in opts): + cmd += " " + BracketNameWithQuotes(name) + oscmd(cmd) if COMPILER == "GCC": @@ -3553,7 +3579,7 @@ if (not RUNTIME): # DIRECTORY: panda/src/vision/ # -if (PkgSkip("VISION") ==0) and (not RUNTIME): +if (PkgSkip("VISION") == 0) and (not RUNTIME): OPTS=['DIR:panda/src/vision', 'BUILDING:VISION', 'ARTOOLKIT', 'OPENCV', 'DX9', 'DIRECTCAM', 'JPEG'] TargetAdd('p3vision_composite1.obj', opts=OPTS, input='p3vision_composite1.cxx') @@ -3566,6 +3592,7 @@ if (PkgSkip("VISION") ==0) and (not RUNTIME): TargetAdd('libp3vision.in', opts=['IMOD:vision', 'ILIB:libp3vision', 'SRCDIR:panda/src/vision']) TargetAdd('libp3vision_igate.obj', input='libp3vision.in', opts=["DEPENDENCYONLY"]) + OPTS=['DIR:panda/src/vision', 'ARTOOLKIT', 'OPENCV', 'DX9', 'DIRECTCAM', 'JPEG'] TargetAdd('vision_module.obj', input='libp3vision.in') TargetAdd('vision_module.obj', opts=OPTS) TargetAdd('vision_module.obj', opts=['IMOD:vision', 'ILIB:vision']) @@ -3581,7 +3608,7 @@ if (PkgSkip("VISION") ==0) and (not RUNTIME): # DIRECTORY: panda/src/rocket/ # -if (PkgSkip("ROCKET") ==0) and (not RUNTIME): +if (PkgSkip("ROCKET") == 0) and (not RUNTIME): OPTS=['DIR:panda/src/rocket', 'BUILDING:ROCKET', 'ROCKET'] TargetAdd('p3rocket_composite1.obj', opts=OPTS, input='p3rocket_composite1.cxx') @@ -3596,6 +3623,7 @@ if (PkgSkip("ROCKET") ==0) and (not RUNTIME): TargetAdd('libp3rocket_igate.obj', input='libp3rocket.in', opts=["DEPENDENCYONLY"]) TargetAdd('p3rocket_rocketRegion_ext.obj', opts=OPTS, input='rocketRegion_ext.cxx') + OPTS=['DIR:panda/src/rocket', 'ROCKET'] TargetAdd('rocket_module.obj', input='libp3rocket.in') TargetAdd('rocket_module.obj', opts=OPTS) TargetAdd('rocket_module.obj', opts=['IMOD:rocket', 'ILIB:rocket']) @@ -3623,6 +3651,7 @@ if PkgSkip("AWESOMIUM") == 0 and not RUNTIME: TargetAdd('libp3awesomium.dll', input=COMMON_PANDA_LIBS) TargetAdd('libp3awesomium.dll', opts=OPTS) + OPTS=['DIR:panda/src/awesomium', 'AWESOMIUM'] TargetAdd('awesomium_module.obj', input='libp3awesomium.in') TargetAdd('awesomium_module.obj', opts=OPTS) TargetAdd('awesomium_module.obj', opts=['IMOD:awesomium', 'ILIB:awesomium']) @@ -3658,7 +3687,6 @@ if (PkgSkip('SKEL')==0) and (not RUNTIME): TargetAdd('libpandaskel.dll', opts=OPTS) TargetAdd('skel_module.obj', input='libp3skel.in') - TargetAdd('skel_module.obj', opts=OPTS) TargetAdd('skel_module.obj', opts=['IMOD:skel', 'ILIB:skel']) TargetAdd('skel.pyd', input='skel_module.obj') @@ -3693,6 +3721,7 @@ if (PkgSkip('PANDAFX')==0) and (not RUNTIME): TargetAdd('libpandafx.dll', input=COMMON_PANDA_LIBS) TargetAdd('libpandafx.dll', opts=['ADVAPI', 'NVIDIACG']) + OPTS=['DIR:panda/metalibs/pandafx', 'DIR:panda/src/distort', 'NVIDIACG'] TargetAdd('fx_module.obj', input='libp3distort.in') TargetAdd('fx_module.obj', opts=OPTS) TargetAdd('fx_module.obj', opts=['IMOD:fx', 'ILIB:fx']) @@ -3709,17 +3738,18 @@ if (PkgSkip('PANDAFX')==0) and (not RUNTIME): # if (PkgSkip("VRPN")==0 and not RUNTIME): - TargetAdd('libp3vrpn.dll', input='p3vrpn_composite1.obj') - TargetAdd('libp3vrpn.dll', input=COMMON_PANDA_LIBS) - TargetAdd('libp3vrpn.dll', opts=['VRPN']) - - OPTS=['DIR:panda/src/vrpn', 'BUILDING:VRPN', 'VRPN'] + OPTS=['DIR:panda/src/vrpn', 'BUILDING:VRPN', 'VRPN'] TargetAdd('p3vrpn_composite1.obj', opts=OPTS, input='p3vrpn_composite1.cxx') IGATEFILES=GetDirectoryContents('panda/src/vrpn', ["*.h", "*_composite*.cxx"]) TargetAdd('libp3vrpn.in', opts=OPTS, input=IGATEFILES) TargetAdd('libp3vrpn.in', opts=['IMOD:vrpn', 'ILIB:libp3vrpn', 'SRCDIR:panda/src/vrpn']) TargetAdd('libp3vrpn_igate.obj', input='libp3vrpn.in', opts=["DEPENDENCYONLY"]) + TargetAdd('libp3vrpn.dll', input='p3vrpn_composite1.obj') + TargetAdd('libp3vrpn.dll', input=COMMON_PANDA_LIBS) + TargetAdd('libp3vrpn.dll', opts=['VRPN']) + + OPTS=['DIR:panda/src/vrpn', 'VRPN'] TargetAdd('vrpn_module.obj', input='libp3vrpn.in') TargetAdd('vrpn_module.obj', opts=OPTS) TargetAdd('vrpn_module.obj', opts=['IMOD:vrpn', 'ILIB:vrpn']) @@ -3964,6 +3994,7 @@ if (not RUNTIME): TargetAdd('libpandaegg.dll', input=COMMON_PANDA_LIBS) TargetAdd('libpandaegg.dll', opts=['ADVAPI']) + OPTS=['DIR:panda/metalibs/pandaegg', 'DIR:panda/src/egg'] TargetAdd('egg_module.obj', input='libp3egg2pg.in') TargetAdd('egg_module.obj', input='libp3egg.in') TargetAdd('egg_module.obj', opts=OPTS) @@ -4181,6 +4212,7 @@ if (PkgSkip("BULLET")==0 and not RUNTIME): TargetAdd('libpandabullet.dll', input=COMMON_PANDA_LIBS) TargetAdd('libpandabullet.dll', opts=['WINUSER', 'BULLET']) + OPTS=['DIR:panda/metalibs/pandabullet', 'BULLET'] TargetAdd('bullet_module.obj', input='libpandabullet.in') TargetAdd('bullet_module.obj', opts=OPTS) TargetAdd('bullet_module.obj', opts=['IMOD:bullet', 'ILIB:bullet']) @@ -4217,6 +4249,7 @@ if (PkgSkip("PHYSX")==0): TargetAdd('libpandaphysx.dll', input=COMMON_PANDA_LIBS) TargetAdd('libpandaphysx.dll', opts=['WINUSER', 'PHYSX', 'NOPPC']) + OPTS=['DIR:panda/metalibs/pandaphysx', 'PHYSX', 'NOPPC'] TargetAdd('physx_module.obj', input='libpandaphysx.in') TargetAdd('physx_module.obj', opts=OPTS) TargetAdd('physx_module.obj', opts=['IMOD:physx', 'ILIB:physx']) @@ -4275,6 +4308,7 @@ if (PkgSkip("PANDAPHYSICS")==0) and (not RUNTIME): TargetAdd('libpandaphysics.dll', input=COMMON_PANDA_LIBS) TargetAdd('libpandaphysics.dll', opts=['ADVAPI']) + OPTS=['DIR:panda/metalibs/pandaphysics'] TargetAdd('physics_module.obj', input='libp3physics.in') if (PkgSkip("PANDAPARTICLESYSTEM")==0): TargetAdd('physics_module.obj', input='libp3particlesystem.in') @@ -4524,6 +4558,7 @@ if (PkgSkip("DIRECT")==0): TargetAdd('libp3direct.dll', input=COMMON_PANDA_LIBS) TargetAdd('libp3direct.dll', opts=['ADVAPI', 'OPENSSL', 'WINUSER', 'WINGDI']) + OPTS=['DIR:direct/metalibs/direct'] TargetAdd('direct_module.obj', input='libp3dcparser.in') TargetAdd('direct_module.obj', input='libp3showbase.in') TargetAdd('direct_module.obj', input='libp3deadrec.in') @@ -5720,6 +5755,7 @@ if (PkgSkip("CONTRIB")==0 and not RUNTIME): TargetAdd('libpandaai.dll', input='p3ai_composite1.obj') TargetAdd('libpandaai.dll', input=COMMON_PANDA_LIBS) + OPTS=['DIR:contrib/src/ai'] TargetAdd('ai_module.obj', input='libpandaai.in') TargetAdd('ai_module.obj', opts=OPTS) TargetAdd('ai_module.obj', opts=['IMOD:ai', 'ILIB:ai']) From e2c6e3fb56b54873329b223b48142297d0a9d811 Mon Sep 17 00:00:00 2001 From: enn0x Date: Thu, 20 Feb 2014 20:53:01 +0000 Subject: [PATCH 26/71] Fix bug #1282466 --- panda/src/bullet/bulletSoftBodyNode.cxx | 23 +++++++++++++++++++++++ panda/src/bullet/bulletSoftBodyNode.h | 3 +++ 2 files changed, 26 insertions(+) diff --git a/panda/src/bullet/bulletSoftBodyNode.cxx b/panda/src/bullet/bulletSoftBodyNode.cxx index 631325586b..24fbbcedb9 100644 --- a/panda/src/bullet/bulletSoftBodyNode.cxx +++ b/panda/src/bullet/bulletSoftBodyNode.cxx @@ -1135,3 +1135,26 @@ append_angular_joint(BulletBodyNode *body, const LVector3 &axis, PN_stdfloat erp _soft->appendAngularJoint(as, ptr); } +//////////////////////////////////////////////////////////////////// +// Function: BulletSoftBodyNode::set_wind_velocity +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +void BulletSoftBodyNode:: +set_wind_velocity(const LVector3 &velocity) { + + nassertv(!velocity.is_nan()); + _soft->setWindVelocity(LVecBase3_to_btVector3(velocity)); +} + +//////////////////////////////////////////////////////////////////// +// Function: BulletSoftBodyNode::get_wind_velocity +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +LVector3 BulletSoftBodyNode:: +get_wind_velocity() const { + + return btVector3_to_LVector3(_soft->getWindVelocity()); +} + diff --git a/panda/src/bullet/bulletSoftBodyNode.h b/panda/src/bullet/bulletSoftBodyNode.h index a9ebe075b9..a8e62d1714 100644 --- a/panda/src/bullet/bulletSoftBodyNode.h +++ b/panda/src/bullet/bulletSoftBodyNode.h @@ -96,6 +96,9 @@ PUBLISHED: void add_velocity(const LVector3 &velocity); void add_velocity(const LVector3 &velocity, int node); + void set_wind_velocity(const LVector3 &velocity); + LVector3 get_wind_velocity() const; + void set_pose(bool bvolume, bool bframe); BoundingBox get_aabb() const; From 6e16ca7f686c69aca5bc00e875a1a203081b8d8e Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 20 Feb 2014 20:53:51 +0000 Subject: [PATCH 27/71] More fixes for windows modules --- makepanda/makepanda.py | 52 +++++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index 4eaedaabf4..e73ab29b4d 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -3587,12 +3587,12 @@ if (PkgSkip("VISION") == 0) and (not RUNTIME): TargetAdd('libp3vision.dll', input=COMMON_PANDA_LIBS) TargetAdd('libp3vision.dll', opts=OPTS) + OPTS=['DIR:panda/src/vision', 'ARTOOLKIT', 'OPENCV', 'DX9', 'DIRECTCAM', 'JPEG'] IGATEFILES=GetDirectoryContents('panda/src/vision', ["*.h", "*_composite*.cxx"]) TargetAdd('libp3vision.in', opts=OPTS, input=IGATEFILES) TargetAdd('libp3vision.in', opts=['IMOD:vision', 'ILIB:libp3vision', 'SRCDIR:panda/src/vision']) TargetAdd('libp3vision_igate.obj', input='libp3vision.in', opts=["DEPENDENCYONLY"]) - OPTS=['DIR:panda/src/vision', 'ARTOOLKIT', 'OPENCV', 'DX9', 'DIRECTCAM', 'JPEG'] TargetAdd('vision_module.obj', input='libp3vision.in') TargetAdd('vision_module.obj', opts=OPTS) TargetAdd('vision_module.obj', opts=['IMOD:vision', 'ILIB:vision']) @@ -3616,6 +3616,7 @@ if (PkgSkip("ROCKET") == 0) and (not RUNTIME): TargetAdd('libp3rocket.dll', input=COMMON_PANDA_LIBS) TargetAdd('libp3rocket.dll', opts=OPTS) + OPTS=['DIR:panda/src/rocket', 'ROCKET'] IGATEFILES=GetDirectoryContents('panda/src/rocket', ["rocketInputHandler.h", "rocketInputHandler.cxx", "rocketRegion.h", "rocketRegion.cxx", "rocketRegion_ext.h"]) TargetAdd('libp3rocket.in', opts=OPTS, input=IGATEFILES) @@ -3623,7 +3624,6 @@ if (PkgSkip("ROCKET") == 0) and (not RUNTIME): TargetAdd('libp3rocket_igate.obj', input='libp3rocket.in', opts=["DEPENDENCYONLY"]) TargetAdd('p3rocket_rocketRegion_ext.obj', opts=OPTS, input='rocketRegion_ext.cxx') - OPTS=['DIR:panda/src/rocket', 'ROCKET'] TargetAdd('rocket_module.obj', input='libp3rocket.in') TargetAdd('rocket_module.obj', opts=OPTS) TargetAdd('rocket_module.obj', opts=['IMOD:rocket', 'ILIB:rocket']) @@ -3642,16 +3642,16 @@ if (PkgSkip("ROCKET") == 0) and (not RUNTIME): if PkgSkip("AWESOMIUM") == 0 and not RUNTIME: OPTS=['DIR:panda/src/awesomium', 'BUILDING:PANDAAWESOMIUM', 'AWESOMIUM'] TargetAdd('pandaawesomium_composite1.obj', opts=OPTS, input='pandaawesomium_composite1.cxx') - IGATEFILES=GetDirectoryContents('panda/src/awesomium', ["*.h", "*_composite1.cxx"]) - TargetAdd('libp3awesomium.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3awesomium.in', opts=['IMOD:awesomium', 'ILIB:libp3awesomium', 'SRCDIR:panda/src/awesomium']) - TargetAdd('libp3awesomium_igate.obj', input='libp3awesomium.in', opts=["DEPENDENCYONLY"]) - TargetAdd('libp3awesomium.dll', input='pandaawesomium_composite1.obj') TargetAdd('libp3awesomium.dll', input=COMMON_PANDA_LIBS) TargetAdd('libp3awesomium.dll', opts=OPTS) OPTS=['DIR:panda/src/awesomium', 'AWESOMIUM'] + IGATEFILES=GetDirectoryContents('panda/src/awesomium', ["*.h", "*_composite1.cxx"]) + TargetAdd('libp3awesomium.in', opts=OPTS, input=IGATEFILES) + TargetAdd('libp3awesomium.in', opts=['IMOD:awesomium', 'ILIB:libp3awesomium', 'SRCDIR:panda/src/awesomium']) + TargetAdd('libp3awesomium_igate.obj', input='libp3awesomium.in', opts=["DEPENDENCYONLY"]) + TargetAdd('awesomium_module.obj', input='libp3awesomium.in') TargetAdd('awesomium_module.obj', opts=OPTS) TargetAdd('awesomium_module.obj', opts=['IMOD:awesomium', 'ILIB:awesomium']) @@ -3670,6 +3670,8 @@ if PkgSkip("AWESOMIUM") == 0 and not RUNTIME: if (PkgSkip('SKEL')==0) and (not RUNTIME): OPTS=['DIR:panda/src/skel', 'BUILDING:PANDASKEL', 'ADVAPI'] TargetAdd('p3skel_composite1.obj', opts=OPTS, input='p3skel_composite1.cxx') + + OPTS=['DIR:panda/src/skel', 'ADVAPI'] IGATEFILES=GetDirectoryContents("panda/src/skel", ["*.h", "*_composite*.cxx"]) TargetAdd('libp3skel.in', opts=OPTS, input=IGATEFILES) TargetAdd('libp3skel.in', opts=['IMOD:skel', 'ILIB:libp3skel', 'SRCDIR:panda/src/skel']) @@ -3681,7 +3683,6 @@ if (PkgSkip('SKEL')==0) and (not RUNTIME): if (PkgSkip('SKEL')==0) and (not RUNTIME): OPTS=['BUILDING:PANDASKEL', 'ADVAPI'] - TargetAdd('libpandaskel.dll', input='p3skel_composite1.obj') TargetAdd('libpandaskel.dll', input=COMMON_PANDA_LIBS) TargetAdd('libpandaskel.dll', opts=OPTS) @@ -3701,7 +3702,7 @@ if (PkgSkip('SKEL')==0) and (not RUNTIME): # if (PkgSkip('PANDAFX')==0) and (not RUNTIME): - OPTS=['DIR:panda/src/distort', 'BUILDING:PANDAFX'] + OPTS=['DIR:panda/src/distort'] TargetAdd('p3distort_composite1.obj', opts=OPTS, input='p3distort_composite1.cxx') IGATEFILES=GetDirectoryContents('panda/src/distort', ["*.h", "*_composite*.cxx"]) TargetAdd('libp3distort.in', opts=OPTS, input=IGATEFILES) @@ -3740,16 +3741,16 @@ if (PkgSkip('PANDAFX')==0) and (not RUNTIME): if (PkgSkip("VRPN")==0 and not RUNTIME): OPTS=['DIR:panda/src/vrpn', 'BUILDING:VRPN', 'VRPN'] TargetAdd('p3vrpn_composite1.obj', opts=OPTS, input='p3vrpn_composite1.cxx') - IGATEFILES=GetDirectoryContents('panda/src/vrpn', ["*.h", "*_composite*.cxx"]) - TargetAdd('libp3vrpn.in', opts=OPTS, input=IGATEFILES) - TargetAdd('libp3vrpn.in', opts=['IMOD:vrpn', 'ILIB:libp3vrpn', 'SRCDIR:panda/src/vrpn']) - TargetAdd('libp3vrpn_igate.obj', input='libp3vrpn.in', opts=["DEPENDENCYONLY"]) - TargetAdd('libp3vrpn.dll', input='p3vrpn_composite1.obj') TargetAdd('libp3vrpn.dll', input=COMMON_PANDA_LIBS) TargetAdd('libp3vrpn.dll', opts=['VRPN']) OPTS=['DIR:panda/src/vrpn', 'VRPN'] + IGATEFILES=GetDirectoryContents('panda/src/vrpn', ["*.h", "*_composite*.cxx"]) + TargetAdd('libp3vrpn.in', opts=OPTS, input=IGATEFILES) + TargetAdd('libp3vrpn.in', opts=['IMOD:vrpn', 'ILIB:libp3vrpn', 'SRCDIR:panda/src/vrpn']) + TargetAdd('libp3vrpn_igate.obj', input='libp3vrpn.in', opts=["DEPENDENCYONLY"]) + TargetAdd('vrpn_module.obj', input='libp3vrpn.in') TargetAdd('vrpn_module.obj', opts=OPTS) TargetAdd('vrpn_module.obj', opts=['IMOD:vrpn', 'ILIB:vrpn']) @@ -3911,13 +3912,15 @@ if PkgSkip("DX9")==0 and not RUNTIME: # if (not RUNTIME): - OPTS=['DIR:panda/src/egg', 'BUILDING:PANDAEGG', 'ZLIB', 'BISONPREFIX_eggyy', 'FLEXDASHI'] + OPTS=['DIR:panda/src/egg', 'BUILDING:PANDAEGG', 'ZLIB', 'BISONPREFIX_eggyy', 'FLEXDASHI'] CreateFile(GetOutputDir()+"/include/parser.h") TargetAdd('p3egg_parser.obj', opts=OPTS, input='parser.yxx') TargetAdd('parser.h', input='p3egg_parser.obj', opts=['DEPENDENCYONLY']) TargetAdd('p3egg_lexer.obj', opts=OPTS, input='lexer.lxx') TargetAdd('p3egg_composite1.obj', opts=OPTS, input='p3egg_composite1.cxx') TargetAdd('p3egg_composite2.obj', opts=OPTS, input='p3egg_composite2.cxx') + + OPTS=['DIR:panda/src/egg', 'ZLIB'] IGATEFILES=GetDirectoryContents('panda/src/egg', ["*.h", "*_composite*.cxx"]) if "parser.h" in IGATEFILES: IGATEFILES.remove("parser.h") TargetAdd('libp3egg.in', opts=OPTS, input=IGATEFILES) @@ -3933,6 +3936,8 @@ if (not RUNTIME): OPTS=['DIR:panda/src/egg2pg', 'BUILDING:PANDAEGG'] TargetAdd('p3egg2pg_composite1.obj', opts=OPTS, input='p3egg2pg_composite1.cxx') TargetAdd('p3egg2pg_composite2.obj', opts=OPTS, input='p3egg2pg_composite2.cxx') + + OPTS=['DIR:panda/src/egg2pg'] IGATEFILES=['load_egg_file.h'] TargetAdd('libp3egg2pg.in', opts=OPTS, input=IGATEFILES) TargetAdd('libp3egg2pg.in', opts=['IMOD:egg', 'ILIB:libp3egg2pg', 'SRCDIR:panda/src/egg2pg']) @@ -4195,6 +4200,8 @@ if (PkgSkip("ODE")==0 and not RUNTIME): if (PkgSkip("BULLET")==0 and not RUNTIME): OPTS=['DIR:panda/src/bullet', 'BUILDING:PANDABULLET', 'BULLET'] TargetAdd('p3bullet_composite.obj', opts=OPTS, input='p3bullet_composite.cxx') + + OPTS=['DIR:panda/src/bullet', 'BULLET'] IGATEFILES=GetDirectoryContents('panda/src/bullet', ["*.h", "*_composite*.cxx"]) TargetAdd('libpandabullet.in', opts=OPTS, input=IGATEFILES) TargetAdd('libpandabullet.in', opts=['IMOD:bullet', 'ILIB:libpandabullet', 'SRCDIR:panda/src/bullet']) @@ -4231,6 +4238,8 @@ if (PkgSkip("BULLET")==0 and not RUNTIME): if (PkgSkip("PHYSX")==0): OPTS=['DIR:panda/src/physx', 'BUILDING:PANDAPHYSX', 'PHYSX', 'NOPPC'] TargetAdd('p3physx_composite.obj', opts=OPTS, input='p3physx_composite.cxx') + + OPTS=['DIR:panda/src/physx', 'PHYSX', 'NOPPC'] IGATEFILES=GetDirectoryContents('panda/src/physx', ["*.h", "*_composite*.cxx"]) TargetAdd('libpandaphysx.in', opts=OPTS, input=IGATEFILES) TargetAdd('libpandaphysx.in', opts=['IMOD:physx', 'ILIB:libpandaphysx', 'SRCDIR:panda/src/physx']) @@ -4269,6 +4278,8 @@ if (PkgSkip("PANDAPHYSICS")==0) and (not RUNTIME): OPTS=['DIR:panda/src/physics', 'BUILDING:PANDAPHYSICS'] TargetAdd('p3physics_composite1.obj', opts=OPTS, input='p3physics_composite1.cxx') TargetAdd('p3physics_composite2.obj', opts=OPTS, input='p3physics_composite2.cxx') + + OPTS=['DIR:panda/src/physics'] IGATEFILES=GetDirectoryContents('panda/src/physics', ["*.h", "*_composite*.cxx"]) IGATEFILES.remove("forces.h") TargetAdd('libp3physics.in', opts=OPTS, input=IGATEFILES) @@ -4283,6 +4294,8 @@ if (PkgSkip("PANDAPHYSICS")==0) and (PkgSkip("PANDAPARTICLESYSTEM")==0) and (not OPTS=['DIR:panda/src/particlesystem', 'BUILDING:PANDAPHYSICS'] TargetAdd('p3particlesystem_composite1.obj', opts=OPTS, input='p3particlesystem_composite1.cxx') TargetAdd('p3particlesystem_composite2.obj', opts=OPTS, input='p3particlesystem_composite2.cxx') + + OPTS=['DIR:panda/src/particlesystem'] IGATEFILES=GetDirectoryContents('panda/src/particlesystem', ["*.h", "*_composite*.cxx"]) IGATEFILES.remove('orientedParticle.h') IGATEFILES.remove('orientedParticleFactory.h') @@ -4291,6 +4304,7 @@ if (PkgSkip("PANDAPHYSICS")==0) and (PkgSkip("PANDAPARTICLESYSTEM")==0) and (not IGATEFILES.remove('particles.h') TargetAdd('libp3particlesystem.in', opts=OPTS, input=IGATEFILES) TargetAdd('libp3particlesystem.in', opts=['IMOD:physics', 'ILIB:libp3particlesystem', 'SRCDIR:panda/src/particlesystem']) + TargetAdd('libp3particlesystem_igate.obj', input='libp3particlesystem.in', opts=["DEPENDENCYONLY"]) # # DIRECTORY: panda/metalibs/pandaphysics/ @@ -5747,15 +5761,15 @@ for VER in MAYAVERSIONS: if (PkgSkip("CONTRIB")==0 and not RUNTIME): OPTS=['DIR:contrib/src/ai', 'BUILDING:PANDAAI'] TargetAdd('p3ai_composite1.obj', opts=OPTS, input='p3ai_composite1.cxx') + TargetAdd('libpandaai.dll', input='p3ai_composite1.obj') + TargetAdd('libpandaai.dll', input=COMMON_PANDA_LIBS) + + OPTS=['DIR:contrib/src/ai'] IGATEFILES=GetDirectoryContents('contrib/src/ai', ["*.h", "*_composite*.cxx"]) TargetAdd('libpandaai.in', opts=OPTS, input=IGATEFILES) TargetAdd('libpandaai.in', opts=['IMOD:ai', 'ILIB:libpandaai', 'SRCDIR:contrib/src/ai']) TargetAdd('libpandaai_igate.obj', input='libpandaai.in', opts=["DEPENDENCYONLY"]) - TargetAdd('libpandaai.dll', input='p3ai_composite1.obj') - TargetAdd('libpandaai.dll', input=COMMON_PANDA_LIBS) - - OPTS=['DIR:contrib/src/ai'] TargetAdd('ai_module.obj', input='libpandaai.in') TargetAdd('ai_module.obj', opts=OPTS) TargetAdd('ai_module.obj', opts=['IMOD:ai', 'ILIB:ai']) From 0735b159810da33701905b00946fa23f109b22e5 Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 20 Feb 2014 21:09:53 +0000 Subject: [PATCH 28/71] fix for boost link error on libRocket --- makepanda/makepanda.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index e73ab29b4d..f8fc349a10 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -3634,7 +3634,7 @@ if (PkgSkip("ROCKET") == 0) and (not RUNTIME): TargetAdd('rocket.pyd', input='libp3rocket.dll') TargetAdd('rocket.pyd', input='core.pyd') TargetAdd('rocket.pyd', input=COMMON_PANDA_LIBS) - TargetAdd('rocket.pyd', opts=['PYTHON']) + TargetAdd('rocket.pyd', opts=['PYTHON', 'ROCKET']) # # DIRECTORY: panda/src/p3awesomium From d3aab3bb1848757d307b7f652e448a7740bdaf2e Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 20 Feb 2014 21:16:09 +0000 Subject: [PATCH 29/71] ugh I'm fuxing up all over the place. sorry guys. :-/ --- makepanda/makepanda.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index f8fc349a10..8d17276a78 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -3702,8 +3702,10 @@ if (PkgSkip('SKEL')==0) and (not RUNTIME): # if (PkgSkip('PANDAFX')==0) and (not RUNTIME): - OPTS=['DIR:panda/src/distort'] + OPTS=['DIR:panda/src/distort', 'BUILDING:PANDAFX'] TargetAdd('p3distort_composite1.obj', opts=OPTS, input='p3distort_composite1.cxx') + + OPTS=['DIR:panda/metalibs/pandafx', 'DIR:panda/src/distort', 'NVIDIACG'] IGATEFILES=GetDirectoryContents('panda/src/distort', ["*.h", "*_composite*.cxx"]) TargetAdd('libp3distort.in', opts=OPTS, input=IGATEFILES) TargetAdd('libp3distort.in', opts=['IMOD:fx', 'ILIB:libp3distort', 'SRCDIR:panda/src/distort']) From dd6cfe5fa9ccd7f98aa933b0554d7fedabe873b6 Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 20 Feb 2014 21:50:00 +0000 Subject: [PATCH 30/71] Export EggParameters --- panda/src/egg/eggParameters.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panda/src/egg/eggParameters.h b/panda/src/egg/eggParameters.h index 788395612c..b813fd684b 100644 --- a/panda/src/egg/eggParameters.h +++ b/panda/src/egg/eggParameters.h @@ -58,6 +58,6 @@ public: double _table_threshold; }; -extern EggParameters *egg_parameters; +extern EXPCL_PANDAEGG EggParameters *egg_parameters; #endif From 3b1e193abbcba77318cfeb4b748d07487c41ce8b Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 20 Feb 2014 21:56:34 +0000 Subject: [PATCH 31/71] extension functions for ODE classes --- panda/src/ode/odeBody_ext.I | 26 ++++++ panda/src/ode/odeBody_ext.h | 43 ++++++++++ panda/src/ode/odeGeom_ext.I | 47 +++++++++++ panda/src/ode/odeGeom_ext.cxx | 133 ++++++++++++++++++++++++++++++ panda/src/ode/odeGeom_ext.h | 46 +++++++++++ panda/src/ode/odeJoint_ext.cxx | 144 +++++++++++++++++++++++++++++++++ panda/src/ode/odeJoint_ext.h | 43 ++++++++++ panda/src/ode/odeSpace_ext.I | 58 +++++++++++++ panda/src/ode/odeSpace_ext.cxx | 112 +++++++++++++++++++++++++ panda/src/ode/odeSpace_ext.h | 54 +++++++++++++ panda/src/ode/odeUtil_ext.cxx | 66 +++++++++++++++ panda/src/ode/odeUtil_ext.h | 47 +++++++++++ 12 files changed, 819 insertions(+) create mode 100644 panda/src/ode/odeBody_ext.I create mode 100644 panda/src/ode/odeBody_ext.h create mode 100644 panda/src/ode/odeGeom_ext.I create mode 100644 panda/src/ode/odeGeom_ext.cxx create mode 100644 panda/src/ode/odeGeom_ext.h create mode 100644 panda/src/ode/odeJoint_ext.cxx create mode 100644 panda/src/ode/odeJoint_ext.h create mode 100644 panda/src/ode/odeSpace_ext.I create mode 100644 panda/src/ode/odeSpace_ext.cxx create mode 100644 panda/src/ode/odeSpace_ext.h create mode 100644 panda/src/ode/odeUtil_ext.cxx create mode 100644 panda/src/ode/odeUtil_ext.h diff --git a/panda/src/ode/odeBody_ext.I b/panda/src/ode/odeBody_ext.I new file mode 100644 index 0000000000..b3618ae4b1 --- /dev/null +++ b/panda/src/ode/odeBody_ext.I @@ -0,0 +1,26 @@ +// Filename: odeBody_ext.I +// Created by: rdb (11Dec13) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +#include "odeJoint_ext.h" + +//////////////////////////////////////////////////////////////////// +// Function: OdeBody::get_converted_joint +// Access: Published +// Description: Equivalent to get_joint().convert() +//////////////////////////////////////////////////////////////////// +INLINE PyObject *Extension:: +get_converted_joint(int i) const { + OdeJoint j = _this->get_joint(i); + return invoke_extension(&j).convert(); +} diff --git a/panda/src/ode/odeBody_ext.h b/panda/src/ode/odeBody_ext.h new file mode 100644 index 0000000000..1331d47183 --- /dev/null +++ b/panda/src/ode/odeBody_ext.h @@ -0,0 +1,43 @@ +// Filename: odeBody_ext.h +// Created by: rdb (11Dec13) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +#ifndef ODEBODY_EXT_H +#define ODEBODY_EXT_H + +#include "dtoolbase.h" + +#ifdef HAVE_PYTHON + +#include "config_ode.h" +#include "odeBody.h" +#include "extension.h" +#include "py_panda.h" + +//////////////////////////////////////////////////////////////////// +// Class : Extension +// Description : This class defines the extension methods for +// NodePathCollection, which are called instead of +// any C++ methods with the same prototype. +//////////////////////////////////////////////////////////////////// +template<> +class Extension : public ExtensionBase { +public: + INLINE PyObject *get_converted_joint(int i) const; +}; + +#include "odeBody_ext.I" + +#endif // HAVE_PYTHON + +#endif // ODEBODY_EXT_H diff --git a/panda/src/ode/odeGeom_ext.I b/panda/src/ode/odeGeom_ext.I new file mode 100644 index 0000000000..6d1cf2bebd --- /dev/null +++ b/panda/src/ode/odeGeom_ext.I @@ -0,0 +1,47 @@ +// Filename: odeGeom_ext.I +// Created by: rdb (11Dec13) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +#include "odeSpace_ext.h" +#include "lpoint3.h" + +#ifndef CPPPARSER +IMPORT_THIS struct Dtool_PyTypedObject Dtool_LPoint3f; +#endif + +//////////////////////////////////////////////////////////////////// +// Function: OdeGeom::get_AA_bounds +// Access: Published +// Description: A more Pythonic way of calling getAABB() +//////////////////////////////////////////////////////////////////// +INLINE PyObject *Extension:: +get_AA_bounds() const { + LPoint3f *min_point = new LPoint3f; + LPoint3f *max_point = new LPoint3f; + _this->get_AABB(*min_point, *max_point); + + PyObject *min_inst = DTool_CreatePyInstance((void*) min_point, Dtool_LPoint3f, true, false); + PyObject *max_inst = DTool_CreatePyInstance((void*) max_point, Dtool_LPoint3f, true, false); + return Py_BuildValue("NN", min_inst, max_inst); +} + +//////////////////////////////////////////////////////////////////// +// Function: OdeGeom::get_converted_space +// Access: Published +// Description: Equivalent to get_space().convert() +//////////////////////////////////////////////////////////////////// +INLINE PyObject *Extension:: +get_converted_space() const { + OdeSpace s = _this->get_space(); + return invoke_extension(&s).convert(); +} diff --git a/panda/src/ode/odeGeom_ext.cxx b/panda/src/ode/odeGeom_ext.cxx new file mode 100644 index 0000000000..c9b2cd5320 --- /dev/null +++ b/panda/src/ode/odeGeom_ext.cxx @@ -0,0 +1,133 @@ +// Filename: odeGeom_ext.cxx +// Created by: rdb (11Dec13) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +#include "odeGeom_ext.h" + +#include "odeBoxGeom.h" +//#include "odeConvexGeom.h" +#include "odeGeom.h" +#include "odeHashSpace.h" +#include "odeCappedCylinderGeom.h" +//#include "odeHeightfieldGeom.h" +#include "odePlaneGeom.h" +#include "odeQuadTreeSpace.h" +#include "odeRayGeom.h" +#include "odeSimpleSpace.h" +#include "odeSpace.h" +#include "odeSphereGeom.h" +#include "odeTriMeshGeom.h" + +#ifdef HAVE_PYTHON + +#ifndef CPPPARSER +extern EXPCL_PANDAODE Dtool_PyTypedObject Dtool_OdeBoxGeom; +//extern EXPCL_PANDAODE Dtool_PyTypedObject Dtool_OdeConvexGeom; +extern EXPCL_PANDAODE Dtool_PyTypedObject Dtool_OdeGeom; +extern EXPCL_PANDAODE Dtool_PyTypedObject Dtool_OdeHashSpace; +extern EXPCL_PANDAODE Dtool_PyTypedObject Dtool_OdeCappedCylinderGeom; +//extern EXPCL_PANDAODE Dtool_PyTypedObject Dtool_OdeHeightfieldGeom; +extern EXPCL_PANDAODE Dtool_PyTypedObject Dtool_OdePlaneGeom; +extern EXPCL_PANDAODE Dtool_PyTypedObject Dtool_OdeQuadTreeSpace; +extern EXPCL_PANDAODE Dtool_PyTypedObject Dtool_OdeRayGeom; +extern EXPCL_PANDAODE Dtool_PyTypedObject Dtool_OdeSimpleSpace; +extern EXPCL_PANDAODE Dtool_PyTypedObject Dtool_OdeSpace; +extern EXPCL_PANDAODE Dtool_PyTypedObject Dtool_OdeSphereGeom; +extern EXPCL_PANDAODE Dtool_PyTypedObject Dtool_OdeTriMeshGeom; +#endif + +//////////////////////////////////////////////////////////////////// +// Function: OdeGeom::convert +// Access: Published +// Description: Do a sort of pseudo-downcast on this space in +// order to expose its specialized functions. +//////////////////////////////////////////////////////////////////// +PyObject *Extension:: +convert() const { + Dtool_PyTypedObject *class_type; + TypedObject *geom; + + switch (_this->get_class()) { + case OdeGeom::GC_sphere: + geom = new OdeSphereGeom(_this->get_id()); + class_type = &Dtool_OdeSphereGeom; + break; + + case OdeGeom::GC_box: + geom = new OdeBoxGeom(_this->get_id()); + class_type = &Dtool_OdeBoxGeom; + break; + + case OdeGeom::GC_capped_cylinder: + geom = new OdeCappedCylinderGeom(_this->get_id()); + class_type = &Dtool_OdeCappedCylinderGeom; + break; + + case OdeGeom::GC_plane: + geom = new OdePlaneGeom(_this->get_id()); + class_type = &Dtool_OdePlaneGeom; + break; + + case OdeGeom::GC_ray: + geom = new OdeRayGeom(_this->get_id()); + class_type = &Dtool_OdeRayGeom; + break; + + //case OdeGeom::GC_convex: + // geom = new OdeConvexGeom(_this->get_id()); + // class_type = &Dtool_OdeConvexGeom; + // break; + + case OdeGeom::GC_tri_mesh: + geom = new OdeTriMeshGeom(_this->get_id()); + class_type = &Dtool_OdeTriMeshGeom; + break; + + //case OdeGeom::GC_heightfield: + // geom = new OdeHeightfieldGeom(_this->get_id()); + // class_type = &Dtool_OdeHeightfieldGeom; + // break; + + case OdeGeom::GC_simple_space: + geom = new OdeSimpleSpace((dSpaceID) _this->get_id()); + class_type = &Dtool_OdeSimpleSpace; + break; + + case OdeGeom::GC_hash_space: + geom = new OdeHashSpace((dSpaceID) _this->get_id()); + class_type = &Dtool_OdeHashSpace; + break; + + case OdeGeom::GC_quad_tree_space: + geom = new OdeQuadTreeSpace((dSpaceID) _this->get_id()); + class_type = &Dtool_OdeQuadTreeSpace; + break; + + default: + // This shouldn't happen, but if it does, we + // should just return a regular OdeGeom or OdeSpace. + if (_this->is_space()) { + geom = new OdeSpace((dSpaceID) _this->get_id()); + class_type = &Dtool_OdeSpace; + + } else { + geom = new OdeGeom(_this->get_id()); + class_type = &Dtool_OdeGeom; + } + } + + return DTool_CreatePyInstanceTyped((void *)geom, *class_type, + true, false, geom->get_type_index()); +} + +#endif // HAVE_PYTHON diff --git a/panda/src/ode/odeGeom_ext.h b/panda/src/ode/odeGeom_ext.h new file mode 100644 index 0000000000..7a3d1120c2 --- /dev/null +++ b/panda/src/ode/odeGeom_ext.h @@ -0,0 +1,46 @@ +// Filename: odeGeom_ext.h +// Created by: rdb (11Dec13) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +#ifndef ODEGEOM_EXT_H +#define ODEGEOM_EXT_H + +#include "dtoolbase.h" + +#ifdef HAVE_PYTHON + +#include "config_ode.h" +#include "odeGeom.h" +#include "extension.h" +#include "py_panda.h" + +//////////////////////////////////////////////////////////////////// +// Class : Extension +// Description : This class defines the extension methods for +// NodePathCollection, which are called instead of +// any C++ methods with the same prototype. +//////////////////////////////////////////////////////////////////// +template<> +class Extension : public ExtensionBase { +public: + INLINE PyObject *get_AA_bounds() const; + + PyObject *convert() const; + INLINE PyObject *get_converted_space() const; +}; + +#include "odeGeom_ext.I" + +#endif // HAVE_PYTHON + +#endif // ODEGEOM_EXT_H diff --git a/panda/src/ode/odeJoint_ext.cxx b/panda/src/ode/odeJoint_ext.cxx new file mode 100644 index 0000000000..871eb09c78 --- /dev/null +++ b/panda/src/ode/odeJoint_ext.cxx @@ -0,0 +1,144 @@ +// Filename: odeJoint_ext.cxx +// Created by: rdb (11Dec13) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +#include "odeJoint_ext.h" + +#ifdef HAVE_PYTHON + +#include "odeJoint.h" +#include "odeBallJoint.h" +#include "odeHingeJoint.h" +#include "odeSliderJoint.h" +#include "odeContactJoint.h" +#include "odeUniversalJoint.h" +#include "odeHinge2Joint.h" +#include "odeFixedJoint.h" +#include "odeNullJoint.h" +#include "odeAMotorJoint.h" +#include "odeLMotorJoint.h" +#include "odePlane2dJoint.h" + +#ifndef CPPPARSER +extern EXPCL_PANDAODE Dtool_PyTypedObject Dtool_OdeJoint; +extern EXPCL_PANDAODE Dtool_PyTypedObject Dtool_OdeBallJoint; +extern EXPCL_PANDAODE Dtool_PyTypedObject Dtool_OdeHingeJoint; +extern EXPCL_PANDAODE Dtool_PyTypedObject Dtool_OdeSliderJoint; +extern EXPCL_PANDAODE Dtool_PyTypedObject Dtool_OdeContactJoint; +extern EXPCL_PANDAODE Dtool_PyTypedObject Dtool_OdeUniversalJoint; +extern EXPCL_PANDAODE Dtool_PyTypedObject Dtool_OdeHinge2Joint; +extern EXPCL_PANDAODE Dtool_PyTypedObject Dtool_OdeFixedJoint; +extern EXPCL_PANDAODE Dtool_PyTypedObject Dtool_OdeNullJoint; +extern EXPCL_PANDAODE Dtool_PyTypedObject Dtool_OdeAMotorJoint; +extern EXPCL_PANDAODE Dtool_PyTypedObject Dtool_OdeLMotorJoint; +extern EXPCL_PANDAODE Dtool_PyTypedObject Dtool_OdePlane2dJoint; +#endif + +//////////////////////////////////////////////////////////////////// +// Function: OdeJoint::attach +// Access: Published +// Description: Attach two bodies together. If either body is None, +// the other will be attached to the environment. +//////////////////////////////////////////////////////////////////// +void Extension:: +attach(const OdeBody *body1, const OdeBody *body2) { + if (body1 && body2) { + _this->attach_bodies(*body1, *body2); + + } else if (body1 && !body2) { + _this->attach_body(*body1, 0); + + } else if (!body1 && body2) { + _this->attach_body(*body2, 1); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: OdeJoint::convert +// Access: Published +// Description: Do a sort of pseudo-downcast on this space in +// order to expose its specialized functions. +//////////////////////////////////////////////////////////////////// +PyObject *Extension:: +convert() const { + Dtool_PyTypedObject *class_type; + OdeJoint *joint; + + switch (_this->get_joint_type()) { + case OdeJoint::JT_ball: + joint = new OdeBallJoint(_this->get_id()); + class_type = &Dtool_OdeBallJoint; + break; + + case OdeJoint::JT_hinge: + joint = new OdeHingeJoint(_this->get_id()); + class_type = &Dtool_OdeHingeJoint; + break; + + case OdeJoint::JT_slider: + joint = new OdeSliderJoint(_this->get_id()); + class_type = &Dtool_OdeSliderJoint; + break; + + case OdeJoint::JT_contact: + joint = new OdeContactJoint(_this->get_id()); + class_type = &Dtool_OdeContactJoint; + break; + + case OdeJoint::JT_universal: + joint = new OdeUniversalJoint(_this->get_id()); + class_type = &Dtool_OdeUniversalJoint; + break; + + case OdeJoint::JT_hinge2: + joint = new OdeHinge2Joint(_this->get_id()); + class_type = &Dtool_OdeHinge2Joint; + break; + + case OdeJoint::JT_fixed: + joint = new OdeFixedJoint(_this->get_id()); + class_type = &Dtool_OdeFixedJoint; + break; + + case OdeJoint::JT_null: + joint = new OdeNullJoint(_this->get_id()); + class_type = &Dtool_OdeNullJoint; + break; + + case OdeJoint::JT_a_motor: + joint = new OdeAMotorJoint(_this->get_id()); + class_type = &Dtool_OdeAMotorJoint; + break; + + case OdeJoint::JT_l_motor: + joint = new OdeLMotorJoint(_this->get_id()); + class_type = &Dtool_OdeLMotorJoint; + break; + + case OdeJoint::JT_plane2d: + joint = new OdePlane2dJoint(_this->get_id()); + class_type = &Dtool_OdePlane2dJoint; + break; + + default: + // This shouldn't happen, but if it does, we + // should just return a regular OdeJoint. + joint = new OdeJoint(_this->get_id()); + class_type = &Dtool_OdeJoint; + } + + return DTool_CreatePyInstanceTyped((void *)joint, *class_type, + true, false, joint->get_type_index()); +} + +#endif // HAVE_PYTHON diff --git a/panda/src/ode/odeJoint_ext.h b/panda/src/ode/odeJoint_ext.h new file mode 100644 index 0000000000..48c5f20495 --- /dev/null +++ b/panda/src/ode/odeJoint_ext.h @@ -0,0 +1,43 @@ +// Filename: odeJoint_ext.h +// Created by: rdb (11Dec13) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +#ifndef ODEJOINT_EXT_H +#define ODEJOINT_EXT_H + +#include "dtoolbase.h" + +#ifdef HAVE_PYTHON + +#include "config_ode.h" +#include "odeJoint.h" +#include "extension.h" +#include "py_panda.h" + +//////////////////////////////////////////////////////////////////// +// Class : Extension +// Description : This class defines the extension methods for +// NodePathCollection, which are called instead of +// any C++ methods with the same prototype. +//////////////////////////////////////////////////////////////////// +template<> +class Extension : public ExtensionBase { +public: + void attach(const OdeBody *body1, const OdeBody *body2); + + PyObject *convert() const; +}; + +#endif // HAVE_PYTHON + +#endif // ODEJOINT_EXT_H diff --git a/panda/src/ode/odeSpace_ext.I b/panda/src/ode/odeSpace_ext.I new file mode 100644 index 0000000000..78e9aeb7c3 --- /dev/null +++ b/panda/src/ode/odeSpace_ext.I @@ -0,0 +1,58 @@ +// Filename: odeSpace_ext.I +// Created by: rdb (11Dec13) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +/* okcircular */ +#include "odeGeom_ext.h" + +#ifndef CPPPARSER +IMPORT_THIS struct Dtool_PyTypedObject Dtool_LPoint3f; +#endif + +//////////////////////////////////////////////////////////////////// +// Function: OdeSpace::get_AA_bounds +// Access: Published +// Description: A more Pythonic way of calling getAABB() +//////////////////////////////////////////////////////////////////// +INLINE PyObject *Extension:: +get_AA_bounds() const { + LPoint3f *min_point = new LPoint3f; + LPoint3f *max_point = new LPoint3f; + _this->get_AABB(*min_point, *max_point); + + PyObject *min_inst = DTool_CreatePyInstance((void*) min_point, Dtool_LPoint3f, true, false); + PyObject *max_inst = DTool_CreatePyInstance((void*) max_point, Dtool_LPoint3f, true, false); + return Py_BuildValue("NN", min_inst, max_inst); +} + +//////////////////////////////////////////////////////////////////// +// Function: OdeSpace::get_converted_geom +// Access: Published +// Description: Equivalent to get_geom(index).convert() +//////////////////////////////////////////////////////////////////// +INLINE PyObject *Extension:: +get_converted_geom(int index) const { + OdeGeom g = _this->get_geom(index); + return invoke_extension(&g).convert(); +} + +//////////////////////////////////////////////////////////////////// +// Function: OdeSpace::get_converted_space +// Access: Published +// Description: Equivalent to get_space().convert() +//////////////////////////////////////////////////////////////////// +INLINE PyObject *Extension:: +get_converted_space() const { + OdeSpace s = _this->get_space(); + return invoke_extension(&s).convert(); +} diff --git a/panda/src/ode/odeSpace_ext.cxx b/panda/src/ode/odeSpace_ext.cxx new file mode 100644 index 0000000000..8baa7aabb1 --- /dev/null +++ b/panda/src/ode/odeSpace_ext.cxx @@ -0,0 +1,112 @@ +// Filename: odeSpace_ext.cxx +// Created by: rdb (10Dec13) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +#include "odeSpace_ext.h" +#include "config_ode.h" + +#ifdef HAVE_PYTHON + +#include "odeGeom.h" +#include "odeHashSpace.h" +#include "odeSimpleSpace.h" +#include "odeSpace.h" +#include "odeQuadTreeSpace.h" + +#ifndef CPPPARSER +extern EXPCL_PANDAODE Dtool_PyTypedObject Dtool_OdeHashSpace; +extern EXPCL_PANDAODE Dtool_PyTypedObject Dtool_OdeSimpleSpace; +extern EXPCL_PANDAODE Dtool_PyTypedObject Dtool_OdeSpace; +extern EXPCL_PANDAODE Dtool_PyTypedObject Dtool_OdeQuadTreeSpace; +#endif + +PyObject *Extension::_python_callback = NULL; + +//////////////////////////////////////////////////////////////////// +// Function: OdeSpace::convert +// Access: Published +// Description: Do a sort of pseudo-downcast on this space in +// order to expose its specialized functions. +//////////////////////////////////////////////////////////////////// +PyObject *Extension:: +convert() const { + Dtool_PyTypedObject *class_type; + OdeSpace *space; + + switch (_this->get_class()) { + case OdeGeom::GC_simple_space: + space = new OdeSimpleSpace(_this->get_id()); + class_type = &Dtool_OdeSimpleSpace; + break; + + case OdeGeom::GC_hash_space: + space = new OdeHashSpace(_this->get_id()); + class_type = &Dtool_OdeHashSpace; + break; + + case OdeGeom::GC_quad_tree_space: + space = new OdeQuadTreeSpace(_this->get_id()); + class_type = &Dtool_OdeQuadTreeSpace; + break; + + default: + // This shouldn't happen, but if it does, we + // should just return a regular OdeSpace. + space = new OdeSpace(_this->get_id()); + class_type = &Dtool_OdeSpace; + } + + return DTool_CreatePyInstanceTyped((void *)space, *class_type, + true, false, space->get_type_index()); +} + +int Extension:: +collide(PyObject* arg, PyObject* callback) { + nassertr(callback != NULL, -1); + + if (!PyCallable_Check(callback)) { + PyErr_Format(PyExc_TypeError, "'%s' object is not callable", callback->ob_type->tp_name); + return -1; + + } else if (_this->get_id() == NULL) { + // Well, while we're in the mood of python exceptions, let's make this one too. + PyErr_Format(PyExc_TypeError, "OdeSpace is not valid!"); + return -1; + + } else { + _python_callback = (PyObject*) callback; + Py_XINCREF(_python_callback); + dSpaceCollide(_this->get_id(), (void*) arg, &near_callback); + Py_XDECREF(_python_callback); + return 0; + } +} + +void Extension:: +near_callback(void *data, dGeomID o1, dGeomID o2) { + OdeGeom g1 (o1); + OdeGeom g2 (o2); + PyObject* p1 = invoke_extension(&g1).convert(); + PyObject* p2 = invoke_extension(&g2).convert(); + PyObject *result = PyObject_CallFunctionObjArgs(_python_callback, (PyObject*) data, p1, p2, NULL); + if (!result) { + odespace_cat.error() << "An error occurred while calling python function!\n"; + PyErr_Print(); + } else { + Py_DECREF(result); + } + Py_XDECREF(p2); + Py_XDECREF(p1); +} + +#endif // HAVE_PYTHON diff --git a/panda/src/ode/odeSpace_ext.h b/panda/src/ode/odeSpace_ext.h new file mode 100644 index 0000000000..4ad3be8bca --- /dev/null +++ b/panda/src/ode/odeSpace_ext.h @@ -0,0 +1,54 @@ +// Filename: odeSpace_ext.h +// Created by: rdb (10Dec13) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +#ifndef ODESPACE_EXT_H +#define ODESPACE_EXT_H + +#include "dtoolbase.h" + +#ifdef HAVE_PYTHON + +#include "config_ode.h" +#include "odeSpace.h" +#include "extension.h" +#include "py_panda.h" + +//////////////////////////////////////////////////////////////////// +// Class : Extension +// Description : This class defines the extension methods for +// NodePathCollection, which are called instead of +// any C++ methods with the same prototype. +//////////////////////////////////////////////////////////////////// +template<> +class Extension : public ExtensionBase { +public: + INLINE PyObject *get_AA_bounds() const; + + PyObject *convert() const; + INLINE PyObject *get_converted_geom(int index) const; + INLINE PyObject *get_converted_space() const; + + int collide(PyObject* arg, PyObject* near_callback); + +private: + static void near_callback(void*, dGeomID, dGeomID); + + static PyObject *_python_callback; +}; + +#include "odeSpace_ext.I" + +#endif // HAVE_PYTHON + +#endif // ODESPACE_EXT_H diff --git a/panda/src/ode/odeUtil_ext.cxx b/panda/src/ode/odeUtil_ext.cxx new file mode 100644 index 0000000000..e2910881bf --- /dev/null +++ b/panda/src/ode/odeUtil_ext.cxx @@ -0,0 +1,66 @@ +// Filename: odeUtil_ext.cxx +// Created by: rdb (10Dec13) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +#include "odeUtil_ext.h" +#include "config_ode.h" +#include "odeGeom.h" +#include "odeGeom_ext.h" + +#ifdef HAVE_PYTHON + +PyObject *Extension::_python_callback = NULL; + +//////////////////////////////////////////////////////////////////// +// Function: OdeUtil::collide2 +// Access: Public, Static +// Description: Calls the callback for all potentially intersecting +// pairs that contain one geom from geom1 and one geom +// from geom2. +//////////////////////////////////////////////////////////////////// +int Extension:: +collide2(const OdeGeom &geom1, const OdeGeom &geom2, PyObject* arg, PyObject* callback) { + nassertr(callback != NULL, -1); + if (!PyCallable_Check(callback)) { + PyErr_Format(PyExc_TypeError, "'%s' object is not callable", callback->ob_type->tp_name); + return -1; + } else { + _python_callback = (PyObject*) callback; + Py_XINCREF(_python_callback); + dSpaceCollide2(geom1.get_id(), geom2.get_id(), (void*) arg, &near_callback); + Py_XDECREF(_python_callback); + return 0; + } +} + +void Extension:: +near_callback(void *data, dGeomID o1, dGeomID o2) { + if (ode_cat.is_spam()) { + ode_cat.spam() + << "near_callback called, data: " << data << ", dGeomID1: " << o1 << ", dGeomID2: " << o2 << "\n"; + } + + OdeGeom g1 (o1); + OdeGeom g2 (o2); + PyObject* p1 = invoke_extension(&g1).convert(); + PyObject* p2 = invoke_extension(&g2).convert(); + PyObject* result = PyObject_CallFunctionObjArgs(_python_callback, (PyObject*) data, p1, p2, NULL); + if (!result) { + ode_cat.error() << "An error occurred while calling python function!\n"; + PyErr_Print(); + } + Py_XDECREF(p1); + Py_XDECREF(p2); +} + +#endif // HAVE_PYTHON diff --git a/panda/src/ode/odeUtil_ext.h b/panda/src/ode/odeUtil_ext.h new file mode 100644 index 0000000000..ce78b6d098 --- /dev/null +++ b/panda/src/ode/odeUtil_ext.h @@ -0,0 +1,47 @@ +// Filename: odeUtil_ext.h +// Created by: rdb (10Dec13) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +#ifndef ODEUTIL_EXT_H +#define ODEUTIL_EXT_H + +#include "dtoolbase.h" + +#ifdef HAVE_PYTHON + +#include "config_ode.h" +#include "odeUtil.h" +#include "extension.h" +#include "py_panda.h" + +//////////////////////////////////////////////////////////////////// +// Class : Extension +// Description : This class defines the extension methods for +// NodePathCollection, which are called instead of +// any C++ methods with the same prototype. +//////////////////////////////////////////////////////////////////// +template<> +class Extension : public ExtensionBase { +public: + static int collide2(const OdeGeom &geom1, const OdeGeom &geom2, + PyObject* arg, PyObject* callback); + +private: + static void near_callback(void*, dGeomID, dGeomID); + + static PyObject *_python_callback; +}; + +#endif // HAVE_PYTHON + +#endif // ODEUTIL_EXT_H From fe07bceef9b3dc8a2cab0ac7b9ce577c36e1f8b7 Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 20 Feb 2014 22:02:34 +0000 Subject: [PATCH 32/71] link in ODE for extension functions --- makepanda/makepanda.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index 8d17276a78..dba8756d28 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -4194,7 +4194,7 @@ if (PkgSkip("ODE")==0 and not RUNTIME): TargetAdd('ode.pyd', input='libpandaode.dll') TargetAdd('ode.pyd', input='core.pyd') TargetAdd('ode.pyd', input=COMMON_PANDA_LIBS) - TargetAdd('ode.pyd', opts=['PYTHON']) + TargetAdd('ode.pyd', opts=['PYTHON', 'ODE']) # # DIRECTORY: panda/src/bullet/ From ee56fa43022c2208d3d330f20b7a0e0a8fd4f87f Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 20 Feb 2014 22:20:58 +0000 Subject: [PATCH 33/71] more ode fixes --- makepanda/makepanda.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index dba8756d28..d72a8ece37 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -4158,7 +4158,6 @@ if (PkgSkip("ODE")==0 and not RUNTIME): TargetAdd('p3ode_composite1.obj', opts=OPTS, input='p3ode_composite1.cxx') TargetAdd('p3ode_composite2.obj', opts=OPTS, input='p3ode_composite2.cxx') TargetAdd('p3ode_composite3.obj', opts=OPTS, input='p3ode_composite3.cxx') - TargetAdd('p3ode_ext_composite.obj', opts=OPTS, input='p3ode_ext_composite.cxx') OPTS=['DIR:panda/src/ode', 'ODE'] IGATEFILES=GetDirectoryContents('panda/src/ode', ["*.h", "*_composite*.cxx"]) @@ -4168,6 +4167,7 @@ if (PkgSkip("ODE")==0 and not RUNTIME): TargetAdd('libpandaode.in', opts=OPTS, input=IGATEFILES) TargetAdd('libpandaode.in', opts=['IMOD:ode', 'ILIB:libpandaode', 'SRCDIR:panda/src/ode']) TargetAdd('libpandaode_igate.obj', input='libpandaode.in', opts=["DEPENDENCYONLY"]) + TargetAdd('p3ode_ext_composite.obj', opts=OPTS, input='p3ode_ext_composite.cxx') # # DIRECTORY: panda/metalibs/pandaode/ @@ -4189,12 +4189,12 @@ if (PkgSkip("ODE")==0 and not RUNTIME): TargetAdd('ode_module.obj', opts=['IMOD:ode', 'ILIB:ode']) TargetAdd('ode.pyd', input='ode_module.obj') - TargetAdd('ode.pyd', input='p3ode_ext_composite.obj') TargetAdd('ode.pyd', input='libpandaode_igate.obj') + TargetAdd('ode.pyd', input='p3ode_ext_composite.obj') TargetAdd('ode.pyd', input='libpandaode.dll') TargetAdd('ode.pyd', input='core.pyd') TargetAdd('ode.pyd', input=COMMON_PANDA_LIBS) - TargetAdd('ode.pyd', opts=['PYTHON', 'ODE']) + TargetAdd('ode.pyd', opts=['PYTHON', 'WINUSER', 'ODE']) # # DIRECTORY: panda/src/bullet/ From e14a8319009738b62ebcf87377adbb89111984cd Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 20 Feb 2014 22:32:06 +0000 Subject: [PATCH 34/71] more windows linker problems --- makepanda/makepanda.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index d72a8ece37..f7da629ccd 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -4231,7 +4231,7 @@ if (PkgSkip("BULLET")==0 and not RUNTIME): TargetAdd('bullet.pyd', input='libpandabullet.dll') TargetAdd('bullet.pyd', input='core.pyd') TargetAdd('bullet.pyd', input=COMMON_PANDA_LIBS) - TargetAdd('bullet.pyd', opts=['PYTHON']) + TargetAdd('bullet.pyd', opts=['PYTHON', 'WINUSER', 'BULLET']) # # DIRECTORY: panda/src/physx/ @@ -4270,7 +4270,7 @@ if (PkgSkip("PHYSX")==0): TargetAdd('physx.pyd', input='libpandaphysx.dll') TargetAdd('physx.pyd', input='core.pyd') TargetAdd('physx.pyd', input=COMMON_PANDA_LIBS) - TargetAdd('physx.pyd', opts=['PYTHON']) + TargetAdd('physx.pyd', opts=['PYTHON', 'WINUSER', 'PHYSX', 'NOPPC']) # # DIRECTORY: panda/src/physics/ @@ -4587,7 +4587,7 @@ if (PkgSkip("DIRECT")==0): TargetAdd('direct.pyd', input='libp3direct.dll') TargetAdd('direct.pyd', input='core.pyd') TargetAdd('direct.pyd', input=COMMON_PANDA_LIBS) - TargetAdd('direct.pyd', opts=['PYTHON']) + TargetAdd('direct.pyd', opts=['PYTHON', 'OPENSSL', 'WINUSER', 'WINGDI']) # # DIRECTORY: direct/src/dcparse/ From 607bdac3dca3c87dcd2c3dca3bb33db24a695820 Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 20 Feb 2014 22:53:16 +0000 Subject: [PATCH 35/71] Add missing EXPCL_PANDAPHYSX symbols --- panda/src/physx/physxJointDriveDesc.h | 2 +- panda/src/physx/physxJointLimitDesc.h | 2 +- panda/src/physx/physxJointLimitSoftDesc.h | 2 +- panda/src/physx/physxMotorDesc.h | 2 +- panda/src/physx/physxSpringDesc.h | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/panda/src/physx/physxJointDriveDesc.h b/panda/src/physx/physxJointDriveDesc.h index 73bc7f80a1..991e3d051a 100644 --- a/panda/src/physx/physxJointDriveDesc.h +++ b/panda/src/physx/physxJointDriveDesc.h @@ -25,7 +25,7 @@ // Description : Used to describe drive properties for a // PhysxD6Joint. //////////////////////////////////////////////////////////////////// -class PhysxJointDriveDesc : public PhysxEnums { +class EXPCL_PANDAPHYSX PhysxJointDriveDesc : public PhysxEnums { PUBLISHED: INLINE PhysxJointDriveDesc(); diff --git a/panda/src/physx/physxJointLimitDesc.h b/panda/src/physx/physxJointLimitDesc.h index aedd83ec29..3665fa4cf7 100644 --- a/panda/src/physx/physxJointLimitDesc.h +++ b/panda/src/physx/physxJointLimitDesc.h @@ -23,7 +23,7 @@ // Class : PhysxJointLimitDesc // Description : Describes a joint limit. //////////////////////////////////////////////////////////////////// -class PhysxJointLimitDesc { +class EXPCL_PANDAPHYSX PhysxJointLimitDesc { PUBLISHED: INLINE PhysxJointLimitDesc(); diff --git a/panda/src/physx/physxJointLimitSoftDesc.h b/panda/src/physx/physxJointLimitSoftDesc.h index 52c78b7331..1d5f9c7b62 100644 --- a/panda/src/physx/physxJointLimitSoftDesc.h +++ b/panda/src/physx/physxJointLimitSoftDesc.h @@ -23,7 +23,7 @@ // Class : PhysxJointLimitSoftDesc // Description : Describes a joint limit. //////////////////////////////////////////////////////////////////// -class PhysxJointLimitSoftDesc { +class EXPCL_PANDAPHYSX PhysxJointLimitSoftDesc { PUBLISHED: INLINE PhysxJointLimitSoftDesc(); diff --git a/panda/src/physx/physxMotorDesc.h b/panda/src/physx/physxMotorDesc.h index 4ad55a1671..fd3595fba1 100644 --- a/panda/src/physx/physxMotorDesc.h +++ b/panda/src/physx/physxMotorDesc.h @@ -28,7 +28,7 @@ // - PhysxPulleyJoint // - PhysxRevoluteJoint //////////////////////////////////////////////////////////////////// -class PhysxMotorDesc { +class EXPCL_PANDAPHYSX PhysxMotorDesc { PUBLISHED: INLINE PhysxMotorDesc(); diff --git a/panda/src/physx/physxSpringDesc.h b/panda/src/physx/physxSpringDesc.h index 29d85ebcf1..f695a66c0c 100644 --- a/panda/src/physx/physxSpringDesc.h +++ b/panda/src/physx/physxSpringDesc.h @@ -25,7 +25,7 @@ // integrated, so even high spring and damper // coefficients should be robust. //////////////////////////////////////////////////////////////////// -class PhysxSpringDesc { +class EXPCL_PANDAPHYSX PhysxSpringDesc { PUBLISHED: INLINE PhysxSpringDesc(); From a58ddb3cfa6e1e1884186477db0d248a18588d84 Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 20 Feb 2014 23:10:59 +0000 Subject: [PATCH 36/71] Add missing EXPCL_PANDAPHYSICS --- .../src/particlesystem/colorInterpolationManager.h | 14 +++++++------- panda/src/physics/linearUserDefinedForce.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/panda/src/particlesystem/colorInterpolationManager.h b/panda/src/particlesystem/colorInterpolationManager.h index e43ecdb2f8..41fb390f9a 100755 --- a/panda/src/particlesystem/colorInterpolationManager.h +++ b/panda/src/particlesystem/colorInterpolationManager.h @@ -27,7 +27,7 @@ // function. //////////////////////////////////////////////////////////////////// -class ColorInterpolationFunction : public TypedReferenceCount { +class EXPCL_PANDAPHYSICS ColorInterpolationFunction : public TypedReferenceCount { PUBLISHED: // virtual string get_type(); @@ -62,7 +62,7 @@ private: // the segment. //////////////////////////////////////////////////////////////////// -class ColorInterpolationFunctionConstant : public ColorInterpolationFunction { +class EXPCL_PANDAPHYSICS ColorInterpolationFunctionConstant : public ColorInterpolationFunction { PUBLISHED: INLINE LColor get_color_a() const; @@ -103,7 +103,7 @@ private: // the segment. //////////////////////////////////////////////////////////////////// -class ColorInterpolationFunctionLinear : public ColorInterpolationFunctionConstant { +class EXPCL_PANDAPHYSICS ColorInterpolationFunctionLinear : public ColorInterpolationFunctionConstant { PUBLISHED: INLINE LColor get_color_b() const; @@ -147,7 +147,7 @@ private: // the end of the segment. //////////////////////////////////////////////////////////////////// -class ColorInterpolationFunctionStepwave : public ColorInterpolationFunctionLinear { +class EXPCL_PANDAPHYSICS ColorInterpolationFunctionStepwave : public ColorInterpolationFunctionLinear { PUBLISHED: INLINE PN_stdfloat get_width_a() const; INLINE PN_stdfloat get_width_b() const; @@ -195,7 +195,7 @@ private: // cycle. //////////////////////////////////////////////////////////////////// -class ColorInterpolationFunctionSinusoid : public ColorInterpolationFunctionLinear { +class EXPCL_PANDAPHYSICS ColorInterpolationFunctionSinusoid : public ColorInterpolationFunctionLinear { PUBLISHED: INLINE PN_stdfloat get_period() const; @@ -238,7 +238,7 @@ private: // also has a function associated with it. //////////////////////////////////////////////////////////////////// -class ColorInterpolationSegment : public ReferenceCount { +class EXPCL_PANDAPHYSICS ColorInterpolationSegment : public ReferenceCount { PUBLISHED: ColorInterpolationSegment(ColorInterpolationFunction* function, const PN_stdfloat &time_begin, const PN_stdfloat &time_end, const bool is_modulated, const int id); ColorInterpolationSegment(const ColorInterpolationSegment &s); @@ -280,7 +280,7 @@ protected: // general use. //////////////////////////////////////////////////////////////////// -class ColorInterpolationManager : public ReferenceCount { +class EXPCL_PANDAPHYSICS ColorInterpolationManager : public ReferenceCount { PUBLISHED: ColorInterpolationManager(); ColorInterpolationManager(const LColor &c); diff --git a/panda/src/physics/linearUserDefinedForce.h b/panda/src/physics/linearUserDefinedForce.h index 51dd6f74ce..2a65583bc7 100644 --- a/panda/src/physics/linearUserDefinedForce.h +++ b/panda/src/physics/linearUserDefinedForce.h @@ -27,7 +27,7 @@ // in the makefile when the time is right or this class // becomes needed... //////////////////////////////////////////////////////////////////// -class LinearUserDefinedForce : public LinearForce { +class EXPCL_PANDAPHYSICS LinearUserDefinedForce : public LinearForce { PUBLISHED: LinearUserDefinedForce(LVector3 (*proc)(const PhysicsObject *) = NULL, PN_stdfloat a = 1.0f, From 46450f279836cd8f9a33d0f1de4d9a915bb6e5de Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 22 Feb 2014 10:56:13 +0000 Subject: [PATCH 37/71] Delete panda3d.py on installation if it exists --- direct/src/directscripts/packpanda.nsi | 3 +++ 1 file changed, 3 insertions(+) diff --git a/direct/src/directscripts/packpanda.nsi b/direct/src/directscripts/packpanda.nsi index a06ef84ce8..a57514c799 100755 --- a/direct/src/directscripts/packpanda.nsi +++ b/direct/src/directscripts/packpanda.nsi @@ -111,6 +111,9 @@ Section "${SMDIRECTORY}" SecCore File /r /x CVS /x Opt?-Win32 "${PSOURCE}\direct\src\*.py" File "${PANDA}\tmp\__init__.py" !endif + Delete "$INSTDIR\panda3d.py" + Delete "$INSTDIR\panda3d.pyc" + Delete "$INSTDIR\panda3d.pyo" SetOutPath $INSTDIR\pandac File /r "${PANDA}\pandac\*.py" SetOutPath $INSTDIR\panda3d From b0c508827b176a4b44b547f3625c7c70689a2aa8 Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 22 Feb 2014 11:22:06 +0000 Subject: [PATCH 38/71] Fix installation of new panda3d tree --- makepanda/installpanda.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/makepanda/installpanda.py b/makepanda/installpanda.py index 1163da7a38..d619a106d8 100644 --- a/makepanda/installpanda.py +++ b/makepanda/installpanda.py @@ -125,7 +125,6 @@ def InstallPanda(destdir="", prefix="/usr", outputdir="built"): oscmd("mkdir -m 0755 -p "+destdir+prefix+"/bin") oscmd("mkdir -m 0755 -p "+destdir+prefix+"/include") oscmd("mkdir -m 0755 -p "+destdir+prefix+"/share/panda3d") - oscmd("mkdir -m 0755 -p "+destdir+prefix+"/share/panda3d/direct") oscmd("mkdir -m 0755 -p "+destdir+prefix+"/share/mime-info") oscmd("mkdir -m 0755 -p "+destdir+prefix+"/share/mime/packages") oscmd("mkdir -m 0755 -p "+destdir+prefix+"/share/application-registry") @@ -147,13 +146,13 @@ def InstallPanda(destdir="", prefix="/usr", outputdir="built"): WriteFile(destdir+"/etc/Config.prc", Configrc) oscmd("cp "+outputdir+"/etc/Confauto.prc "+destdir+"/etc/Confauto.prc") oscmd("cp -R "+outputdir+"/include "+destdir+prefix+"/include/panda3d") - oscmd("cp -R direct/src/* "+destdir+prefix+"/share/panda3d/direct") - oscmd("cp -R "+outputdir+"/pandac "+destdir+prefix+"/share/panda3d/pandac") - oscmd("cp -R "+outputdir+"/models "+destdir+prefix+"/share/panda3d/models") - oscmd("cp direct/src/ffi/panda3d.py "+destdir+prefix+"/share/panda3d/panda3d.py") - if os.path.isdir("samples"): oscmd("cp -R samples "+destdir+prefix+"/share/panda3d/samples") - if os.path.isdir(outputdir+"/Pmw"): oscmd("cp -R "+outputdir+"/Pmw "+destdir+prefix+"/share/panda3d/Pmw") - if os.path.isdir(outputdir+"/plugins"): oscmd("cp -R "+outputdir+"/plugins "+destdir+prefix+"/share/panda3d/plugins") + oscmd("cp -R "+outputdir+"/direct "+destdir+prefix+"/share/panda3d/") + oscmd("cp -R "+outputdir+"/pandac "+destdir+prefix+"/share/panda3d/") + oscmd("cp -R "+outputdir+"/panda3d "+destdir+PPATH+"/") + oscmd("cp -R "+outputdir+"/models "+destdir+prefix+"/share/panda3d/") + if os.path.isdir("samples"): oscmd("cp -R samples "+destdir+prefix+"/share/panda3d/") + if os.path.isdir(outputdir+"/Pmw"): oscmd("cp -R "+outputdir+"/Pmw "+destdir+prefix+"/share/panda3d/") + if os.path.isdir(outputdir+"/plugins"): oscmd("cp -R "+outputdir+"/plugins "+destdir+prefix+"/share/panda3d/") WriteMimeFile(destdir+prefix+"/share/mime-info/panda3d.mime", MIME_INFO) WriteKeysFile(destdir+prefix+"/share/mime-info/panda3d.keys", MIME_INFO) WriteMimeXMLFile(destdir+prefix+"/share/mime/packages/panda3d.xml", MIME_INFO) From 3ac369a3b06ee20e2f7738a034b7ccc02ed847d9 Mon Sep 17 00:00:00 2001 From: enn0x Date: Sun, 23 Feb 2014 09:49:12 +0000 Subject: [PATCH 39/71] Fix for bug #1282477 --- panda/src/bullet/bulletHelper.cxx | 28 +++++++++++++++++-------- panda/src/bullet/bulletSoftBodyConfig.h | 2 +- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/panda/src/bullet/bulletHelper.cxx b/panda/src/bullet/bulletHelper.cxx index c5b591bba3..61927dc24b 100644 --- a/panda/src/bullet/bulletHelper.cxx +++ b/panda/src/bullet/bulletHelper.cxx @@ -220,9 +220,11 @@ make_geom(BulletSoftBodyNode *node, const GeomVertexFormat *format, bool two_sid if (two_sided) { for (int j=0; jm_faces); prim = new GeomTriangles(Geom::UH_stream); prim->set_shade_model(Geom::SM_uniform); for (int j=0; jadd_vertices(int(faces[j].m_n[0] - node0), - int(faces[j].m_n[1] - node0), - int(faces[j].m_n[2] - node0)); + i0 = int(faces[j].m_n[0] - node0); + i1 = int(faces[j].m_n[1] - node0); + i2 = int(faces[j].m_n[2] - node0); + + prim->add_vertices(i0, i1, i2); prim->close_primitive(); if (two_sided) { - prim->add_vertices(nodes.size() + int(faces[j].m_n[0] - node0), - nodes.size() + int(faces[j].m_n[2] - node0), - nodes.size() + int(faces[j].m_n[1] - node0)); + i0 = nodes.size() + int(faces[j].m_n[0] - node0); + i1 = nodes.size() + int(faces[j].m_n[2] - node0); + i2 = nodes.size() + int(faces[j].m_n[1] - node0); + + prim->add_vertices(i0, i1, i2); prim->close_primitive(); } } @@ -259,8 +267,10 @@ make_geom(BulletSoftBodyNode *node, const GeomVertexFormat *format, bool two_sid prim->set_shade_model(Geom::SM_uniform); for (int j=0; jadd_vertices(int(links[j].m_n[0] - node0), - int(links[j].m_n[1] - node0)); + i0 = int(links[j].m_n[0] - node0); + i1 = int(links[j].m_n[1] - node0); + + prim->add_vertices(i0, i1); prim->close_primitive(); } } diff --git a/panda/src/bullet/bulletSoftBodyConfig.h b/panda/src/bullet/bulletSoftBodyConfig.h index 8f2d47f052..22505c8358 100644 --- a/panda/src/bullet/bulletSoftBodyConfig.h +++ b/panda/src/bullet/bulletSoftBodyConfig.h @@ -29,7 +29,7 @@ PUBLISHED: INLINE ~BulletSoftBodyConfig(); enum CollisionFlag { - CF_rigid__vs_soft_mask = 0x000f, // RVSmask: Rigid versus soft mask + CF_rigid_vs_soft_mask = 0x000f, // RVSmask: Rigid versus soft mask CF_sdf_rigid_soft = 0x0001, // SDF_RS: SDF based rigid vs soft CF_cluster_rigid_soft = 0x0002, // CL_RS: Cluster vs convex rigid vs soft CF_soft_vs_soft_mask = 0x0030, // SVSmask: Soft versus soft mask From ab395f0ec5cf92111a090c2c21e22da57c72259e Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 24 Feb 2014 15:09:46 +0000 Subject: [PATCH 40/71] fix broken line I forgot to remove. --- makepanda/installpanda.py | 1 - 1 file changed, 1 deletion(-) diff --git a/makepanda/installpanda.py b/makepanda/installpanda.py index d619a106d8..4833682bae 100644 --- a/makepanda/installpanda.py +++ b/makepanda/installpanda.py @@ -136,7 +136,6 @@ def InstallPanda(destdir="", prefix="/usr", outputdir="built"): oscmd("mkdir -m 0755 -p "+destdir+"/usr/local/libdata/ldconfig") else: oscmd("mkdir -m 0755 -p "+destdir+"/etc/ld.so.conf.d") - WriteFile(destdir+prefix+"/share/panda3d/direct/__init__.py", "") Configrc = ReadFile(outputdir+"/etc/Config.prc") Configrc = Configrc.replace("model-path $THIS_PRC_DIR/..", "model-path "+prefix+"/share/panda3d") if (sys.platform.startswith("freebsd")): From c15ce9885fe6e8b727a55965f66da86f81b1bccf Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 24 Feb 2014 15:22:20 +0000 Subject: [PATCH 41/71] Expose HierarchyMatchFlags to Python --- panda/src/chan/partGroup.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panda/src/chan/partGroup.h b/panda/src/chan/partGroup.h index a32540bb86..1ba23978d9 100644 --- a/panda/src/chan/partGroup.h +++ b/panda/src/chan/partGroup.h @@ -43,7 +43,7 @@ class AnimChannelBase; // MovingPart. It defines a hierarchy of MovingParts. //////////////////////////////////////////////////////////////////// class EXPCL_PANDA_CHAN PartGroup : public TypedWritableReferenceCount, public Namable { -public: +PUBLISHED: // This enum defines bits which may be passed into check_hierarchy() // and PartBundle::bind_anim() to allow an inexact match of channel // hierarchies. This specifies conditions that we don't care about From 52f1abeeae568de97f2bb060f0dc5a2c3542527e Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 25 Feb 2014 07:52:32 +0000 Subject: [PATCH 42/71] Remove French accent encoded in who-knows-what --- panda/src/tinydisplay/zmath.cxx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/panda/src/tinydisplay/zmath.cxx b/panda/src/tinydisplay/zmath.cxx index fba1751c3b..e71666084c 100644 --- a/panda/src/tinydisplay/zmath.cxx +++ b/panda/src/tinydisplay/zmath.cxx @@ -140,7 +140,7 @@ int Matrix_Inv(PN_stdfloat *r,PN_stdfloat *m,int n) int i,j,k,l; PN_stdfloat max,tmp,t; - /* identitée dans r */ + /* identite dans r */ for(i=0;i Date: Wed, 26 Feb 2014 01:57:28 +0000 Subject: [PATCH 43/71] Add a check to make sure the method has a __name__ member before trying to print it --- direct/src/showbase/Messenger.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/direct/src/showbase/Messenger.py b/direct/src/showbase/Messenger.py index 44248e6a4e..6e45fb088c 100644 --- a/direct/src/showbase/Messenger.py +++ b/direct/src/showbase/Messenger.py @@ -634,7 +634,10 @@ class Messenger: functionName = method.im_class.__name__ + '.' + \ method.im_func.__name__ else: - functionName = method.__name__ + if hasattr(method, "__name__"): + functionName = method.__name__ + else: + return "" return functionName def __eventRepr(self, event): From 79430f6bc584d90fc5ce621663d58e4dd152282e Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 27 Feb 2014 11:30:56 +0000 Subject: [PATCH 44/71] Fixes for Python 3 --- direct/src/actor/Actor.py | 13 +++++-------- direct/src/interval/FunctionInterval.py | 5 +++-- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/direct/src/actor/Actor.py b/direct/src/actor/Actor.py index 53eaf3808d..60bd7df8dc 100644 --- a/direct/src/actor/Actor.py +++ b/direct/src/actor/Actor.py @@ -566,7 +566,7 @@ class Actor(DirectObject, NodePath): # and sort them every time somebody asks for the list self.__sortedLODNames = self.__partBundleDict.keys() # Reverse sort the doing a string->int - def sortFunc(x, y): + def sortKey(x): if not str(x).isdigit(): smap = {'h':3, 'm':2, @@ -574,19 +574,16 @@ class Actor(DirectObject, NodePath): 'f':0} """ - sx = smap.get(x[0],None) - sy = smap.get(y[0],None) + sx = smap.get(x[0], None) if sx is None: self.notify.error('Invalid lodName: %s' % x) - if sy is None: - self.notify.error('Invalid lodName: %s' % y) """ - return cmp(smap[y[0]], smap[x[0]]) + return smap[x[0]] else: - return cmp (int(y), int(x)) + return int(x) - self.__sortedLODNames.sort(sortFunc) + self.__sortedLODNames.sort(key=sortKey, reverse=True) def getLODNames(self): """ diff --git a/direct/src/interval/FunctionInterval.py b/direct/src/interval/FunctionInterval.py index 450cb5d5de..fd46bad994 100644 --- a/direct/src/interval/FunctionInterval.py +++ b/direct/src/interval/FunctionInterval.py @@ -57,9 +57,10 @@ class FunctionInterval(Interval.Interval): self.function = function # Create a unique name for the interval if necessary - if (name == None): + if name is None: name = self.makeUniqueName(function) - assert isinstance(name, types.StringType) + assert isinstance(name, str) + # Record any arguments self.extraArgs = extraArgs self.kw = kw From ae820a30bcb3169f2aaed4cf2d6febb066c5f3a4 Mon Sep 17 00:00:00 2001 From: enn0x Date: Thu, 27 Feb 2014 20:57:23 +0000 Subject: [PATCH 45/71] Cleaning up BulletContact (API changes only in C++) --- panda/src/bullet/bulletContactResult.I | 29 +++++++-------- panda/src/bullet/bulletContactResult.cxx | 47 +++++++++++++++++++++--- panda/src/bullet/bulletContactResult.h | 18 ++++++--- panda/src/bullet/bulletManifoldPoint.cxx | 22 +++++++++++ panda/src/bullet/bulletManifoldPoint.h | 3 ++ 5 files changed, 93 insertions(+), 26 deletions(-) diff --git a/panda/src/bullet/bulletContactResult.I b/panda/src/bullet/bulletContactResult.I index a0f20d54a6..fe78cedbe7 100644 --- a/panda/src/bullet/bulletContactResult.I +++ b/panda/src/bullet/bulletContactResult.I @@ -14,41 +14,40 @@ //////////////////////////////////////////////////////////////////// -// Function: BulletContactResult::get_node0 +// Function: BulletContact::get_node0 // Access: Published // Description: //////////////////////////////////////////////////////////////////// INLINE PandaNode *BulletContact:: get_node0() const { - return _obj0 ? (PandaNode *)_obj0->getUserPointer() : NULL; + return _node0; } //////////////////////////////////////////////////////////////////// -// Function: BulletContactResult::get_node1 +// Function: BulletContact::get_node1 // Access: Published // Description: //////////////////////////////////////////////////////////////////// INLINE PandaNode *BulletContact:: get_node1() const { - return _obj1 ? (PandaNode *)_obj1->getUserPointer() : NULL; + return _node1; } //////////////////////////////////////////////////////////////////// -// Function: BulletContactResult::get_manifold_point +// Function: BulletContact::get_manifold_point // Access: Published // Description: //////////////////////////////////////////////////////////////////// -INLINE const BulletManifoldPoint *BulletContact:: -get_manifold_point() const { +INLINE BulletManifoldPoint &BulletContact:: +get_manifold_point() { - btManifoldPoint &mp = const_cast(_mp); - return new BulletManifoldPoint(mp); + return _mp; } //////////////////////////////////////////////////////////////////// -// Function: BulletContactResult::get_idx0 +// Function: BulletContact::get_idx0 // Access: Published // Description: //////////////////////////////////////////////////////////////////// @@ -59,7 +58,7 @@ get_idx0() const { } //////////////////////////////////////////////////////////////////// -// Function: BulletContactResult::get_idx1 +// Function: BulletContact::get_idx1 // Access: Published // Description: //////////////////////////////////////////////////////////////////// @@ -70,7 +69,7 @@ get_idx1() const { } //////////////////////////////////////////////////////////////////// -// Function: BulletContactResult::get_part_id0 +// Function: BulletContact::get_part_id0 // Access: Published // Description: //////////////////////////////////////////////////////////////////// @@ -81,7 +80,7 @@ get_part_id0() const { } //////////////////////////////////////////////////////////////////// -// Function: BulletContactResult::get_part_id1 +// Function: BulletContact::get_part_id1 // Access: Published // Description: //////////////////////////////////////////////////////////////////// @@ -107,8 +106,8 @@ get_num_contacts() const { // Access: Published // Description: //////////////////////////////////////////////////////////////////// -INLINE const BulletContact &BulletContactResult:: -get_contact(int idx) const { +INLINE BulletContact &BulletContactResult:: +get_contact(int idx) { nassertr(idx >= 0 && idx < (int)_contacts.size(), _empty); return _contacts[idx]; diff --git a/panda/src/bullet/bulletContactResult.cxx b/panda/src/bullet/bulletContactResult.cxx index f111667d31..a55f0c3668 100644 --- a/panda/src/bullet/bulletContactResult.cxx +++ b/panda/src/bullet/bulletContactResult.cxx @@ -14,8 +14,37 @@ #include "bulletContactResult.h" +btManifoldPoint BulletContact::_empty; BulletContact BulletContactResult::_empty; +//////////////////////////////////////////////////////////////////// +// Function: BulletContact::Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +BulletContact:: +BulletContact() : _mp(_empty) { + + _node0 = NULL; + _node1 = NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: BulletContact::Copy Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +BulletContact:: +BulletContact(const BulletContact &other) : _mp(other._mp) { + + _node0 = other._node0; + _node1 = other._node1; + _part_id0 = other._part_id0; + _part_id1 = other._part_id1; + _idx0 = other._idx0; + _idx1 = other._idx1; +} + //////////////////////////////////////////////////////////////////// // Function: BulletContactResult::Constructor // Access: Protected @@ -74,11 +103,14 @@ addSingleResult(btManifoldPoint &mp, const btCollisionObjectWrapper *wrap0, int part_id0, int idx0, const btCollisionObjectWrapper *wrap1, int part_id1, int idx1) { + const btCollisionObject *obj0 = wrap0->getCollisionObject(); + const btCollisionObject *obj1 = wrap1->getCollisionObject(); + BulletContact contact; - contact._mp = mp; - contact._obj0 = wrap0->getCollisionObject(); - contact._obj1 = wrap1->getCollisionObject(); + contact._mp = BulletManifoldPoint(mp); + contact._node0 = obj0 ? (PandaNode *)obj0->getUserPointer() : NULL; + contact._node1 = obj1 ? (PandaNode *)obj1->getUserPointer() : NULL; contact._part_id0 = part_id0; contact._part_id1 = part_id1; contact._idx0 = idx0; @@ -99,11 +131,14 @@ addSingleResult(btManifoldPoint &mp, const btCollisionObject *obj0, int part_id0, int idx0, const btCollisionObject *obj1, int part_id1, int idx1) { + const btCollisionObject *obj0 = wrap0->getCollisionObject(); + const btCollisionObject *obj1 = wrap1->getCollisionObject(); + BulletContact contact; - contact._mp = mp; - contact._obj0 = obj0; - contact._obj1 = obj1; + contact._mp = BulletManifoldPoint(mp); + contact._node0 = obj0 ? (PandaNode *)obj0->getUserPointer() : NULL; + contact._node1 = obj1 ? (PandaNode *)obj1->getUserPointer() : NULL; contact._part_id0 = part_id0; contact._part_id1 = part_id1; contact._idx0 = idx0; diff --git a/panda/src/bullet/bulletContactResult.h b/panda/src/bullet/bulletContactResult.h index 56bbe54396..6de4962ae5 100644 --- a/panda/src/bullet/bulletContactResult.h +++ b/panda/src/bullet/bulletContactResult.h @@ -28,8 +28,12 @@ //////////////////////////////////////////////////////////////////// struct EXPCL_PANDABULLET BulletContact { +public: + BulletContact(); + BulletContact(const BulletContact &other); + PUBLISHED: - INLINE const BulletManifoldPoint *get_manifold_point() const; + INLINE BulletManifoldPoint &get_manifold_point(); INLINE PandaNode *get_node0() const; INLINE PandaNode *get_node1() const; INLINE const int get_idx0() const; @@ -38,9 +42,13 @@ PUBLISHED: INLINE const int get_part_id1() const; private: - btManifoldPoint _mp; - const btCollisionObject *_obj0; - const btCollisionObject *_obj1; + static btManifoldPoint _empty; + + BulletManifoldPoint _mp; + + PT(PandaNode) _node0; + PT(PandaNode) _node1; + int _part_id0; int _part_id1; int _idx0; @@ -57,7 +65,7 @@ struct EXPCL_PANDABULLET BulletContactResult : public btCollisionWorld::ContactR PUBLISHED: INLINE int get_num_contacts() const; - INLINE const BulletContact &get_contact(int idx) const; + INLINE BulletContact &get_contact(int idx); MAKE_SEQ(get_contacts, get_num_contacts, get_contact); public: diff --git a/panda/src/bullet/bulletManifoldPoint.cxx b/panda/src/bullet/bulletManifoldPoint.cxx index df43a45340..509a36b9cc 100644 --- a/panda/src/bullet/bulletManifoldPoint.cxx +++ b/panda/src/bullet/bulletManifoldPoint.cxx @@ -25,6 +25,28 @@ BulletManifoldPoint(btManifoldPoint &pt) } +//////////////////////////////////////////////////////////////////// +// Function: BulletManifoldPoint::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +BulletManifoldPoint:: +BulletManifoldPoint(const BulletManifoldPoint &other) + : _pt(other._pt) { + +} + +//////////////////////////////////////////////////////////////////// +// Function: BulletManifoldPoint::Copy Assignment +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +BulletManifoldPoint& BulletManifoldPoint:: +operator=(const BulletManifoldPoint& other) { + + return *this; +} + //////////////////////////////////////////////////////////////////// // Function: BulletManifoldPoint::get_lift_time // Access: Published diff --git a/panda/src/bullet/bulletManifoldPoint.h b/panda/src/bullet/bulletManifoldPoint.h index 95e67def6e..672a8db282 100644 --- a/panda/src/bullet/bulletManifoldPoint.h +++ b/panda/src/bullet/bulletManifoldPoint.h @@ -73,6 +73,9 @@ PUBLISHED: public: BulletManifoldPoint(btManifoldPoint &pt); + BulletManifoldPoint(const BulletManifoldPoint &other); + BulletManifoldPoint& operator=(const BulletManifoldPoint& other); + private: btManifoldPoint &_pt; }; From 984314970fa08c3b55043cd6d800eff95c8ee556 Mon Sep 17 00:00:00 2001 From: Chris Brunner Date: Fri, 28 Feb 2014 21:36:49 +0000 Subject: [PATCH 46/71] With maya notify level set to debug, print out some useful light information if found in the scene, even though it will never make it into the egg file. --- pandatool/src/mayaegg/mayaToEggConverter.cxx | 56 +++++++++++++++----- 1 file changed, 44 insertions(+), 12 deletions(-) diff --git a/pandatool/src/mayaegg/mayaToEggConverter.cxx b/pandatool/src/mayaegg/mayaToEggConverter.cxx index 64f09cdcfc..b9b979d66c 100644 --- a/pandatool/src/mayaegg/mayaToEggConverter.cxx +++ b/pandatool/src/mayaegg/mayaToEggConverter.cxx @@ -969,7 +969,6 @@ process_model_node(MayaNodeDesc *node_desc) { << "Ignoring light node " << path << "\n"; } - /* MFnLight light (dag_path, &status); if ( !status ) { @@ -977,26 +976,59 @@ process_model_node(MayaNodeDesc *node_desc) { mayaegg_cat.error() << "light extraction failed" << endl; return false; } + mayaegg_cat.debug() << "-- Light found -- tranlations in cm, rotations in rads\n"; + + mayaegg_cat.debug() << "\"" << dag_path.partialPathName() << "\" : \n"; // Get the translation/rotation/scale data - //printTransformData(dag_path, quiet); + MObject transformNode = dag_path.transform(&status); + // This node has no transform - i.e., it's the world node + if (!status && status.statusCode () == MStatus::kInvalidParameter) + return false; + MFnDagNode transform (transformNode, &status); + if (!status) { + status.perror("MFnDagNode constructor"); + return false; + } + MTransformationMatrix matrix (transform.transformationMatrix()); + MVector tl = matrix.getTranslation(MSpace::kWorld); + // Stop rediculously small values like -4.43287e-013 + if (tl.x < 0.0001) { + tl.x = 0; + } + if (tl.y < 0.0001) { + tl.y = 0; + } + if (tl.z < 0.0001) { + tl.z = 0; + } + // We swap Y and Z in the next few bits cuz Panda is Z-up by default and Maya is Y-up + mayaegg_cat.debug() << " \"translation\" : (" << tl.x << ", " << tl.z << ", " << tl.y << ")" + << endl; + double threeDoubles[3]; + MTransformationMatrix::RotationOrder rOrder; + + matrix.getRotation (threeDoubles, rOrder, MSpace::kWorld); + mayaegg_cat.debug() << " \"rotation\": (" + << threeDoubles[0] << ", " + << threeDoubles[2] << ", " + << threeDoubles[1] << ")\n"; + matrix.getScale (threeDoubles, MSpace::kWorld); + mayaegg_cat.debug() << " \"scale\" : (" + << threeDoubles[0] << ", " + << threeDoubles[2] << ", " + << threeDoubles[1] << ")\n"; // Extract some interesting Light data MColor color; - color = light.color(); - cout << " color: [" + mayaegg_cat.debug() << " \"color\" : (" << color.r << ", " << color.g << ", " - << color.b << "]\n"; + << color.b << ")\n"; color = light.shadowColor(); - cout << " shadowColor: [" - << color.r << ", " - << color.g << ", " - << color.b << "]\n"; - - cout << " intensity: " << light.intensity() << endl; - */ + mayaegg_cat.debug() << " \"intensity\" : " << light.intensity() << endl; + } else if (dag_path.hasFn(MFn::kNurbsSurface)) { EggGroup *egg_group = _tree.get_egg_group(node_desc); get_transform(node_desc, dag_path, egg_group); From 33f4aa520dbe14c1a7aae752c60194c2c7ec6447 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 4 Mar 2014 08:29:32 +0000 Subject: [PATCH 47/71] Normal maps should affect aux normal even without lighting --- panda/src/pgraphnodes/shaderGenerator.cxx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/panda/src/pgraphnodes/shaderGenerator.cxx b/panda/src/pgraphnodes/shaderGenerator.cxx index c0422d1aa2..7d199a056c 100644 --- a/panda/src/pgraphnodes/shaderGenerator.cxx +++ b/panda/src/pgraphnodes/shaderGenerator.cxx @@ -669,7 +669,7 @@ synthesize_shader(const RenderState *rs) { } } - if ((_map_index_normal == i && _lighting && _auto_normal_on) || _map_index_height == i) { + if ((_map_index_normal == i && (_lighting || _out_aux_normal) && _auto_normal_on) || _map_index_height == i) { const InternalName *texcoord_name = stage->get_texcoord_name(); PT(InternalName) tangent_name = InternalName::get_tangent(); PT(InternalName) binormal_name = InternalName::get_binormal(); @@ -684,7 +684,7 @@ synthesize_shader(const RenderState *rs) { text << "\t in float4 vtx_" << tangent_input << " : " << alloc_vreg() << ",\n"; text << "\t in float4 vtx_" << binormal_input << " : " << alloc_vreg() << ",\n"; - if (_map_index_normal == i && _lighting && _auto_normal_on) { + if (_map_index_normal == i && (_lighting || _out_aux_normal) && _auto_normal_on) { tangent_freg = alloc_freg(); binormal_freg = alloc_freg(); text << "\t out float4 l_tangent : " << tangent_freg << ",\n"; @@ -781,7 +781,7 @@ synthesize_shader(const RenderState *rs) { if (_vertex_colors) { text << "\t l_color = vtx_color;\n"; } - if (_lighting && (_map_index_normal >= 0 && _auto_normal_on)) { + if ((_lighting || _out_aux_normal) && (_map_index_normal >= 0 && _auto_normal_on)) { text << "\t l_tangent.xyz = mul((float3x3)tpose_view_to_model, vtx_" << tangent_input << ".xyz);\n"; text << "\t l_tangent.w = 0;\n"; text << "\t l_binormal.xyz = mul((float3x3)tpose_view_to_model, -vtx_" << binormal_input << ".xyz);\n"; @@ -842,7 +842,7 @@ synthesize_shader(const RenderState *rs) { text << "\t uniform float4x4 texmat_" << i << ",\n"; } } - if (_lighting && (_map_index_normal >= 0 && _auto_normal_on)) { + if ((_lighting || _out_aux_normal) && (_map_index_normal >= 0 && _auto_normal_on)) { text << "\t in float3 l_tangent : " << tangent_freg << ",\n"; text << "\t in float3 l_binormal : " << binormal_freg << ",\n"; } @@ -1014,7 +1014,7 @@ synthesize_shader(const RenderState *rs) { text << ");\n"; } } - if (_lighting) { + if (_lighting || _out_aux_normal) { if (_map_index_normal >= 0 && _auto_normal_on) { text << "\t // Translate tangent-space normal in map to view-space.\n"; text << "\t float3 tsnormal = ((float3)tex" << _map_index_normal << " * 2) - 1;\n"; From 82eb82d026b7154da76e2ce366d972d382ff1473 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 5 Mar 2014 15:23:12 +0000 Subject: [PATCH 48/71] Restructure code slightly to reduce code duplication, fix GLES compile errors --- panda/src/glstuff/glGraphicsBuffer_src.cxx | 175 +++++++++------------ panda/src/glstuff/glGraphicsBuffer_src.h | 1 + 2 files changed, 77 insertions(+), 99 deletions(-) diff --git a/panda/src/glstuff/glGraphicsBuffer_src.cxx b/panda/src/glstuff/glGraphicsBuffer_src.cxx index a036d703ca..1ae44e9194 100644 --- a/panda/src/glstuff/glGraphicsBuffer_src.cxx +++ b/panda/src/glstuff/glGraphicsBuffer_src.cxx @@ -559,15 +559,10 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot, } if (tex) { - GLenum target = glgsg->get_texture_target(tex->get_texture_type()); - if (target == GL_TEXTURE_CUBE_MAP) { - target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + layer; - } - // Bind the texture to the slot. tex->set_x_size(_rb_size_x); tex->set_y_size(_rb_size_y); - if (target != GL_TEXTURE_CUBE_MAP && _rb_size_z > 1) { + if (tex->get_texture_type() != Texture::TT_cube_map && _rb_size_z > 1) { tex->set_z_size(_rb_size_z); } tex->set_pad_size(_rb_size_x - _x_size, _rb_size_y - _y_size); @@ -617,85 +612,41 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot, } } - // Create the OpenGL texture object. - TextureContext *tc = tex->prepare_now(0, glgsg->get_prepared_objects(), glgsg); - nassertv(tc != (TextureContext *)NULL); - CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc); - glgsg->update_texture(tc, true); +#ifndef OPENGLES + GLenum target = glgsg->get_texture_target(tex->get_texture_type()); + if (target == GL_TEXTURE_CUBE_MAP) { + target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + layer; + } +#endif if (attachpoint == GL_DEPTH_ATTACHMENT_EXT) { GLCAT.debug() << "Binding texture " << *tex << " to depth attachment.\n"; -#ifndef OPENGLES - GLclampf priority = 1.0f; - glPrioritizeTextures(1, >c->_index, &priority); -#endif - if (_rb_size_z == 1) { - if (target == GL_TEXTURE_3D) { - glgsg->_glFramebufferTexture3D(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, - target, gtc->_index, 0, layer); - } else if (target == GL_TEXTURE_2D_ARRAY_EXT) { - glgsg->_glFramebufferTextureLayer(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, - gtc->_index, 0, layer); - } else { - glgsg->_glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, - target, gtc->_index, 0); - } - } else { - glgsg->_glFramebufferTexture(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, - gtc->_index, 0); - } + attach_tex(layer, 0, tex, GL_DEPTH_ATTACHMENT_EXT); +#ifndef OPENGLES GLint depth_size = 0; GLP(GetTexLevelParameteriv)(target, 0, GL_TEXTURE_DEPTH_SIZE, &depth_size); _fb_properties.set_depth_bits(depth_size); +#endif if (slot == RTP_depth_stencil) { GLCAT.debug() << "Binding texture " << *tex << " to stencil attachment.\n"; - if (_rb_size_z == 1) { - if (target == GL_TEXTURE_3D) { - glgsg->_glFramebufferTexture3D(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, - target, gtc->_index, 0, layer); - } else if (target == GL_TEXTURE_2D_ARRAY_EXT) { - glgsg->_glFramebufferTextureLayer(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, - gtc->_index, 0, layer); - } else { - glgsg->_glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, - target, gtc->_index, 0); - } - } else { - glgsg->_glFramebufferTexture(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, - gtc->_index, 0); - } + attach_tex(layer, 0, tex, GL_STENCIL_ATTACHMENT_EXT); + +#ifndef OPENGLES GLint stencil_size = 0; GLP(GetTexLevelParameteriv)(target, 0, GL_TEXTURE_STENCIL_SIZE, &stencil_size); _fb_properties.set_stencil_bits(stencil_size); +#endif } } else { GLCAT.debug() << "Binding texture " << *tex << " to color attachment.\n"; -#ifndef OPENGLES - GLclampf priority = 1.0f; - glPrioritizeTextures(1, >c->_index, &priority); -#endif - glgsg->update_texture(tc, true); - if (_rb_size_z == 1) { - if (target == GL_TEXTURE_3D) { - glgsg->_glFramebufferTexture3D(GL_FRAMEBUFFER_EXT, attachpoint, - target, gtc->_index, 0, layer); - } else if (target == GL_TEXTURE_2D_ARRAY_EXT) { - glgsg->_glFramebufferTextureLayer(GL_FRAMEBUFFER_EXT, attachpoint, - gtc->_index, 0, layer); - } else { - glgsg->_glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, attachpoint, - target, gtc->_index, 0); - } - } else { - glgsg->_glFramebufferTexture(GL_FRAMEBUFFER_EXT, attachpoint, - gtc->_index, 0); - } + attach_tex(layer, 0, tex, attachpoint); + #ifndef OPENGLES if (attachpoint == GL_COLOR_ATTACHMENT0_EXT) { GLint red_size = 0, green_size = 0, blue_size = 0, alpha_size = 0; @@ -749,16 +700,18 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot, gl_format = GL_RGB10_EXT; } } else if (_fb_properties.get_color_bits() == 0) { - gl_format = GL_ALPHA8_OES; + gl_format = GL_ALPHA8_EXT; } else if (_fb_properties.get_color_bits() <= 12 && _fb_properties.get_alpha_bits() <= 4) { gl_format = GL_RGBA4_OES; +#ifndef OPENGLES_1 } else if (_fb_properties.get_color_bits() <= 15 && _fb_properties.get_alpha_bits() == 1) { gl_format = GL_RGB5_A1_EXT; +#endif } else if (_fb_properties.get_color_bits() <= 30 && _fb_properties.get_alpha_bits() <= 2) { - gl_format = GL_RGB10_A2_OES; + gl_format = GL_RGB10_A2_EXT; } else { gl_format = GL_RGBA8_OES; } @@ -1000,6 +953,61 @@ bind_slot_multisample(bool rb_resize, Texture **attach, RenderTexturePlane slot, glgsg->report_my_gl_errors(); } +//////////////////////////////////////////////////////////////////// +// Function: glGraphicsBuffer::attach_tex +// Access: Private +// Description: This function attaches the given texture to the +// given attachment point. +//////////////////////////////////////////////////////////////////// +void CLP(GraphicsBuffer):: +attach_tex(int layer, int view, Texture *attach, GLenum attachpoint) { + CLP(GraphicsStateGuardian) *glgsg; + DCAST_INTO_V(glgsg, _gsg); + + // Create the OpenGL texture object. + TextureContext *tc = attach->prepare_now(view, glgsg->get_prepared_objects(), glgsg); + nassertv(tc != (TextureContext *)NULL); + CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc); + glgsg->update_texture(tc, true); + +#ifndef OPENGLES + GLclampf priority = 1.0f; + glPrioritizeTextures(1, >c->_index, &priority); +#endif + +#ifndef OPENGLES + if (_rb_size_z != 1) { + // Bind all of the layers of the texture. + glgsg->_glFramebufferTexture(GL_FRAMEBUFFER_EXT, attachpoint, + gtc->_index, 0); + return; + } +#endif + + GLenum target = glgsg->get_texture_target(attach->get_texture_type()); + if (target == GL_TEXTURE_CUBE_MAP) { + target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + layer; + } + + switch (target) { +#ifndef OPENGLES_1 + case GL_TEXTURE_3D: + glgsg->_glFramebufferTexture3D(GL_FRAMEBUFFER_EXT, attachpoint, + target, gtc->_index, 0, layer); + break; +#endif +#ifndef OPENGLES + case GL_TEXTURE_2D_ARRAY_EXT: + glgsg->_glFramebufferTextureLayer(GL_FRAMEBUFFER_EXT, attachpoint, + gtc->_index, 0, layer); + break; +#endif + default: + glgsg->_glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, attachpoint, + target, gtc->_index, 0); + } +} + //////////////////////////////////////////////////////////////////// // Function: glGraphicsBuffer::generate_mipmaps // Access: Private @@ -1129,44 +1137,13 @@ select_target_tex_page(int page, int view) { tex->set_num_views(view + 1); } - GLenum target = glgsg->get_texture_target(tex->get_texture_type()); - if (target == GL_TEXTURE_CUBE_MAP) { - target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + _bound_tex_page; - } - - // Create the OpenGL texture object. - TextureContext *tc = tex->prepare_now(view, glgsg->get_prepared_objects(), glgsg); - nassertv(tc != (TextureContext *)NULL); - CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc); - glgsg->update_texture(tc, true); - if (GLCAT.is_spam()) { GLCAT.spam() << "Binding texture " << *tex << " view " << view << " to color attachment.\n"; } -#ifndef OPENGLES - GLclampf priority = 1.0f; - glPrioritizeTextures(1, >c->_index, &priority); -#endif - glgsg->update_texture(tc, true); - - if (_rb_size_z == 1) { - if (target == GL_TEXTURE_3D) { - glgsg->_glFramebufferTexture3D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, - target, gtc->_index, 0, _bound_tex_page); - } else if (target == GL_TEXTURE_2D_ARRAY_EXT) { - glgsg->_glFramebufferTextureLayer(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, - gtc->_index, 0, _bound_tex_page); - } else { - glgsg->_glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, - target, gtc->_index, 0); - } - } else { - glgsg->_glFramebufferTexture(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, - gtc->_index, 0); - } + attach_tex(_bound_tex_page, view, tex, GL_COLOR_ATTACHMENT0_EXT); report_my_gl_errors(); } diff --git a/panda/src/glstuff/glGraphicsBuffer_src.h b/panda/src/glstuff/glGraphicsBuffer_src.h index 21850b2cb6..bf4c0d0c51 100644 --- a/panda/src/glstuff/glGraphicsBuffer_src.h +++ b/panda/src/glstuff/glGraphicsBuffer_src.h @@ -98,6 +98,7 @@ private: RenderTexturePlane plane, GLenum attachpoint); void bind_slot_multisample(bool rb_resize, Texture **attach, RenderTexturePlane plane, GLenum attachpoint); + void attach_tex(int layer, int view, Texture *attach, GLenum attachpoint); bool check_fbo(); void generate_mipmaps(); void rebuild_bitplanes(); From 455a504cec9597f56d15faa83d328eb60cf39689 Mon Sep 17 00:00:00 2001 From: enn0x Date: Wed, 5 Mar 2014 23:15:53 +0000 Subject: [PATCH 49/71] Fixes to Bullet soft body transform handling. --- panda/src/bullet/bulletSoftBodyNode.cxx | 46 ++++++++++++++++--------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/panda/src/bullet/bulletSoftBodyNode.cxx b/panda/src/bullet/bulletSoftBodyNode.cxx index 24fbbcedb9..7fd660b8a6 100644 --- a/panda/src/bullet/bulletSoftBodyNode.cxx +++ b/panda/src/bullet/bulletSoftBodyNode.cxx @@ -194,22 +194,35 @@ transform_changed() { LMatrix4 m_ts = ts->get_mat(); if (!m_sync.almost_equal(m_ts)) { - _sync = ts; + // New transform for the center btTransform trans = TransformState_to_btTrans(ts); - trans *= _soft->m_initialWorldTransform.inverse(); + // Offset between current approx center and current initial transform + btVector3 pos = LVecBase3_to_btVector3(this->get_aabb().get_approx_center()); + btVector3 origin = _soft->m_initialWorldTransform.getOrigin(); + btVector3 offset = pos - origin; + + // Subtract offset to get new transform for the body + trans.setOrigin(trans.getOrigin() - offset); + + // Now apply the new transform + _soft->transform(_soft->m_initialWorldTransform.inverse()); _soft->transform(trans); if (ts->has_scale()) { - LVecBase3 scale = ts->get_scale(); - if (!scale.almost_equal(LVecBase3(1.0f, 1.0f, 1.0f))) { - for (int i=0; iset_local_scale(scale); - } - } + btVector3 current_scale = LVecBase3_to_btVector3(_sync->get_scale()); + btVector3 new_scale = LVecBase3_to_btVector3(ts->get_scale()); + + current_scale.setX(1.0 / current_scale.getX()); + current_scale.setY(1.0 / current_scale.getY()); + current_scale.setZ(1.0 / current_scale.getZ()); + + _soft->scale(current_scale); + _soft->scale(new_scale); } + + _sync = ts; } } @@ -221,7 +234,7 @@ transform_changed() { void BulletSoftBodyNode:: sync_p2b() { - transform_changed(); + //transform_changed(); Disabled for now... } //////////////////////////////////////////////////////////////////// @@ -280,22 +293,21 @@ sync_b2p() { } } - // It is ok to pass the address of a temporary object here, because - // set_bounds does not store the pointer - it makes a copy using - // volume->make_copy(). - BoundingBox bb = this->get_aabb(); - LVecBase3 pos = bb.get_approx_center(); + // Update the synchronized transform with the current + // approximate center of the soft body + LVecBase3 pos = this->get_aabb().get_approx_center(); + CPT(TransformState) ts = TransformState::make_pos(pos); NodePath np = NodePath::any_path((PandaNode *)this); LVecBase3 scale = np.get_net_transform()->get_scale(); - - CPT(TransformState) ts = TransformState::make_pos(pos); ts = ts->set_scale(scale); _sync = ts; _sync_disable = true; np.set_transform(NodePath(), ts); _sync_disable = false; +/* +*/ Thread *current_thread = Thread::get_current_thread(); this->r_mark_geom_bounds_stale(current_thread); From ec5f9a9e06acb70904b24e1b9449f622efffdc9f Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 6 Mar 2014 18:35:38 +0000 Subject: [PATCH 50/71] Add -f to ln command to overwrite existing ppython links --- makepanda/installpanda.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makepanda/installpanda.py b/makepanda/installpanda.py index 4833682bae..2f3deadd5d 100644 --- a/makepanda/installpanda.py +++ b/makepanda/installpanda.py @@ -167,7 +167,7 @@ def InstallPanda(destdir="", prefix="/usr", outputdir="built"): else: oscmd("echo '"+libdir+"/panda3d'> "+destdir+"/etc/ld.so.conf.d/panda3d.conf") oscmd("chmod +x "+destdir+"/etc/ld.so.conf.d/panda3d.conf") - oscmd("ln -s "+PEXEC+" "+destdir+prefix+"/bin/ppython") + oscmd("ln -f -s "+PEXEC+" "+destdir+prefix+"/bin/ppython") oscmd("cp "+outputdir+"/bin/* "+destdir+prefix+"/bin/") for base in os.listdir(outputdir+"/lib"): if (not base.endswith(".a")) or base == "libp3pystub.a": From aa2c24033646be1e109bf3cc9a752df67b4032c4 Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 6 Mar 2014 21:31:02 +0000 Subject: [PATCH 51/71] Fix bug: loadSound should respect positional parameter --- direct/src/showbase/Loader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/direct/src/showbase/Loader.py b/direct/src/showbase/Loader.py index 2f4b81e411..52f253d717 100644 --- a/direct/src/showbase/Loader.py +++ b/direct/src/showbase/Loader.py @@ -814,7 +814,7 @@ class Loader(DirectObject): result = [] for soundPath in soundList: # should return a valid sound obj even if musicMgr is invalid - sound = manager.getSound(soundPath) + sound = manager.getSound(soundPath, positional) result.append(sound) if gotList: From 85a5be984fc726cd5dd97e404e8333ba410ef632 Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 7 Mar 2014 12:52:15 +0000 Subject: [PATCH 52/71] Raw button event support, with X11 implementation (untransformed QWERTY) --- .../src/display/graphicsWindowInputDevice.cxx | 22 +++ panda/src/display/graphicsWindowInputDevice.h | 22 +-- panda/src/event/buttonEvent.cxx | 8 ++ panda/src/event/buttonEvent.h | 9 +- panda/src/tform/buttonThrower.I | 47 +++++++ panda/src/tform/buttonThrower.cxx | 48 ++++++- panda/src/tform/buttonThrower.h | 9 +- panda/src/tform/mouseWatcher.cxx | 6 + panda/src/x11display/x11GraphicsWindow.cxx | 131 ++++++++++++++++++ panda/src/x11display/x11GraphicsWindow.h | 1 + 10 files changed, 285 insertions(+), 18 deletions(-) diff --git a/panda/src/display/graphicsWindowInputDevice.cxx b/panda/src/display/graphicsWindowInputDevice.cxx index e6f9fdf483..9cbffb3e64 100644 --- a/panda/src/display/graphicsWindowInputDevice.cxx +++ b/panda/src/display/graphicsWindowInputDevice.cxx @@ -365,3 +365,25 @@ focus_lost(double time) { } _buttons_held.clear(); } + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::raw_button_down +// Access: Public +// Description: Records that the indicated button has been depressed. +//////////////////////////////////////////////////////////////////// +void GraphicsWindowInputDevice:: +raw_button_down(ButtonHandle button, double time) { + LightMutexHolder holder(_lock); + _button_events.push_back(ButtonEvent(button, ButtonEvent::T_raw_down, time)); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindowInputDevice::raw_button_up +// Access: Public +// Description: Records that the indicated button has been released. +//////////////////////////////////////////////////////////////////// +void GraphicsWindowInputDevice:: +raw_button_up(ButtonHandle button, double time) { + LightMutexHolder holder(_lock); + _button_events.push_back(ButtonEvent(button, ButtonEvent::T_raw_up, time)); +} diff --git a/panda/src/display/graphicsWindowInputDevice.h b/panda/src/display/graphicsWindowInputDevice.h index 367feebd26..99843c32ee 100644 --- a/panda/src/display/graphicsWindowInputDevice.h +++ b/panda/src/display/graphicsWindowInputDevice.h @@ -45,27 +45,27 @@ public: static GraphicsWindowInputDevice pointer_only(GraphicsWindow *host, const string &name); static GraphicsWindowInputDevice keyboard_only(GraphicsWindow *host, const string &name); static GraphicsWindowInputDevice pointer_and_keyboard(GraphicsWindow *host, const string &name); - + INLINE GraphicsWindowInputDevice(); GraphicsWindowInputDevice(const GraphicsWindowInputDevice ©); void operator = (const GraphicsWindowInputDevice ©); ~GraphicsWindowInputDevice(); - + INLINE string get_name() const; INLINE bool has_pointer() const; INLINE bool has_keyboard() const; INLINE void set_device_index(int index); - + INLINE MouseData get_pointer() const; INLINE MouseData get_raw_pointer() const; - + INLINE void enable_pointer_events(); INLINE void disable_pointer_events(); - + void enable_pointer_mode(double speed); void disable_pointer_mode(); - + bool has_button_event() const; ButtonEvent get_button_event(); bool has_pointer_event() const; @@ -81,6 +81,8 @@ PUBLISHED: void candidate(const wstring &candidate_string, size_t highlight_start, size_t highlight_end, size_t cursor_pos); void focus_lost(double time = ClockObject::get_global_clock()->get_frame_time()); + void raw_button_down(ButtonHandle button, double time = ClockObject::get_global_clock()->get_frame_time()); + void raw_button_up(ButtonHandle button, double time = ClockObject::get_global_clock()->get_frame_time()); INLINE void set_pointer_in_window(double x, double y, double time = ClockObject::get_global_clock()->get_frame_time()); INLINE void set_pointer_out_of_window(double time = ClockObject::get_global_clock()->get_frame_time()); @@ -102,17 +104,17 @@ private: typedef pdeque ButtonEvents; LightMutex _lock; - + GraphicsWindow *_host; - + string _name; int _flags; int _device_index; int _event_sequence; - + bool _pointer_mode_enable; double _pointer_speed; - + bool _enable_pointer_events; MouseData _mouse_data; MouseData _true_mouse_data; diff --git a/panda/src/event/buttonEvent.cxx b/panda/src/event/buttonEvent.cxx index 3a212f34a1..774b5ec0a6 100644 --- a/panda/src/event/buttonEvent.cxx +++ b/panda/src/event/buttonEvent.cxx @@ -55,6 +55,14 @@ output(ostream &out) const { case T_move: out << "move"; break; + + case T_raw_down: + out << "raw button " << _button << " down"; + break; + + case T_raw_up: + out << "raw button " << _button << " up"; + break; } } diff --git a/panda/src/event/buttonEvent.h b/panda/src/event/buttonEvent.h index 7469de0e82..adc395d405 100644 --- a/panda/src/event/buttonEvent.h +++ b/panda/src/event/buttonEvent.h @@ -81,10 +81,17 @@ public: // from a menu. T_candidate, - // T_move is used to indicate that the mouse has moved within the + // T_move is used to indicate that the mouse has moved within the // current region. Button drag mode needs this, others may ignore // this event T_move, + + // T_raw_down is usually sent together with T_down, except that + // this is the original, untransformed scan key sent by the keyboard. + // It is not altered by modifier keys and acts as if the user is + // using the US (qwerty) keyboard layout. + T_raw_down, + T_raw_up, }; INLINE ButtonEvent(); diff --git a/panda/src/tform/buttonThrower.I b/panda/src/tform/buttonThrower.I index 96e7e8434e..b5aa817858 100644 --- a/panda/src/tform/buttonThrower.I +++ b/panda/src/tform/buttonThrower.I @@ -200,6 +200,53 @@ get_move_event() const { return _move_event; } +//////////////////////////////////////////////////////////////////// +// Function: ButtonThrower::set_raw_button_down_event +// Access: Published +// Description: Like set_button_down_event, but uses the raw, +// untransformed scan key from the operating system. +// This uses buttons that are independent of the +// user's selected keyboard layout. +//////////////////////////////////////////////////////////////////// +INLINE void ButtonThrower:: +set_raw_button_down_event(const string &raw_button_down_event) { + _raw_button_down_event = raw_button_down_event; +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonThrower::get_raw_button_down_event +// Access: Published +// Description: Returns the raw_button_down_event that has been set on +// this ButtonThrower. See set_raw_button_down_event(). +//////////////////////////////////////////////////////////////////// +INLINE const string &ButtonThrower:: +get_raw_button_down_event() const { + return _raw_button_down_event; +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonThrower::set_raw_button_up_event +// Access: Published +// Description: Specifies the generic event that is generated (if +// any) each time a key or button is released. See +// set_raw_button_down_event(). +//////////////////////////////////////////////////////////////////// +INLINE void ButtonThrower:: +set_raw_button_up_event(const string &raw_button_up_event) { + _raw_button_up_event = raw_button_up_event; +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonThrower::get_raw_button_up_event +// Access: Published +// Description: Returns the raw_button_up_event that has been set on +// this ButtonThrower. See set_raw_button_up_event(). +//////////////////////////////////////////////////////////////////// +INLINE const string &ButtonThrower:: +get_raw_button_up_event() const { + return _raw_button_up_event; +} + //////////////////////////////////////////////////////////////////// // Function: ButtonThrower::set_prefix // Access: Published diff --git a/panda/src/tform/buttonThrower.cxx b/panda/src/tform/buttonThrower.cxx index b0e1d1452f..4d52caebcd 100644 --- a/panda/src/tform/buttonThrower.cxx +++ b/panda/src/tform/buttonThrower.cxx @@ -268,16 +268,16 @@ void ButtonThrower:: do_specific_event(const string &event_name, double time) { if (_specific_flag) { PT(Event) event = new Event(_prefix + event_name); - + if (_time_flag) { event->add_parameter(time); } - + ParameterList::const_iterator pi; for (pi = _parameters.begin(); pi != _parameters.end(); ++pi) { event->add_parameter(*pi); } - + throw_event(event); } } @@ -318,6 +318,14 @@ do_general_event(const ButtonEvent &button_event, const string &button_name) { case ButtonEvent::T_move: event_name = _move_event; break; + + case ButtonEvent::T_raw_down: + event_name = _raw_button_down_event; + break; + + case ButtonEvent::T_raw_up: + event_name = _raw_button_up_event; + break; } if (event_name.empty()) { // This general event is not configured. @@ -336,6 +344,8 @@ do_general_event(const ButtonEvent &button_event, const string &button_name) { case ButtonEvent::T_resume_down: case ButtonEvent::T_up: case ButtonEvent::T_repeat: + case ButtonEvent::T_raw_down: + case ButtonEvent::T_raw_up: event->add_parameter(button_name); break; @@ -405,7 +415,7 @@ do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &input, do_specific_event(event_name, be._time); } do_general_event(be, event_name); - + } else { // Don't process this button; instead, pass it down to future // generations. @@ -418,7 +428,7 @@ do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &input, // throw an event now (since we already missed it), but do // make sure our modifiers are up-to-date. _mods.button_down(be._button); - + } else if (be._type == ButtonEvent::T_up) { // Button up. _mods.button_up(be._button); @@ -438,6 +448,34 @@ do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &input, _button_events->add_event(be); } + } else if (be._type == ButtonEvent::T_raw_down) { + // Raw button down. + if (!_throw_buttons_active || has_throw_button(be._button)) { + // Process this button. + do_specific_event("raw-" + event_name, be._time); + do_general_event(be, event_name); + + } else { + // Don't process this button; instead, pass it down to future + // generations. + _button_events->add_event(be); + } + + } else if (be._type == ButtonEvent::T_raw_up) { + // Raw button up. + if (!_throw_buttons_active || has_throw_button(be._button)) { + // Process this button. + do_specific_event("raw-" + event_name + "-up", be._time); + do_general_event(be, event_name); + } + if (_throw_buttons_active) { + // Now pass the event on to future generations. We always + // pass "up" events, even if we are intercepting this + // particular button; unless we're processing all buttons in + // which case it doesn't matter. + _button_events->add_event(be); + } + } else { // Some other kind of button event (e.g. keypress). Don't // throw an event for this, but do pass it down. diff --git a/panda/src/tform/buttonThrower.h b/panda/src/tform/buttonThrower.h index 59b19b7bbf..b779406b05 100644 --- a/panda/src/tform/buttonThrower.h +++ b/panda/src/tform/buttonThrower.h @@ -53,6 +53,10 @@ PUBLISHED: INLINE const string &get_candidate_event() const; INLINE void set_move_event(const string &move_event); INLINE const string &get_move_event() const; + INLINE void set_raw_button_down_event(const string &raw_button_down_event); + INLINE const string &get_raw_button_down_event() const; + INLINE void set_raw_button_up_event(const string &raw_button_up_event); + INLINE const string &get_raw_button_up_event() const; INLINE void set_prefix(const string &prefix); INLINE const string &get_prefix() const; @@ -61,7 +65,6 @@ PUBLISHED: INLINE void set_time_flag(bool time_flag); INLINE bool get_time_flag() const; - void add_parameter(const EventParameter &obj); int get_num_parameters() const; @@ -85,7 +88,7 @@ public: private: void do_specific_event(const string &event_name, double time); - void do_general_event(const ButtonEvent &button_event, + void do_general_event(const ButtonEvent &button_event, const string &event_name); private: @@ -95,6 +98,8 @@ private: string _keystroke_event; string _candidate_event; string _move_event; + string _raw_button_up_event; + string _raw_button_down_event; bool _specific_flag; string _prefix; bool _time_flag; diff --git a/panda/src/tform/mouseWatcher.cxx b/panda/src/tform/mouseWatcher.cxx index 58de969a8e..f0b5f9d34e 100644 --- a/panda/src/tform/mouseWatcher.cxx +++ b/panda/src/tform/mouseWatcher.cxx @@ -1539,6 +1539,12 @@ do_transmit_data(DataGraphTraverser *trav, const DataNodeTransmit &input, case ButtonEvent::T_move: // This is handled below. break; + + case ButtonEvent::T_raw_down: + case ButtonEvent::T_raw_up: + // These are passed through. + new_button_events.add_event(be); + break; } } } diff --git a/panda/src/x11display/x11GraphicsWindow.cxx b/panda/src/x11display/x11GraphicsWindow.cxx index 27819dbf84..41d6a145be 100644 --- a/panda/src/x11display/x11GraphicsWindow.cxx +++ b/panda/src/x11display/x11GraphicsWindow.cxx @@ -1404,6 +1404,11 @@ handle_keypress(XKeyEvent &event) { if (button != ButtonHandle::none()) { _input_devices[0].button_down(button); } + + ButtonHandle raw_button = map_raw_button(event.keycode); + if (raw_button != ButtonHandle::none()) { + _input_devices[0].raw_button_down(raw_button); + } } //////////////////////////////////////////////////////////////////// @@ -1432,6 +1437,11 @@ handle_keyrelease(XKeyEvent &event) { if (button != ButtonHandle::none()) { _input_devices[0].button_up(button); } + + ButtonHandle raw_button = map_raw_button(event.keycode); + if (raw_button != ButtonHandle::none()) { + _input_devices[0].raw_button_up(raw_button); + } } //////////////////////////////////////////////////////////////////// @@ -1839,6 +1849,127 @@ map_button(KeySym key) { return ButtonHandle::none(); } +//////////////////////////////////////////////////////////////////// +// Function: x11GraphicsWindow::map_raw_button +// Access: Private +// Description: Maps from a single X keycode to Panda's ButtonHandle. +//////////////////////////////////////////////////////////////////// +ButtonHandle x11GraphicsWindow:: +map_raw_button(KeyCode key) { + switch (key) { + case 9: return KeyboardButton::escape(); + case 10: return KeyboardButton::ascii_key('1'); + case 11: return KeyboardButton::ascii_key('2'); + case 12: return KeyboardButton::ascii_key('3'); + case 13: return KeyboardButton::ascii_key('4'); + case 14: return KeyboardButton::ascii_key('5'); + case 15: return KeyboardButton::ascii_key('6'); + case 16: return KeyboardButton::ascii_key('7'); + case 17: return KeyboardButton::ascii_key('8'); + case 18: return KeyboardButton::ascii_key('9'); + case 19: return KeyboardButton::ascii_key('0'); + case 20: return KeyboardButton::ascii_key('-'); + case 21: return KeyboardButton::ascii_key('='); + case 22: return KeyboardButton::backspace(); + case 23: return KeyboardButton::tab(); + case 24: return KeyboardButton::ascii_key('q'); + case 25: return KeyboardButton::ascii_key('w'); + case 26: return KeyboardButton::ascii_key('e'); + case 27: return KeyboardButton::ascii_key('r'); + case 28: return KeyboardButton::ascii_key('t'); + case 29: return KeyboardButton::ascii_key('y'); + case 30: return KeyboardButton::ascii_key('u'); + case 31: return KeyboardButton::ascii_key('i'); + case 32: return KeyboardButton::ascii_key('o'); + case 33: return KeyboardButton::ascii_key('p'); + case 34: return KeyboardButton::ascii_key('['); + case 35: return KeyboardButton::ascii_key(']'); + case 36: return KeyboardButton::enter(); + case 37: return KeyboardButton::lcontrol(); + case 38: return KeyboardButton::ascii_key('a'); + case 39: return KeyboardButton::ascii_key('s'); + case 40: return KeyboardButton::ascii_key('d'); + case 41: return KeyboardButton::ascii_key('f'); + case 42: return KeyboardButton::ascii_key('g'); + case 43: return KeyboardButton::ascii_key('h'); + case 44: return KeyboardButton::ascii_key('j'); + case 45: return KeyboardButton::ascii_key('k'); + case 46: return KeyboardButton::ascii_key('l'); + case 47: return KeyboardButton::ascii_key(';'); + case 48: return KeyboardButton::ascii_key('\''); + case 49: return KeyboardButton::ascii_key('`'); + case 50: return KeyboardButton::lshift(); + case 51: return KeyboardButton::ascii_key('\\'); + case 52: return KeyboardButton::ascii_key('z'); + case 53: return KeyboardButton::ascii_key('x'); + case 54: return KeyboardButton::ascii_key('c'); + case 55: return KeyboardButton::ascii_key('v'); + case 56: return KeyboardButton::ascii_key('b'); + case 57: return KeyboardButton::ascii_key('n'); + case 58: return KeyboardButton::ascii_key('m'); + case 59: return KeyboardButton::ascii_key(','); + case 60: return KeyboardButton::ascii_key('.'); + case 61: return KeyboardButton::ascii_key('/'); + case 62: return KeyboardButton::rshift(); + case 63: return KeyboardButton::ascii_key('*'); + case 64: return KeyboardButton::lalt(); + case 65: return KeyboardButton::space(); + case 66: return KeyboardButton::caps_lock(); + case 67: return KeyboardButton::f1(); + case 68: return KeyboardButton::f2(); + case 69: return KeyboardButton::f3(); + case 70: return KeyboardButton::f4(); + case 71: return KeyboardButton::f5(); + case 72: return KeyboardButton::f6(); + case 73: return KeyboardButton::f7(); + case 74: return KeyboardButton::f8(); + case 75: return KeyboardButton::f9(); + case 76: return KeyboardButton::f10(); + case 77: return KeyboardButton::num_lock(); + case 78: return KeyboardButton::scroll_lock(); + case 79: return KeyboardButton::ascii_key('7'); + case 80: return KeyboardButton::ascii_key('8'); + case 81: return KeyboardButton::ascii_key('9'); + case 82: return KeyboardButton::ascii_key('-'); + case 83: return KeyboardButton::ascii_key('4'); + case 84: return KeyboardButton::ascii_key('5'); + case 85: return KeyboardButton::ascii_key('6'); + case 86: return KeyboardButton::ascii_key('+'); + case 87: return KeyboardButton::ascii_key('1'); + case 88: return KeyboardButton::ascii_key('2'); + case 89: return KeyboardButton::ascii_key('3'); + case 90: return KeyboardButton::ascii_key('0'); + case 91: return KeyboardButton::ascii_key('.'); + + case 95: return KeyboardButton::f11(); + case 96: return KeyboardButton::f12(); + + case 104: return KeyboardButton::enter(); + case 105: return KeyboardButton::rcontrol(); + case 106: return KeyboardButton::ascii_key('/'); + case 107: return KeyboardButton::print_screen(); + case 108: return KeyboardButton::ralt(); + + case 110: return KeyboardButton::home(); + case 111: return KeyboardButton::up(); + case 112: return KeyboardButton::page_up(); + case 113: return KeyboardButton::left(); + case 114: return KeyboardButton::right(); + case 115: return KeyboardButton::end(); + case 116: return KeyboardButton::down(); + case 117: return KeyboardButton::page_down(); + case 118: return KeyboardButton::insert(); + case 119: return KeyboardButton::del(); + + case 127: return KeyboardButton::pause(); + + //case 133: return KeyboardButton::lmeta(); + //case 134: return KeyboardButton::rmeta(); + //case 135: return KeyboardButton::menu(); + } + return ButtonHandle::none(); +} + //////////////////////////////////////////////////////////////////// // Function: x11GraphicsWindow::get_mouse_button // Access: Private diff --git a/panda/src/x11display/x11GraphicsWindow.h b/panda/src/x11display/x11GraphicsWindow.h index d99d6fe2b1..e5f51d9cf3 100644 --- a/panda/src/x11display/x11GraphicsWindow.h +++ b/panda/src/x11display/x11GraphicsWindow.h @@ -67,6 +67,7 @@ protected: ButtonHandle get_button(XKeyEvent &key_event, bool allow_shift); ButtonHandle map_button(KeySym key); + ButtonHandle map_raw_button(KeyCode key); ButtonHandle get_mouse_button(XButtonEvent &button_event); static Bool check_event(X11_Display *display, XEvent *event, char *arg); From 93ed8b275eedbb8f7b9aeff6beaaad6620685b47 Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 7 Mar 2014 19:14:45 +0000 Subject: [PATCH 53/71] Fix build for GLES --- panda/src/gles2gsg/gles2gsg.h | 6 +++++ .../glstuff/glGraphicsStateGuardian_src.cxx | 24 ++++++++++++++----- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/panda/src/gles2gsg/gles2gsg.h b/panda/src/gles2gsg/gles2gsg.h index ec24b6d470..56b59eb89b 100644 --- a/panda/src/gles2gsg/gles2gsg.h +++ b/panda/src/gles2gsg/gles2gsg.h @@ -86,6 +86,12 @@ typedef char GLchar; #define GL_MAX_3D_TEXTURE_SIZE GL_MAX_3D_TEXTURE_SIZE_OES #define GL_SAMPLER_3D GL_SAMPLER_3D_OES #define GL_BGRA GL_BGRA_EXT +#define GL_RED GL_RED_EXT +#define GL_RG GL_RG_EXT +#define GL_R16F GL_R16F_EXT +#define GL_RG16F GL_RG16F_EXT +#define GL_RGB16F GL_RGB16F_EXT +#define GL_RGBA16F GL_RGBA16F_EXT #undef SUPPORT_IMMEDIATE_MODE #define APIENTRY diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index cfeaaea617..8f29657bce 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -6405,8 +6405,10 @@ get_external_image_format(Texture *tex) const { #endif case Texture::F_alpha: return GL_ALPHA; +#ifndef OPENGLES_1 case Texture::F_rg16: return GL_RG; +#endif case Texture::F_rgb: case Texture::F_rgb5: case Texture::F_rgb8: @@ -6681,8 +6683,10 @@ get_internal_image_format(Texture *tex) const { return GL_RGBA8_OES; case Texture::F_rgba12: return GL_RGBA; +#ifndef OPENGLES_1 case Texture::F_rgba16: return GL_RGBA16F_EXT; +#endif // OPENGLES_1 case Texture::F_rgba32: return GL_RGBA32F_EXT; #else @@ -6733,12 +6737,12 @@ get_internal_image_format(Texture *tex) const { return GL_R3_G3_B2; #endif -#ifdef OPENGLES +#if defined(OPENGLES_2) case Texture::F_r16: return GL_R16F_EXT; case Texture::F_rg16: return GL_RG16F_EXT; -#else +#elif !defined(OPENGLES_1) case Texture::F_r16: if (tex->get_component_type() == Texture::T_float) { return GL_R16F; @@ -6756,10 +6760,12 @@ get_internal_image_format(Texture *tex) const { case Texture::F_alpha: return GL_ALPHA; +#ifndef OPENGLES_1 case Texture::F_red: case Texture::F_green: case Texture::F_blue: return GL_RED; +#endif case Texture::F_luminance: return GL_LUMINANCE; @@ -9333,9 +9339,11 @@ upload_texture_image(CLP(TextureContext) *gtc, break; case GL_TEXTURE_2D_ARRAY: #endif +#ifndef OPENGLES_1 case GL_TEXTURE_3D: _glTexImage3D(page_target, 0, internal_format, width, height, depth, 0, external_format, component_type, NULL); break; +#endif default: GLP(TexImage2D)(page_target, 0, internal_format, width, height, 0, external_format, component_type, NULL); break; @@ -9817,10 +9825,12 @@ do_extract_texture_data(CLP(TextureContext) *gtc) { type = Texture::T_unsigned_short; format = Texture::F_depth_component; break; +#ifndef OPENGLES case GL_DEPTH_COMPONENT32F: type = Texture::T_float; format = Texture::F_depth_component; break; +#endif case GL_DEPTH_STENCIL_EXT: case GL_DEPTH24_STENCIL8_EXT: type = Texture::T_unsigned_int_24_8; @@ -9848,10 +9858,6 @@ do_extract_texture_data(CLP(TextureContext) *gtc) { format = Texture::F_rgba12; break; #endif - case GL_RGBA16F: - type = Texture::T_float; - format = Texture::F_rgba16; - break; case GL_RGB: case 3: @@ -9877,6 +9883,11 @@ do_extract_texture_data(CLP(TextureContext) *gtc) { break; #endif +#ifndef OPENGLES_1 + case GL_RGBA16F: + type = Texture::T_float; + format = Texture::F_rgba16; + break; case GL_RGB16F: type = Texture::T_float; format = Texture::F_rgb16; @@ -9889,6 +9900,7 @@ do_extract_texture_data(CLP(TextureContext) *gtc) { type = Texture::T_float; format = Texture::F_r16; break; +#endif #ifndef OPENGLES case GL_RGB16: From e6c8957d27e55c0ce774b9b3b7198f1c8b56bbbb Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 7 Mar 2014 19:35:41 +0000 Subject: [PATCH 54/71] Fix DTOOL_SUPER_BASE111 assertion error with the new building style --- direct/src/showbase/PythonUtil.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/direct/src/showbase/PythonUtil.py b/direct/src/showbase/PythonUtil.py index 38d4df8663..9f18522c2e 100644 --- a/direct/src/showbase/PythonUtil.py +++ b/direct/src/showbase/PythonUtil.py @@ -2459,7 +2459,8 @@ def _getDtoolSuperBase(): from pandac.PandaModules import PandaNode dtoolSuperBase = PandaNode('').__class__.__bases__[0].__bases__[0].__bases__[0] assert repr(dtoolSuperBase) == "" \ - or repr(dtoolSuperBase) == "" + or repr(dtoolSuperBase) == "" \ + or repr(dtoolSuperBase) == "" safeReprNotify = None From a81e192ba5f7d7ebc1ea06c1b9431267b0a017f7 Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 7 Mar 2014 19:43:48 +0000 Subject: [PATCH 55/71] Fix a compile warning on Windows --- panda/src/tinydisplay/zline.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/panda/src/tinydisplay/zline.h b/panda/src/tinydisplay/zline.h index 20914a67f9..8e162d54a4 100644 --- a/panda/src/tinydisplay/zline.h +++ b/panda/src/tinydisplay/zline.h @@ -35,6 +35,7 @@ b = p2->b << 8; #endif +#undef RGB /* from wingdi.h */ #ifdef INTERP_RGB #define RGB(x) x #define RGBPIXEL *pp = RGB_TO_PIXEL(r >> 8,g >> 8,b >> 8) @@ -105,4 +106,4 @@ #undef PUTPIXEL #undef ZZ #undef RGB -#undef RGBPIXEL +#undef RGBPIXEL From 31b79dc5549cf37bf9596a4b7490766e47ebffef Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 7 Mar 2014 19:45:02 +0000 Subject: [PATCH 56/71] Add raw keyboard event support on Windows --- panda/src/windisplay/winGraphicsWindow.cxx | 160 +++++++++++++++++++++ panda/src/windisplay/winGraphicsWindow.h | 3 + 2 files changed, 163 insertions(+) diff --git a/panda/src/windisplay/winGraphicsWindow.cxx b/panda/src/windisplay/winGraphicsWindow.cxx index 470dfe2848..1389b1a637 100644 --- a/panda/src/windisplay/winGraphicsWindow.cxx +++ b/panda/src/windisplay/winGraphicsWindow.cxx @@ -1819,6 +1819,10 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { handle_keypress(lookup_key(wparam), point.x, point.y, get_message_time()); + if ((lparam & 0x40000000) == 0) { + handle_raw_keypress(lookup_raw_key(lparam), get_message_time()); + } + // wparam does not contain left/right information for SHIFT, // CONTROL, or ALT, so we have to track their status and do // the right thing. We'll send the left/right specific key @@ -1879,6 +1883,7 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { ScreenToClient(hwnd, &point); handle_keypress(lookup_key(wparam), point.x, point.y, get_message_time()); + handle_raw_keypress(lookup_raw_key(lparam), get_message_time()); // wparam does not contain left/right information for SHIFT, // CONTROL, or ALT, so we have to track their status and do @@ -2005,6 +2010,7 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { << "keyup: " << wparam << " (" << lookup_key(wparam) << ")\n"; } handle_keyrelease(lookup_key(wparam), get_message_time()); + handle_raw_keyrelease(lookup_raw_key(lparam), get_message_time()); // wparam does not contain left/right information for SHIFT, // CONTROL, or ALT, so we have to track their status and do @@ -2367,6 +2373,30 @@ handle_keyrelease(ButtonHandle key, double time) { } } +//////////////////////////////////////////////////////////////////// +// Function: WinGraphicsWindow::handle_raw_keypress +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void WinGraphicsWindow:: +handle_raw_keypress(ButtonHandle key, double time) { + if (key != ButtonHandle::none()) { + _input_devices[0].raw_button_down(key, time); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: WinGraphicsWindow::handle_raw_keyrelease +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void WinGraphicsWindow:: +handle_raw_keyrelease(ButtonHandle key, double time) { + if (key != ButtonHandle::none()) { + _input_devices[0].raw_button_up(key, time); + } +} + //////////////////////////////////////////////////////////////////// // Function: WinGraphicsWindow::lookup_key // Access: Private @@ -2452,6 +2482,136 @@ lookup_key(WPARAM wparam) const { return ButtonHandle::none(); } +//////////////////////////////////////////////////////////////////// +// Function: WinGraphicsWindow::lookup_raw_key +// Access: Private +// Description: Translates the scancode reported by Windows to an +// appropriate Panda ButtonHandle. +//////////////////////////////////////////////////////////////////// +ButtonHandle WinGraphicsWindow:: +lookup_raw_key(LPARAM lparam) const { + unsigned char vsc = (lparam & 0xff0000) >> 16; + + if (lparam & 0x1000000) { + // Extended keys + switch (vsc) { + case 29: return KeyboardButton::rcontrol(); + case 56: return KeyboardButton::ralt(); + case 69: return KeyboardButton::num_lock(); + case 71: return KeyboardButton::home(); + case 72: return KeyboardButton::up(); + case 73: return KeyboardButton::page_up(); + case 75: return KeyboardButton::left(); + case 77: return KeyboardButton::right(); + case 79: return KeyboardButton::end(); + case 80: return KeyboardButton::down(); + case 81: return KeyboardButton::page_down(); + case 82: return KeyboardButton::insert(); + case 83: return KeyboardButton::del(); + } + } + + if (vsc <= 83) { + static ButtonHandle raw_map[] = { + ButtonHandle::none(), + KeyboardButton::escape(), + KeyboardButton::ascii_key('1'), + KeyboardButton::ascii_key('2'), + KeyboardButton::ascii_key('3'), + KeyboardButton::ascii_key('4'), + KeyboardButton::ascii_key('5'), + KeyboardButton::ascii_key('6'), + KeyboardButton::ascii_key('7'), + KeyboardButton::ascii_key('8'), + KeyboardButton::ascii_key('9'), + KeyboardButton::ascii_key('0'), + KeyboardButton::ascii_key('-'), + KeyboardButton::ascii_key('='), + KeyboardButton::backspace(), + KeyboardButton::tab(), + KeyboardButton::ascii_key('q'), + KeyboardButton::ascii_key('w'), + KeyboardButton::ascii_key('e'), + KeyboardButton::ascii_key('r'), + KeyboardButton::ascii_key('t'), + KeyboardButton::ascii_key('y'), + KeyboardButton::ascii_key('u'), + KeyboardButton::ascii_key('i'), + KeyboardButton::ascii_key('o'), + KeyboardButton::ascii_key('p'), + KeyboardButton::ascii_key('['), + KeyboardButton::ascii_key(']'), + KeyboardButton::enter(), + KeyboardButton::lcontrol(), + KeyboardButton::ascii_key('a'), + KeyboardButton::ascii_key('s'), + KeyboardButton::ascii_key('d'), + KeyboardButton::ascii_key('f'), + KeyboardButton::ascii_key('g'), + KeyboardButton::ascii_key('h'), + KeyboardButton::ascii_key('j'), + KeyboardButton::ascii_key('k'), + KeyboardButton::ascii_key('l'), + KeyboardButton::ascii_key(';'), + KeyboardButton::ascii_key('\''), + KeyboardButton::ascii_key('`'), + KeyboardButton::lshift(), + KeyboardButton::ascii_key('\\'), + KeyboardButton::ascii_key('z'), + KeyboardButton::ascii_key('x'), + KeyboardButton::ascii_key('c'), + KeyboardButton::ascii_key('v'), + KeyboardButton::ascii_key('b'), + KeyboardButton::ascii_key('n'), + KeyboardButton::ascii_key('m'), + KeyboardButton::ascii_key(','), + KeyboardButton::ascii_key('.'), + KeyboardButton::ascii_key('/'), + KeyboardButton::rshift(), + KeyboardButton::ascii_key('*'), + KeyboardButton::lalt(), + KeyboardButton::space(), + KeyboardButton::caps_lock(), + KeyboardButton::f1(), + KeyboardButton::f2(), + KeyboardButton::f3(), + KeyboardButton::f4(), + KeyboardButton::f5(), + KeyboardButton::f6(), + KeyboardButton::f7(), + KeyboardButton::f8(), + KeyboardButton::f9(), + KeyboardButton::f10(), + KeyboardButton::pause(), + KeyboardButton::scroll_lock(), + KeyboardButton::ascii_key('7'), + KeyboardButton::ascii_key('8'), + KeyboardButton::ascii_key('9'), + KeyboardButton::ascii_key('-'), + KeyboardButton::ascii_key('4'), + KeyboardButton::ascii_key('5'), + KeyboardButton::ascii_key('6'), + KeyboardButton::ascii_key('+'), + KeyboardButton::ascii_key('1'), + KeyboardButton::ascii_key('2'), + KeyboardButton::ascii_key('3'), + KeyboardButton::ascii_key('0'), + KeyboardButton::ascii_key('.') + }; + return raw_map[vsc]; + } + + // A few additional keys don't fit well in the above table. + switch (vsc) { + case 87: return KeyboardButton::f11(); + case 88: return KeyboardButton::f12(); + //case 91: return KeyboardButton::lmeta(); + //case 92: return KeyboardButton::rmeta(); + //case 93: return KeyboardButton::menu(); + default: return ButtonHandle::none(); + } +} + //////////////////////////////////////////////////////////////////// // Function: WinGraphicsWindow::handle_raw_input // Access: Private diff --git a/panda/src/windisplay/winGraphicsWindow.h b/panda/src/windisplay/winGraphicsWindow.h index f3d0155c1c..8519a3034c 100644 --- a/panda/src/windisplay/winGraphicsWindow.h +++ b/panda/src/windisplay/winGraphicsWindow.h @@ -131,7 +131,10 @@ private: void handle_keypress(ButtonHandle key, int x, int y, double time); void handle_keyresume(ButtonHandle key, double time); void handle_keyrelease(ButtonHandle key, double time); + void handle_raw_keypress(ButtonHandle key, double time); + void handle_raw_keyrelease(ButtonHandle key, double time); ButtonHandle lookup_key(WPARAM wparam) const; + ButtonHandle lookup_raw_key(LPARAM lparam) const; INLINE int translate_mouse(int pos) const; INLINE void set_cursor_in_window(); INLINE void set_cursor_out_of_window(); From 20c1f16291651f927784c101248bf980b098b767 Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 7 Mar 2014 22:09:29 +0000 Subject: [PATCH 57/71] Set default text-pixels-per-unit from 30 to 40 to improve default font quality --- panda/src/pnmtext/config_pnmtext.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panda/src/pnmtext/config_pnmtext.cxx b/panda/src/pnmtext/config_pnmtext.cxx index 0f5cd52b71..418fc1113a 100644 --- a/panda/src/pnmtext/config_pnmtext.cxx +++ b/panda/src/pnmtext/config_pnmtext.cxx @@ -27,7 +27,7 @@ ConfigureFn(config_pnmtext) { ConfigVariableDouble text_point_size ("text-point-size", 10.0f); ConfigVariableDouble text_pixels_per_unit -("text-pixels-per-unit", 30.0f); +("text-pixels-per-unit", 40.0f); ConfigVariableDouble text_scale_factor ("text-scale-factor", 2.0f); ConfigVariableBool text_native_antialias From 4255c939d30850e00561b49356f00b68cb22b89a Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 7 Mar 2014 22:27:11 +0000 Subject: [PATCH 58/71] fix compile errors with older bullet versions --- panda/src/bullet/bulletContactResult.cxx | 3 --- panda/src/bullet/bulletWorld.cxx | 3 ++- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/panda/src/bullet/bulletContactResult.cxx b/panda/src/bullet/bulletContactResult.cxx index a55f0c3668..b9adc65c75 100644 --- a/panda/src/bullet/bulletContactResult.cxx +++ b/panda/src/bullet/bulletContactResult.cxx @@ -131,9 +131,6 @@ addSingleResult(btManifoldPoint &mp, const btCollisionObject *obj0, int part_id0, int idx0, const btCollisionObject *obj1, int part_id1, int idx1) { - const btCollisionObject *obj0 = wrap0->getCollisionObject(); - const btCollisionObject *obj1 = wrap1->getCollisionObject(); - BulletContact contact; contact._mp = BulletManifoldPoint(mp); diff --git a/panda/src/bullet/bulletWorld.cxx b/panda/src/bullet/bulletWorld.cxx index 1a01ffb79b..07a891aa4c 100644 --- a/panda/src/bullet/bulletWorld.cxx +++ b/panda/src/bullet/bulletWorld.cxx @@ -764,10 +764,11 @@ contact_test(PandaNode *node, bool use_filter) const { BulletContactResult cb; if (obj) { - +#if BT_BULLET_VERSION >= 281 if (use_filter) { cb.use_filter(_filter_cb, obj->getBroadphaseHandle()); } +#endif _world->contactTest(obj, cb); } From 8c12dd1f84708e9e42dde97de1f3b1619788cf43 Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 8 Mar 2014 12:03:57 +0000 Subject: [PATCH 59/71] Compile fixes for GLES 2 --- panda/src/gles2gsg/gles2gsg.h | 4 ++++ panda/src/glstuff/glGraphicsBuffer_src.cxx | 4 +--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/panda/src/gles2gsg/gles2gsg.h b/panda/src/gles2gsg/gles2gsg.h index 56b59eb89b..490d4da778 100644 --- a/panda/src/gles2gsg/gles2gsg.h +++ b/panda/src/gles2gsg/gles2gsg.h @@ -80,6 +80,10 @@ typedef char GLchar; #define GL_DEPTH_ATTACHMENT_EXT GL_DEPTH_ATTACHMENT #define GL_COLOR_ATTACHMENT0_EXT GL_COLOR_ATTACHMENT0 #define GL_STENCIL_ATTACHMENT_EXT GL_STENCIL_ATTACHMENT +#define GL_DEPTH_STENCIL GL_DEPTH_STENCIL_OES +#define GL_DEPTH_STENCIL_EXT GL_DEPTH_STENCIL_OES +#define GL_UNSIGNED_INT_24_8_EXT GL_UNSIGNED_INT_24_8_OES +#define GL_DEPTH24_STENCIL8_EXT GL_DEPTH24_STENCIL8_OES #define GL_DEPTH_COMPONENT24 GL_DEPTH_COMPONENT24_OES #define GL_DEPTH_COMPONENT32 GL_DEPTH_COMPONENT32_OES #define GL_TEXTURE_3D GL_TEXTURE_3D_OES diff --git a/panda/src/glstuff/glGraphicsBuffer_src.cxx b/panda/src/glstuff/glGraphicsBuffer_src.cxx index 1ae44e9194..82590c816b 100644 --- a/panda/src/glstuff/glGraphicsBuffer_src.cxx +++ b/panda/src/glstuff/glGraphicsBuffer_src.cxx @@ -704,11 +704,9 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot, } else if (_fb_properties.get_color_bits() <= 12 && _fb_properties.get_alpha_bits() <= 4) { gl_format = GL_RGBA4_OES; -#ifndef OPENGLES_1 } else if (_fb_properties.get_color_bits() <= 15 && _fb_properties.get_alpha_bits() == 1) { - gl_format = GL_RGB5_A1_EXT; -#endif + gl_format = GL_RGB5_A1_OES; } else if (_fb_properties.get_color_bits() <= 30 && _fb_properties.get_alpha_bits() <= 2) { gl_format = GL_RGB10_A2_EXT; From d8201030d46bba34031e279fa4e8bbb33627f76f Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 8 Mar 2014 12:17:57 +0000 Subject: [PATCH 60/71] Try cast to keep paranoid compiler happy --- panda/src/glstuff/glGraphicsStateGuardian_src.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index 8f29657bce..5135fcab92 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -1106,7 +1106,7 @@ reset() { _glGetShaderInfoLog = glGetShaderInfoLog; _glGetUniformLocation = glGetUniformLocation; _glLinkProgram = glLinkProgram; - _glShaderSource = glShaderSource; + _glShaderSource = (PFNGLSHADERSOURCEPROC_P) glShaderSource; _glUseProgram = glUseProgram; _glUniform4f = glUniform4f; _glUniform1i = glUniform1i; From c6cbaa92f997d9712387554ccbf7d409a6be784a Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 8 Mar 2014 14:22:17 +0000 Subject: [PATCH 61/71] Fix a range of build issues, in particular on Mac OS X --- makepanda/makepanda.py | 52 +++++++++++++++++++++++--------------- makepanda/makepandacore.py | 24 +++++++++++------- 2 files changed, 46 insertions(+), 30 deletions(-) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index f7da629ccd..5f5d3b5bc6 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -1459,7 +1459,11 @@ def CompileLink(dll, obj, opts): cmd = cxx + ' -undefined dynamic_lookup' if ("BUNDLE" in opts): cmd += ' -bundle ' else: - cmd += ' -dynamiclib -install_name ' + os.path.basename(dll) + if GetOrigExt(dll) == ".pyd": + install_name = '@loader_path/../panda3d/' + os.path.basename(dll) + else: + install_name = os.path.basename(dll) + cmd += ' -dynamiclib -install_name ' + install_name cmd += ' -compatibility_version ' + MAJOR_VERSION + ' -current_version ' + VERSION cmd += ' -o ' + dll + ' -L' + GetOutputDir() + '/lib -L' + GetOutputDir() + '/tmp' else: @@ -1801,16 +1805,16 @@ def CompileAnything(target, inputs, opts, progress = None): ProgressOutput(progress, "Linking dynamic library", target) # Add version number to the dynamic library, on unix - if origsuffix==".dll" and "MODULE" not in opts and not RTDIST: + if origsuffix == ".dll" and "MODULE" not in opts and not RTDIST: tplatform = GetTarget() if tplatform == "darwin": # On Mac, libraries are named like libpanda.1.2.dylib - if tplatform.lower().endswith(".dylib"): - tplatform = tplatform[:-5] + MAJOR_VERSION + ".dylib" + if target.lower().endswith(".dylib"): + target = target[:-5] + MAJOR_VERSION + ".dylib" SetOrigExt(target, origsuffix) elif tplatform != "windows" and tplatform != "android": # On Linux, libraries are named like libpanda.so.1.2 - tplatform += "." + MAJOR_VERSION + target += "." + MAJOR_VERSION SetOrigExt(target, origsuffix) return CompileLink(target, inputs, opts) elif (origsuffix==".in"): @@ -2420,6 +2424,8 @@ if (PkgSkip("DIRECT")==0): # Don't copy this file, which would cause conflict with our 'panda3d' module. if os.path.isfile(GetOutputDir() + '/direct/ffi/panda3d.py'): os.remove(GetOutputDir() + '/direct/ffi/panda3d.py') + if os.path.isfile(GetOutputDir() + '/direct/ffi/panda3d.pyc'): + os.remove(GetOutputDir() + '/direct/ffi/panda3d.pyc') ########################################################################################## # @@ -3318,6 +3324,7 @@ if (not RUNTIME): TargetAdd('p3grutil_composite1.obj', opts=OPTS, input='p3grutil_composite1.cxx') TargetAdd('p3grutil_composite2.obj', opts=OPTS, input='p3grutil_composite2.cxx') IGATEFILES=GetDirectoryContents('panda/src/grutil', ["*.h", "*_composite*.cxx"]) + if 'convexHull.h' in IGATEFILES: IGATEFILES.remove('convexHull.h') TargetAdd('libp3grutil.in', opts=OPTS, input=IGATEFILES) TargetAdd('libp3grutil.in', opts=['IMOD:core', 'ILIB:libp3grutil', 'SRCDIR:panda/src/grutil']) TargetAdd('libp3grutil_igate.obj', input='libp3grutil.in', opts=["DEPENDENCYONLY"]) @@ -6361,6 +6368,7 @@ def MakeInstallerOSX(): #oscmd("sed -e 's@\\$1@%s@' < direct/src/directscripts/profilepaths-osx.command >> Panda3D-tpl-rw/panda3dpaths.command" % VERSION) oscmd("mkdir -p dstroot/base/Developer/Panda3D/lib") + oscmd("mkdir -p dstroot/base/Developer/Panda3D/panda3d") oscmd("mkdir -p dstroot/base/Developer/Panda3D/etc") oscmd("cp %s/etc/Config.prc dstroot/base/Developer/Panda3D/etc/Config.prc" % GetOutputDir()) oscmd("cp %s/etc/Confauto.prc dstroot/base/Developer/Panda3D/etc/Confauto.prc" % GetOutputDir()) @@ -6373,22 +6381,25 @@ def MakeInstallerOSX(): install_libs = [] for base in os.listdir(GetOutputDir()+"/lib"): if (not base.endswith(".a")): - install_libs.append(base) + install_libs.append("lib/"+base) + for base in os.listdir(GetOutputDir()+"/panda3d"): + if (not base.endswith(".a")): + install_libs.append("panda3d/"+base) for base in install_libs: - libname = "dstroot/base/Developer/Panda3D/lib/" + base + libname = "dstroot/base/Developer/Panda3D/" + base # We really need to specify -R in order not to follow symlinks # On OSX, just specifying -P is not enough to do that. - oscmd("cp -R -P " + GetOutputDir() + "/lib/" + base + " " + libname) + oscmd("cp -R -P " + GetOutputDir() + "/" + base + " " + libname) # Execute install_name_tool to make them reference an absolute path if (libname.endswith(".dylib") or libname.endswith(".so")) and not os.path.islink(libname): - oscmd("install_name_tool -id /Developer/Panda3D/lib/%s %s" % (base, libname), True) + oscmd("install_name_tool -id /Developer/Panda3D/%s %s" % (base, libname), True) oscmd("otool -L %s | grep .dylib > %s/tmp/otool-libs.txt" % (libname, GetOutputDir()), True) for line in open(GetOutputDir()+"/tmp/otool-libs.txt", "r"): if len(line.strip()) > 0 and not line.strip().endswith(":"): libdep = line.strip().split(" ", 1)[0] - if os.path.basename(libdep) in install_libs: + if 'lib/' + os.path.basename(libdep) in install_libs: oscmd("install_name_tool -change %s /Developer/Panda3D/lib/%s %s" % (libdep, os.path.basename(libdep), libname), True) # Temporary script that should clean up the poison that the early 1.7.0 builds injected into environment.plist @@ -6419,26 +6430,25 @@ def MakeInstallerOSX(): for line in open(GetOutputDir()+"/tmp/otool-libs.txt", "r"): if len(line.strip()) > 0 and not line.strip().endswith(":"): libdep = line.strip().split(" ", 1)[0] - if os.path.basename(libdep) in install_libs: + if 'lib/' + os.path.basename(libdep) in install_libs: oscmd("install_name_tool -change %s /Developer/Panda3D/lib/%s %s" % (libdep, os.path.basename(libdep), binname), True) if PkgSkip("PYTHON")==0: PV = SDK["PYTHONVERSION"].replace("python", "") oscmd("mkdir -p dstroot/pythoncode/usr/bin") - oscmd("mkdir -p dstroot/pythoncode/Developer/Panda3D/lib/direct") + oscmd("mkdir -p dstroot/pythoncode/Developer/Panda3D/direct") oscmd("mkdir -p dstroot/pythoncode/Library/Python/%s/site-packages" % PV) - WriteFile("dstroot/pythoncode/Library/Python/%s/site-packages/Panda3D.pth" % PV, "/Developer/Panda3D/lib") - oscmd("cp -R %s/pandac dstroot/pythoncode/Developer/Panda3D/lib/pandac" % GetOutputDir()) - oscmd("cp -R direct/src/* dstroot/pythoncode/Developer/Panda3D/lib/direct") - oscmd("cp direct/src/ffi/panda3d.py dstroot/pythoncode/Developer/Panda3D/lib/panda3d.py") + WriteFile("dstroot/pythoncode/Library/Python/%s/site-packages/Panda3D.pth" % PV, "/Developer/Panda3D") + oscmd("cp -R %s/pandac dstroot/pythoncode/Developer/Panda3D/pandac" % GetOutputDir()) + oscmd("cp -R direct/src/* dstroot/pythoncode/Developer/Panda3D/direct") oscmd("ln -s %s dstroot/pythoncode/usr/bin/ppython" % SDK["PYTHONEXEC"]) if os.path.isdir(GetOutputDir()+"/Pmw"): - oscmd("cp -R %s/Pmw dstroot/pythoncode/Developer/Panda3D/lib/Pmw" % GetOutputDir()) - compileall.compile_dir("dstroot/pythoncode/Developer/Panda3D/lib/Pmw") - WriteFile("dstroot/pythoncode/Developer/Panda3D/lib/direct/__init__.py", "") - for base in os.listdir("dstroot/pythoncode/Developer/Panda3D/lib/direct"): + oscmd("cp -R %s/Pmw dstroot/pythoncode/Developer/Panda3D/Pmw" % GetOutputDir()) + compileall.compile_dir("dstroot/pythoncode/Developer/Panda3D/Pmw") + WriteFile("dstroot/pythoncode/Developer/Panda3D/direct/__init__.py", "") + for base in os.listdir("dstroot/pythoncode/Developer/Panda3D/direct"): if ((base != "extensions") and (base != "extensions_native")): - compileall.compile_dir("dstroot/pythoncode/Developer/Panda3D/lib/direct/"+base) + compileall.compile_dir("dstroot/pythoncode/Developer/Panda3D/direct/"+base) oscmd("mkdir -p dstroot/headers/Developer/Panda3D") oscmd("cp -R %s/include dstroot/headers/Developer/Panda3D/include" % GetOutputDir()) diff --git a/makepanda/makepandacore.py b/makepanda/makepandacore.py index e39f5b213b..1046aa29e4 100644 --- a/makepanda/makepandacore.py +++ b/makepanda/makepandacore.py @@ -1558,9 +1558,14 @@ def SmartPkgEnable(pkg, pkgconfig = None, libs = None, incs = None, defs = None, if SystemLibraryExists(libname): LibName(target_pkg, "-l" + libname) else: - have_pkg = False - if VERBOSE: - print(GetColor("cyan") + "Couldn't find library lib" + libname + GetColor()) + # Try searching in the package's LibDirectories. + lpath = [dir for ppkg, dir in LIBDIRECTORIES if pkg == ppkg] + if LibraryExists(libname, lpath): + LibName(target_pkg, "-l" + libname) + else: + have_pkg = False + if VERBOSE: + print(GetColor("cyan") + "Couldn't find library lib" + libname + GetColor()) for i in incs: incdir = None @@ -1572,17 +1577,18 @@ def SmartPkgEnable(pkg, pkgconfig = None, libs = None, incs = None, defs = None, elif (os.path.isdir(sysroot_usr + "/PCBSD") and len(glob.glob(sysroot_usr + "/PCBSD/local/include/" + i)) > 0): incdir = sorted(glob.glob(sysroot_usr + "/PCBSD/local/include/" + i))[-1] else: - have_pkg = False # Try searching in the package's IncDirectories. for ppkg, pdir in INCDIRECTORIES: - if (pkg == ppkg and len(glob.glob(os.path.join(pdir, i))) > 0): + if pkg == ppkg and len(glob.glob(os.path.join(pdir, i))) > 0: incdir = sorted(glob.glob(os.path.join(pdir, i)))[-1] - have_pkg = True - if (incdir == None and VERBOSE and i.endswith(".h")): - print(GetColor("cyan") + "Couldn't find header file " + i + GetColor()) + + if incdir is None and i.endswith(".h"): + have_pkg = False + if VERBOSE: + print(GetColor("cyan") + "Couldn't find header file " + i + GetColor()) # Note: It's possible to specify a file instead of a dir, for the sake of checking if it exists. - if (incdir != None and os.path.isdir(incdir)): + if incdir is not None and os.path.isdir(incdir): IncDirectory(target_pkg, incdir) if (not have_pkg): From 738b2936e4555400cad80fcfeede80e22b50ed8c Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 8 Mar 2014 14:30:34 +0000 Subject: [PATCH 62/71] safety asserts --- panda/src/express/virtualFileSystem.cxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/panda/src/express/virtualFileSystem.cxx b/panda/src/express/virtualFileSystem.cxx index 69a32bd794..c21706e335 100644 --- a/panda/src/express/virtualFileSystem.cxx +++ b/panda/src/express/virtualFileSystem.cxx @@ -514,6 +514,7 @@ make_directory(const Filename &filename) { _lock.acquire(); PT(VirtualFile) result = do_get_file(filename, OF_make_directory); _lock.release(); + nassertr_always(result != NULL, false); return result->is_directory(); } @@ -542,6 +543,7 @@ make_directory_full(const Filename &filename) { // Now make the last one, and check the return value. PT(VirtualFile) result = do_get_file(filename, OF_make_directory); _lock.release(); + nassertr_always(result != NULL, false); return result->is_directory(); } From 9367ad586aad8d5f91333f0ff67cc105df4349a0 Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 8 Mar 2014 15:07:42 +0000 Subject: [PATCH 63/71] Distinguish between lmeta and rmeta key, add menu key --- panda/src/egldisplay/eglGraphicsWindow.cxx | 47 +++++++++++-------- panda/src/putil/keyboardButton.cxx | 11 +++-- panda/src/putil/keyboardButton.h | 3 ++ panda/src/windisplay/winGraphicsWindow.cxx | 6 +-- panda/src/x11display/x11GraphicsWindow.cxx | 53 +++++++++++++--------- 5 files changed, 73 insertions(+), 47 deletions(-) diff --git a/panda/src/egldisplay/eglGraphicsWindow.cxx b/panda/src/egldisplay/eglGraphicsWindow.cxx index 9e82bac2e7..7ef3efb80e 100644 --- a/panda/src/egldisplay/eglGraphicsWindow.cxx +++ b/panda/src/egldisplay/eglGraphicsWindow.cxx @@ -1215,16 +1215,19 @@ handle_keypress(XKeyEvent &event) { // Now get the raw unshifted button. ButtonHandle button = get_button(event, false); - if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) { - _input_devices[0].button_down(KeyboardButton::control()); - } - if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) { - _input_devices[0].button_down(KeyboardButton::shift()); - } - if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) { - _input_devices[0].button_down(KeyboardButton::alt()); - } if (button != ButtonHandle::none()) { + if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) { + _input_devices[0].button_down(KeyboardButton::control()); + } + if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) { + _input_devices[0].button_down(KeyboardButton::shift()); + } + if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) { + _input_devices[0].button_down(KeyboardButton::alt()); + } + if (button == KeyboardButton::lmeta() || button == KeyboardButton::rmeta()) { + _input_devices[0].button_down(KeyboardButton::meta()); + } _input_devices[0].button_down(button); } } @@ -1241,16 +1244,19 @@ handle_keyrelease(XKeyEvent &event) { // Now get the raw unshifted button. ButtonHandle button = get_button(event, false); - if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) { - _input_devices[0].button_up(KeyboardButton::control()); - } - if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) { - _input_devices[0].button_up(KeyboardButton::shift()); - } - if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) { - _input_devices[0].button_up(KeyboardButton::alt()); - } if (button != ButtonHandle::none()) { + if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) { + _input_devices[0].button_up(KeyboardButton::control()); + } + if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) { + _input_devices[0].button_up(KeyboardButton::shift()); + } + if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) { + _input_devices[0].button_up(KeyboardButton::alt()); + } + if (button == KeyboardButton::lmeta() || button == KeyboardButton::rmeta()) { + _input_devices[0].button_up(KeyboardButton::meta()); + } _input_devices[0].button_up(button); } } @@ -1636,6 +1642,8 @@ map_button(KeySym key) { return KeyboardButton::print_screen(); case XK_Pause: return KeyboardButton::pause(); + case XK_Menu: + return KeyboardButton::menu(); case XK_Shift_L: return KeyboardButton::lshift(); case XK_Shift_R: @@ -1649,8 +1657,9 @@ map_button(KeySym key) { case XK_Alt_R: return KeyboardButton::ralt(); case XK_Meta_L: + return KeyboardButton::lmeta(); case XK_Meta_R: - return KeyboardButton::meta(); + return KeyboardButton::rmeta(); case XK_Caps_Lock: return KeyboardButton::caps_lock(); case XK_Shift_Lock: diff --git a/panda/src/putil/keyboardButton.cxx b/panda/src/putil/keyboardButton.cxx index 6f4971c1fb..6b728096da 100644 --- a/panda/src/putil/keyboardButton.cxx +++ b/panda/src/putil/keyboardButton.cxx @@ -49,7 +49,6 @@ ascii_key(const string &ascii_equivalent) { #define DEFINE_KEYBD_BUTTON_HANDLE(KeyName) \ static ButtonHandle _##KeyName; \ ButtonHandle KeyboardButton::KeyName() { return _##KeyName; } - DEFINE_KEYBD_BUTTON_HANDLE(space) DEFINE_KEYBD_BUTTON_HANDLE(backspace) @@ -90,6 +89,7 @@ DEFINE_KEYBD_BUTTON_HANDLE(scroll_lock) DEFINE_KEYBD_BUTTON_HANDLE(num_lock) DEFINE_KEYBD_BUTTON_HANDLE(print_screen) DEFINE_KEYBD_BUTTON_HANDLE(pause) +DEFINE_KEYBD_BUTTON_HANDLE(menu) DEFINE_KEYBD_BUTTON_HANDLE(shift) DEFINE_KEYBD_BUTTON_HANDLE(control) DEFINE_KEYBD_BUTTON_HANDLE(alt) @@ -99,6 +99,8 @@ DEFINE_KEYBD_BUTTON_HANDLE(lcontrol) DEFINE_KEYBD_BUTTON_HANDLE(rcontrol) DEFINE_KEYBD_BUTTON_HANDLE(lalt) DEFINE_KEYBD_BUTTON_HANDLE(ralt) +DEFINE_KEYBD_BUTTON_HANDLE(lmeta) +DEFINE_KEYBD_BUTTON_HANDLE(rmeta) //////////////////////////////////////////////////////////////////// @@ -143,7 +145,7 @@ init_keyboard_buttons() { ButtonRegistry::ptr()->register_button(_left, "arrow_left"); ButtonRegistry::ptr()->register_button(_right, "arrow_right"); ButtonRegistry::ptr()->register_button(_up, "arrow_up"); // cannot name this 'up' since it conflicts with key-release name 'up' - ButtonRegistry::ptr()->register_button(_down, "arrow_down"); + ButtonRegistry::ptr()->register_button(_down, "arrow_down"); ButtonRegistry::ptr()->register_button(_page_up, "page_up"); ButtonRegistry::ptr()->register_button(_page_down, "page_down"); ButtonRegistry::ptr()->register_button(_home, "home"); @@ -161,6 +163,7 @@ init_keyboard_buttons() { ButtonRegistry::ptr()->register_button(_scroll_lock, "scroll_lock"); ButtonRegistry::ptr()->register_button(_print_screen, "print_screen"); ButtonRegistry::ptr()->register_button(_pause, "pause"); + ButtonRegistry::ptr()->register_button(_menu, "menu"); ButtonRegistry::ptr()->register_button(_lshift, "lshift", _shift); ButtonRegistry::ptr()->register_button(_rshift, "rshift", _shift); @@ -168,12 +171,14 @@ init_keyboard_buttons() { ButtonRegistry::ptr()->register_button(_rcontrol, "rcontrol", _control); ButtonRegistry::ptr()->register_button(_lalt, "lalt", _alt); ButtonRegistry::ptr()->register_button(_ralt, "ralt", _alt); + ButtonRegistry::ptr()->register_button(_lmeta, "lmeta", _meta); + ButtonRegistry::ptr()->register_button(_rmeta, "rmeta", _meta); // Also register all of the visible ASCII characters. for (int i = 32; i < 127; i++) { if (isgraph(i)) { ButtonHandle key; - ButtonRegistry::ptr()->register_button(key, string(1, (char)i), + ButtonRegistry::ptr()->register_button(key, string(1, (char)i), ButtonHandle::none(), i); } } diff --git a/panda/src/putil/keyboardButton.h b/panda/src/putil/keyboardButton.h index 94be8d3eed..cd74d7d023 100644 --- a/panda/src/putil/keyboardButton.h +++ b/panda/src/putil/keyboardButton.h @@ -66,6 +66,7 @@ PUBLISHED: static ButtonHandle insert(); static ButtonHandle del(); // delete is a C++ keyword. static ButtonHandle help(); + static ButtonHandle menu(); static ButtonHandle shift(); static ButtonHandle control(); @@ -84,6 +85,8 @@ PUBLISHED: static ButtonHandle rcontrol(); static ButtonHandle lalt(); static ButtonHandle ralt(); + static ButtonHandle lmeta(); + static ButtonHandle rmeta(); public: static void init_keyboard_buttons(); diff --git a/panda/src/windisplay/winGraphicsWindow.cxx b/panda/src/windisplay/winGraphicsWindow.cxx index 1389b1a637..25090a7069 100644 --- a/panda/src/windisplay/winGraphicsWindow.cxx +++ b/panda/src/windisplay/winGraphicsWindow.cxx @@ -2605,9 +2605,9 @@ lookup_raw_key(LPARAM lparam) const { switch (vsc) { case 87: return KeyboardButton::f11(); case 88: return KeyboardButton::f12(); - //case 91: return KeyboardButton::lmeta(); - //case 92: return KeyboardButton::rmeta(); - //case 93: return KeyboardButton::menu(); + case 91: return KeyboardButton::lmeta(); + case 92: return KeyboardButton::rmeta(); + case 93: return KeyboardButton::menu(); default: return ButtonHandle::none(); } } diff --git a/panda/src/x11display/x11GraphicsWindow.cxx b/panda/src/x11display/x11GraphicsWindow.cxx index 41d6a145be..ec7c79a48b 100644 --- a/panda/src/x11display/x11GraphicsWindow.cxx +++ b/panda/src/x11display/x11GraphicsWindow.cxx @@ -1392,16 +1392,19 @@ handle_keypress(XKeyEvent &event) { // Now get the raw unshifted button. ButtonHandle button = get_button(event, false); - if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) { - _input_devices[0].button_down(KeyboardButton::control()); - } - if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) { - _input_devices[0].button_down(KeyboardButton::shift()); - } - if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) { - _input_devices[0].button_down(KeyboardButton::alt()); - } if (button != ButtonHandle::none()) { + if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) { + _input_devices[0].button_down(KeyboardButton::control()); + } + if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) { + _input_devices[0].button_down(KeyboardButton::shift()); + } + if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) { + _input_devices[0].button_down(KeyboardButton::alt()); + } + if (button == KeyboardButton::lmeta() || button == KeyboardButton::rmeta()) { + _input_devices[0].button_down(KeyboardButton::meta()); + } _input_devices[0].button_down(button); } @@ -1425,16 +1428,19 @@ handle_keyrelease(XKeyEvent &event) { // Now get the raw unshifted button. ButtonHandle button = get_button(event, false); - if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) { - _input_devices[0].button_up(KeyboardButton::control()); - } - if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) { - _input_devices[0].button_up(KeyboardButton::shift()); - } - if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) { - _input_devices[0].button_up(KeyboardButton::alt()); - } if (button != ButtonHandle::none()) { + if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) { + _input_devices[0].button_up(KeyboardButton::control()); + } + if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) { + _input_devices[0].button_up(KeyboardButton::shift()); + } + if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) { + _input_devices[0].button_up(KeyboardButton::alt()); + } + if (button == KeyboardButton::lmeta() || button == KeyboardButton::rmeta()) { + _input_devices[0].button_up(KeyboardButton::meta()); + } _input_devices[0].button_up(button); } @@ -1825,6 +1831,8 @@ map_button(KeySym key) { return KeyboardButton::print_screen(); case XK_Pause: return KeyboardButton::pause(); + case XK_Menu: + return KeyboardButton::menu(); case XK_Shift_L: return KeyboardButton::lshift(); case XK_Shift_R: @@ -1838,8 +1846,9 @@ map_button(KeySym key) { case XK_Alt_R: return KeyboardButton::ralt(); case XK_Meta_L: + return KeyboardButton::lmeta(); case XK_Meta_R: - return KeyboardButton::meta(); + return KeyboardButton::rmeta(); case XK_Caps_Lock: return KeyboardButton::caps_lock(); case XK_Shift_Lock: @@ -1963,9 +1972,9 @@ map_raw_button(KeyCode key) { case 127: return KeyboardButton::pause(); - //case 133: return KeyboardButton::lmeta(); - //case 134: return KeyboardButton::rmeta(); - //case 135: return KeyboardButton::menu(); + case 133: return KeyboardButton::lmeta(); + case 134: return KeyboardButton::rmeta(); + case 135: return KeyboardButton::menu(); } return ButtonHandle::none(); } From c897b0ece18eb0d344ce0fbb04ab02b244d89ffa Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 8 Mar 2014 20:09:58 +0000 Subject: [PATCH 64/71] Remove old cruft relating to Mac OS X installer --- makepanda/Panda3D-tpl.dmg | Bin 523531 -> 0 bytes makepanda/makepanda.py | 90 +------------------------------------- 2 files changed, 1 insertion(+), 89 deletions(-) delete mode 100644 makepanda/Panda3D-tpl.dmg diff --git a/makepanda/Panda3D-tpl.dmg b/makepanda/Panda3D-tpl.dmg deleted file mode 100644 index 55c6fa12a1659ecda2ec5f628f0afe50ec8e099e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 523531 zcmYhhb8scj7d`l*7u(jvPF`%=wrx8T+xEn^ZQJI=b}~sOnf-isYj=OAy8G7cs(bIL zTV4G}pBB-vVqp>1rc~7)?=oQm0Dx;K3xELtH~<9z2n2^A0g#pjzQC}Wz^5r-^?|R) zbdu##OPTJnUr;Y<2?c@4g0&0Ac_D7(mDfhZ&3u zaGHgSkV#q$8w^@3MgdJG1p*>K6BA~Gh&XbnWpOw`Wpc3qNK|QbbSY8EvhqvR!s3;& z@{+CmY@1j5>e(A@0~d|J07X7I*7#ON&ZQg5DmPp`ODiZJ;IorygJK;3P3ZOHs+x*5wm zvf|4|B9>IQd_x$ngpIp@Em1W-{VkS+l!O^&ghvLQ*b?>YFiGPLi5N(KUqg@3aormu zy(N;>5*W+!tRUu+)#O0$71hpxMHIaR4jOf8h`- z5F|-xuq>={3pOB7%2=|6wW9PvcC0*Nveit=BAdKq$;c2824uo&U6%P@WneZQgwTKC zcve>btwDvwLI40@0TBQK{g+GVKT5#B|9lp}1PEYV0AK|H{@Vw_ijd)1Wm{Jsn=KKg zs8TLgrdab+N|uGlrAXu#3;%aaw}Of#gv%=ntji)&7bftm01N+7!vp3LLE4dq$jSKA2{R7G7!PVI%RV{J>t)T8{4$ zmaOh+LWYYr_#JM4=zdr42TNQ?vVk|YS$^BMrj@2Ic!O;ib~!p!-;k4yh@p*a z1+fg(DnmV=QrjJcsmqo|V6@7E#W)Fp^WtDG2S$=k6chwB%pNntkWs72z{J&e2*K?P zAgz>bxy?X_CS5O+$|uc9q?f`lf~5NR^VrhZnV9cfUB|;u@uwf zxkKE(#RwpMELzQO4`-<%%N7(psKm0lwmmsG6m7aApV<;^F?m}>2U)Dw(4H0>fQ=e8 zfY%Jr^*mvbOU*3J$xz!K%4C>2YP4GTxiS+er!l&b*WP57+cTm~c^aWcq2S=Z2^sOSZIL!?!S*z}0FP- zj6-26`Ai5N!ypcZ*ozrZu=8|rkfyhkJDK)rCyE=v3vbzEwDiL^eS`=?D$2z~2tsF+ z@J%k{35OUg9Z#tszgj%SSbOR89t;IMN+sG4v5RRvSRB+!TgU}jgg8BWpf$KC?k(#k z4Ztn@%_63^#3vXmTj%nAbPJ*DR9>Flr#ud-rNm)E7>xoL*9PM~K70pBxS|7EZ*OGRj`U3wZrAmb z00&<{cIFoV1#ROw8p#-p|>@D^nCK(J;s)n^1#E9)*^~`S{zcNz1%`>Wf?QI zM5|z{jYfP*z|<}3vhBsp`rYYknFr6nbHG0`r*@|xPJhY?50I<|kz|;@x(iRr1NYUo zs&?fSBHEJ|h~$$qX#wHrL#H4u!J4uS?vkS>l&a7sXg?AN+t}#C7{J2E054>+YzsjV zlq=D?TQq3rSZ_!q0mTnn z236aLdF#oSk`~6|tl)yne}Bfhae%-JA4aLOa?+EV-IrkuXFP1Qj&8hl2V0y0ze=*I}fd0XH!n6aO?EH_OX~=Ap zWjwFDth%aDwp+ejmTsY@>tDLPvCc|0MCuWe82E&dr0b~Vo#PmV#4}-t+R>Qn3}e-Z zm~Y6Lo)SxTIUy;d9(%FQU^^gDjh1?Sa&;@AtrE^2Y}HaGm)TP*y^}T zjf##PSA;^BcA}<%I9;}~z)IiQ^9Z>KPF}Eb0oKTEvkO?K;6FcUMO}=lnG+{r#9`WNDR5_IuDL34==$TH>HMcW`c%=z+1Ds{g0Mq z%{|%}byN(9$n+5iF_&*QPyc=&<9^yaJ^D{CCOyLW4&VK>xk?eDw~DBudz5=HK_I+b zdF3_H&=$S+s*Ap*QyN23n7l#n$(F!b!$RlLj}s>$E=2BhVEfbLn$C6#=S4Y3iW*)& z-cN*Di7)Xrrg3jYtSpbUr9NhG)*LVVq4hUC8!F0}VM*DdttJ0n-kxpBiy)y6#GnNQ<0Soir==~Bat9DKJ-Yod#1VbQ^A?ogvkWAFPG0k#`d{McIx4+9ys`-{N0hCfa67F;w}p!Geaid!wPeQKi8XEJz9V zL7*O6B5FY4G+-MP@~qD>>`*5|KeI-*6XksZxbL%q+_ufVP#dI=8Ap!Ni4sjnFr;VB zEt@0h5(mt7Nmul&6u_z5EZJhWBXhKQZ=5qmAK}a9qv7>&ePYY9um2S54p)oF-UR@M zGD(6@Sps}}>I|~u)7KrAqdZM-XM$?jJTnSKJFop(w&SU?5c;^-f29@%MQJH>;c5`U zYZp!%cg}F0+D7P=+Oyn8HR8< zz7mOF{>yc`uv3_|GJ>X*&P_i-W$a!1(8V+~Zyntb_6^++_B8qWwzZ~Q(55g2kTq#} z9#dh+3j|?u(xB*7i4|0#{1^;D5#W?m$34F{IwrH3$QZR1 zLdbnQWwnS4ArRv534y?wX!ED=$%vc5-eBLuk#s?YfoKD<+7y1w=znTAido$&IY8lg z$#GhP4fo@>MmWsb3o3BAImg%5cJ1HMyvA3f%J38Ow$>|*MMc*Bx_vjzbri@@z2n-F z!<_m{>zI`6PJmGoEvy46rm!$8niAMVMomE)9t290y6DU;r|hghzeVx z^J`9#MOG@uI+>7VvNG-{*3vNLsU9SYS`eXClc-9A33r1HJE_m_F>0(vP^N8rEHM~jQj#L7 zUa@d~<;LW6IC>rMb_mj@-~aeUtrn~Fg^k=?t4a)GlJ7*j)$em*_3NfH+8$%Ue-{4m zvL^&P88%Zb2lJ)|AJfIhUUa+8I}>{rOD40R>zwVhPFwX`KZD3ApWD}TAfgq%W(pl) z!{nFrg>tZMTd>prvr@pLXnC&A@j&l#Ge~6xQ_)S^GJ%=MKfAGm^Vt~+tL6m@cD8ie zKK7<2^IgNzg|fB|QH=A3Ie%245*alKEFS$6JAK(~i7VX@AH6)$PwwV@2iPRT9mC(FDEj%DBDbX$qd!v8& z-l_vtKBm0-T<~$QE5skNZayZuaCtvm*0vwUhI#R|ac!Qr{B+!jhPRb9|5mM-}NJln+nb4RzIKxrIH^rYa z$d;Z`NiKZd%*{gIdrf;(YrEOb$+k!$S`VVt0%wEN!cF3Pe_Uu)2T%~fYi9M{f0&ceKWih0j*Rj-R?)A789CiHtQ)la=7-t67$ z%XTVxCkp*$w6yQ<6^^_Zt+DF*w16;12oD*EM7L&XpMCyHx9%j?5Uj4+XXj-x?_l8? zMK+~#KQ!Y;UshI0)#-*zen8=`Q-2zBG`MLxTn2vXE1u*Z@b>2(G4@cIB^N%)yGDTQ zs{=SCM7G;UBqRk?s@hr4_j|^8UFu-JH=9>76n`tN;2tfn44PN>4ZPttGNXSDpfp_p82o{GbU2T^S&G+Q9METe|1IA*eLEBR3tFjKN{<5dbC$vRm7s zuxXTB!@g}6jd%~amKPC$>`g8^f&oI}(O$>)rcR6m*eS0i} zkh>>4ZyWOU&RUPgUh|`#rwiVQS$Xy_;(hp-E5A!Cdi4e{T4n$Y;9j^?;V~)qSW%;wi{?fx(vK;ffD@ z$j*BXnMKHk(+Ed|ukfFdG?jxgd+wX#Efz@xCaM+DO6SMF7ji(e+B5Qdc-kp}C4L!` zVo-{{p1v=^5YvrFKbqw!#g?B7V=~v;FpKF&J6?O)Hxg{d;N)wv8#foEh4Mm9Vl|x3 zZjBQfSc=KV@_Z7!+`Vi!^J`4V_&jShg{&fkd>G5JQ-5@K9E)pb{jto&S+r4g70qbV6E<?T;OhXa>zHjOA{&ZQb>Q!_mga>>?20kzdIeOF%bW>PNTZCBNdmGu4>oNjNnvS| zfqpoD1gt|}-+F-E#J+{up>l^vIK7lbQN=-^S20B~3bGk_-JA`#UKtWh4xbbOq{^DL znp{3^ok~c8l{g*Q#JY^m>znCD*R5Js3QJh^Bz-)>bei(lOL zh(YP0L(kv#_jWvW&fwoJVSj%6tD54!7c8G;k?9u#JfjS6|BABZ=6>3EuQvr$-@37h=TS1=3{0=s>hg4Neza_ zM^&%Ls{sf zS`jn#G2WrzIXXc-Z8VAi<{Q{Et}(=To&Jgf-*=k&`bU)M`a)o3satV8Gfjnv6ww;}$7uya@R z7G=Hp4qUAYm0F@AS_kH^QAzeXb!rEr)uz7 zWeSjK8%R#SblFAvu&@gVaK?rV5#kxIU>za(rPa*tvg0N`wXz&UXDSt}A812Adj^asHEjKBe*C zsjg^982a@i(2&Z?b`W%PqV>&2J~e10WXmRNI2mXGmXrjW%vda5n3ae|Qfr+5D@x}8 z-7}>BhT)CPZLpiSfx4U#5Y@u%5U0Tr&`WcJ?1kib9c1ocE;Z2Jr;<4$f|8WMPLh?Z zHWOEw1roYIGT+1ApJ__R&fw~JwsTMAB}~uRoe$z8glUW;Wdn&Cmrl#-!-w&_H2s zDF_#w6QN9XVYUu+#;jk3K_krI?7Z~Jr02`_0^j_0RPM0o$^zcnn#fjE!)ui&e8hN$-i`s4{Y1ISGSG zd$H|x{FH+=wQ<-v$wl!1m~cKA7#3V&7bz5@nxB92Y3Qek(!%l_+k0SbBdF{zx}}gB zSf?BjPz}~#in1q3Or{3NF>FKiDs&XovnpQf&hm%MolLt-QK!eN!OYz4mH2i4v35Ev z1*x*Zp~?zd=5p7!cc7=58hKlRi6tYDI~`Fw1PprBPr1f)<&?zPS-8W)t230eT3MnN z)P?bbA$+q+qCWmcH_-s=Y*g;n|DGwp#IGt@o6pXCH-mX#&%_~lSrN3)(Bm?-0oFR5 z#AbgRCFAM?8O|-w{Xxq5Mt<}KAqBf~f!bDk^x(hM`@PLug#uZn1v%SH>gBM1z}H0D z;biFxUdeEwWT4jOaGYD!b>K2J-4=&EA_`bwU($fbbpA>I#moKiB)40SY(Z4cpk-}VtQ4FSR zEHZgfc8+=*e;NkvDZC7^%*WLu)S*+S}V$@5-0<@IZieIFli&x;KrgnrkyxVr^&QaQ`BKZ0K`rCZ(zm zFxW@>w>kfR84+>is8TIbM)8F~oAP-0)C(hK{OIy{RttEzB_Pnm3sG4dU=f6}kOiA0 ze@;%mvSpKUFE*4wb0H{EUU{jw%2{MDued6x_N6GcV$V-XRNxa=CP#C@FMpZy<3ENA zFB}C#1rU;z#iH`il?C>q|4CjDLPksak z^0N!dcwz9BS(iKyDpCsbQ_8a<5~Tn~4pG8%9BcH}W0)fhrvL)pc$Csaq z%U6sJk}m|!ii%qC3n?mJ`ea(I@FiL;lNKhm2w&py6-&f{%$T#ymRGjKHlxc&%X{~4 z&rOb1-qpNlA+N5uQt^d?sX8tugX;5r#Ria0Z4tKwTf9P7WTN=w?%j_se}#rKtHtt0h}x7MZN z;}v>cGQA~;=at1lB8g3QCMinC`f<_!8Qd)&1XW9Q|BX5xa}H>6l;qljbP1EFxr+;* z_8^R2CqQ)_iibhCK}h{GD@+}}2NJC#{VlBq%5shne*F&p$L%%wG?KDsIN}wGR)B(d zEp(#D^GKk>Tu8b_%d)3`>|7GhwdsUW?sHV9nVT&#wy0GHYME$C4ZZgk#^rip znry1Uu(u;#*3=onz^;)pfs3qVgwldEVfR+cre&qz34;nu182=hG8dPJ+{^T>(fA-||IxkN1&pIMqroIgbnKa*ycx4{;Yg z4J*~UL)xU1`ZL)3bn&$pf724DWwS#F<4zPnu&nZltp~lD$zs9+sv||^9 zhru%4<_WjXHo9KKiX_zmj;BH#J^{^OiIziZm1!_0ddnk1Y>MeT~p}D}6-gqOFWxW`}m`qZmBTXHH4W zgHKN1p6h}bavTH6WF=L+E!y6$+hjHjVU)CP8PxLJN`f2cfCdLG6p@@#8=E=s`-r|` zyZYXRN+hhzRB93HW{5|)z`BIeT0$LhRqEMZz$mmVF%nqK znovNt;&S7EFBbZfSslWQ7ZWIhaHwUM|4lCfm{Uc}V-sVmYe7$sv1chFK>~!9P34*KL5KcB~>{4Uc zVA%>vJtZ@qkCIsz6bAKggU7AEF${JzPz-@%abrf0t8~m9YEO%;vCZ=+Q-7yK^P?3S z)hB*;Y%>Ahr)-Za;cBRTz%86WntomDxcRNR5axR^E)lICdg*b(ztOs znOmN+63sl)deX>9V323fi=iN~$R-6MXw9*;klZG1br^|M{3Dm@(hgG}5%nrE-S&CT()+iUSCn%s!W;`SwUd<+p;y+-5aEw0t%e-m|-=sk|$F`Y+j!PzQBe%c0{HsOV3i zVWj!nYOAXVTnYq~1655J;L6;$LjOn~UBu^P&Jm~Ky-90;X%nTX5PUkX5GL}OAh$W- zpP=N=qbpX9zjf(Tw=Y9;`3<~82wXoJ1RjMScM@m4X0bQ6eIq(%GtaVDpIu+*&MhgnH%@uyPK(J%vG6FDfV36KRQ`mT2kFP zHYuQ`d-F`T=%HEi%4#Rv?9UDU=hl|TGIgS3laB_&emvMZXY)OL&6zOy87RP#|1WH# zO$ym)Wqi^Lb`jc(w&(1Ihks)wdceRj=~g|;nOkM^bZtv#lx1(mE41Vt1c4qZS*M1g zCb(;1afG*gTvbG?z(2quF&jvC5|MNmv25&WVSAbBUzLnMvvYV!M99%avXP)^mCFXc zxqoFAvt$Jl!b?c8nLTcLXPTIOYEGnMF_Rt*LfHqYp;1qQpmD^3PT5zaKOE*O5c;XIS z@o~>ih_L>6(o&F5(@SuD?)s`v7+Jw*{v307IP77G>g+}{XAvErq@J|tDiIJwgYebj z{*{4}mogY_YLp*Sil#+T!nt;f;U-e$jIC;g-x(sTCIgAQY_d_r)`PbWFDsO~+3KHL zqG&VQkJfN1ff(X*opnxo#5#x{Rfm#B0`P{$JCn-HD2iRYKIpcrGT{h{hDqahIw z431v?J&Z=0xJCkkrb8T>;37t+WEE~aiq$cMH9Lx`v%kg&6{y6>sgW)4X7ul4X)g>M zGsw)BP1{%MtW;tI>H&`opl&kT;&|_)*=OVM+d>F7v=B0+UD$wG7iK>K>u5 z3bdwZL=DWi%!+zt3z;S(vdVC@t4M6VB^iD>bv4x`y%n7o7cIMaC&H1$7W{@8a9~m5_Ku)E2G-THWDSXHk^h6ZHWoi1CJ*Y?J1*D3wO*udzS8_`(Y#AI;qo9 zZ!8tl9EiniT(XS#o(i~syglzsroowSOgm$N+~zYGUW4fPczpEfF9@uh=X(u3*316* zD1*n7kdhQ4G`?PLoduJjK21rC=JFMW=p1&dcH#*AnAf3NhH z1gxZNKtxk8RlBe;TCv^?0uEDdGGVK>RpQ&$R0U00yv`~QibAb#raT$Ou)9ZF%*e)R zLB>X~kvtv5qO&C=uqBNO_aLM6W^HTF`OF?I;U8+aVr8R}1h_aGW<2~*6Y<#UH%>(U zwjOO=)$^O1Wy2+_@Fuuwbx9Z%+#v zRMT>7wm!eH@@RdM-&I8@edLLj37eOuaPO$ky4M-HizCU6OCj`i9Jpwnm%T2SO8B8{ zPhWN<5v5(|wyIcY_ApcEckTfYsDs>AP_HJwIL21Kx6QD6foA-AmqtzB$kXI}4`z35 zkDjcDpgKIpoK7a1gC2oewk&_o%N0j2R@RVFzR8u!pAKy{D z>t@*6o9eDiRo{Vi-1cU?5XV@4-TvUekyYT=R?@5CmQN>UNIJdJJ=EQib?@1fL1waV ziB-Eay$mT4TPR{2@z8pu6OA5y@MQOWJw1)ExDXNS5m>AhsI9dyjScA<<%6rLaHzu@ zP>E~I?fY4G{z-6ir=)d0#SPPT3CsN}T{z3?!7t?`A!| zjD`(j#^nt0hVX3gj;$>rt~PFEKgC+Mb8*9s(}haW=tQ9pJ~_u~TLc5)Mw_9rDGr0Q z2xmK-3~P<_W#N(h){>6twN)O)OD;;^EizAPFrnCzPaUCRdQ*Dq))9+-XLXh>dO=n$ zDGN9zm20wp{8F9j!{X4UrAxYqO;Rk6nj4P;%$b^-fDdtqrlw`a8*0!P)KC7T*C?pK zo?oHd`o_&A*qb_vrRY5}JEGD}Yn(2SV-8{dLWS}nTW2M9Q%*L*;0?zrP{Sa*Dv)H* zZ-S3s)ncd=@a48&=_W&@;N#1NSs-bHc3qygKyAm4T2CX7zr~EyBTs4>j`S;A(%CaH+)eIrVamgry^o%~7#COIO`| z4YzLluxDLk%KVOP_w%xRc*Vo#@xty|>Drm)`MJeUX>@#wwosZBW`y&)t+hRhl^n@Y zHM2g_{>!*NWu|+lR~usCQ$O#?Kk~{lsn8s(AXo7emCAY^^ zL}*QBQh!ClD{hK^9OZXvitMv)U^x$eh;F zWb`5iDBf5Ve3w^%VlOyrS*i!VpvK4n1f*x!}X9;RBS5`Q}Z^V-@UKZ1M$==oLHN#}1DrOlCMcZAM&!Oh| zPcE;rP;XC~UD2_*6G2Cwcy9hC>08zw$d3D%!iPgpR*{VO*{HZ#t?!>eBxmk;8DqfJ zysp_VIXFZhoyARCd))HTMg61eo*x?v!3dvHY*#k)Jevwt5BA1zkP1qpyjE9**8Vhb zd%sWTX{Wiz%Z$7F_Fp)BgX=HN?*8fta(@N(lUW2N$ZJ_KpoLa{~I$h|xDP4QT>*Lyo-()-T1E4PRr5wIS`HDl&a7kgM=V zU7DX=mdKnuryP^lmcKs&Mo=rpmO>HBhy`F^E#~DfF0Ct^ieN6AhBN%${nPhHs`BFh znTjBNt_Ux%zHhkWcSn^HvQtl7-eOA;&_k+-je2};U%^S(^2_5piQfF}3@}M-j5s}@ z3c8vD>rf9=xO?K3F!<$rv!Q%n$aDZSk3g3J=|T4s`tIN1&(o_w{YRDwpGLacDKpg{O1vr<$X<2exS*6fyeH@8V@~xm#>|x z{9XwGK4?d+(6>J}s@=m|D$(kXggD?rUe4@JeU|&-+-?a!$^3d@4dcO(ydFX5!|eRE z5+}DHIBn_DC_IZx7Qu>!2!3$lLo1s3IxD$7kvIOG)P+Z_T|qYFzP^hvJ#B#OPT;Ex z@R8dhFUYk;#M-l7`6H+$*vgv6y5rbHr`hjpQ#LV&o*l!ei$j);Dq72E@S$B*cS)3nx6+$v3(2@(pS@MxG9yK+Q5xV0f#SA-acBGVEB_rb` zMHMUL7!L+e+2W1eQD1i1;gbiyUC$ptPw6!zh;f=9*TEA=K$Q`!aCq;EwfW~7p4D;; z-y`sX4Cf@0#tVZARw=@j5K@uV!}@84+T5QHMVJyF!n<1{082*5;N?iVwl>v(^$8)! z5S7qd4uTvN)ODG)S{07meOIj~tsUD=9BlQ(;Aj}9t!dp@Oya)~KLKZumUs&5af zpZ6GUYc|c|MlxWwqIsrWlw1XKQPx#RoL%q&H6xir1zvM`I0^A2E1Ze5^PXW79}zix zi+|j8yubg_&5eI7$HD(Sc2e=Ij|NN^+J;|dOF#mJ1y@vgtj1Ju{EGf=Ra4s41b5Bk zt~VJ-R4dyeWovISrsvaWQ*eMnIpS$+OfALpGdwQOZtIY^2+ebg|CR8Auk7iUOzbZS zieb+`x2QPXmqUTS9)3?Wl0(EUSA$OGpL|bu&If+E<)j7nHF3rN>&29|)b80WZ)x|} zxal`xnn49Gi3GX&$-uQ#CK)9d_sX|_U1Mp7x}zfK#PeVJ9$9Bv4_R>%(e8a1Y(bt4 z7o6+&nDwV97PtRuUPadbX{sK5Ft(L?_xtNw!FkPR;PgFYr+8I=WLhe0$$*WH#3Iu*@JXtt&qpAtpTMc-^(y>j{m>jFUr!fbU8P0 zYwUXjq5gP5)RB_U;KDKz$Xu7JT_fPa5AmM&WSRrZEe~pAzMo=KU>HFH95Aux(=i4v z4vI2t%jXJ2K!J}-g{u{xrI)UEBFb^o2>Xe`vWrVHh}Lh6O6dYX!OcRIk7KO|)U3hy zk<3*1I1-Be&|PMj@|d33XLs!!+$h(}NwKJ_$w;B!vNRk^V3(7wctClc-omO68NHw? z7T#rW+~=Flnv&(odty?Ru`MhBfPyg?r~-i@_;qpUCnncM1{HILz({Av!DP9`HOLUW zTuinRCL)kIE04hCk+OgHn}46%C(QK9cuX^9FLzZyb%}6b%4*?eKTZBH+h)IS(SgGi z`?~8n({o!n!P(X!aPnf)LYj$z<*mpTkCmb&KE!JL@BZ4%TtdMgB|Jx}1vPNZ^5yf7 zpx6g*0Zq3~QTusO-xQCgqed~IzjE)&Vk{4(vYbIfdh!lI}QF|NK-u>S|l#O_OXLN*1x8 zwSJFzrjm^wHcJY?=>Rv6;cAN@dBaNX7j zgbE{}-p$9uPvUk^mOOXVCLUq5Yp0$s<3(97GJ%~l)efH%wP!XSQ-Ow$E7j=eq9}&^ zt3_+Y-9_P&E1Va2HXySO)d3|ChaD0OHV9kBD?Sa9_9Z=-jhB{+povnyfrX_?t!)M2 z<|`A~q)c+4i(l@IjoJOvO>^tFVdcziE1-2)f}PKymcJP;LcPv@BGiqsMFRERVwC&?2xgQJ$bUEk~5#BHmifmM|e?eArkz2(7$F?&W>d4=E1;3mB~X@WjqKBV{(#6d$~${TDV(PXGSuo)JJ({0EH6v^;4wh7xef$^C! zTs!5=ZOFHKo$rC!GqQ>r6%wtde3#{Uhn%@6!#HE z0YS5gp6DQTBtATlI$sDQRFIqpyw%%69;E^VDcTGS*gW3@y`ZUSJJWP1@;h(4zG5_c zZp=bQN#!W#=VW=yVK@?&_inK%@STlU-*sorNPW_yx8jj?)u6jpI)B1eTvQK)n`d_7 zX$cmS5-O*p&;ltzq9k_lSePlX%9vG$jWWuuZprSENfmFhZQbwQ1cE1eP8f6#m94+) zd-k%lni0D(11X2HQ68drY!+iyG-k7bbNA8pBXVHiqtgy3gZ*Zxmw;z8r_#bBVIlrP z$l-wu6qrJLh5LNx(Xn(~0g#?;9`l_ZTXKpF3KK}f_}kt`c}__K1)C=^e^=SMOo)wT zI*#YQKh;XV6~~!bCm_|4H8_*DarDZ{r<35^wRjW>Zyn$9`cLYq3uMP2-k-&({p#Va zgA?b}^(}L$X@{Rgh%dX39(g}Ut7k%7%aV5`lqB_6os7c;>qp|XZ?y0VJH=pAXntHqEWhdTVpyb zQ04fn`F@1ISe~B?W$JF z*pk?CCwyEmH5{B)jrx%sw`i^D#UsadC}b+sX)AXRW0R-ZV);cXJN<>}@S1$fS?wS0 za}6cdlJQ?qxl|#|C=3F)FimVZ^n3XCOw$+$x?KH$_uG$t z@B9Iqg_x?d&qhBEckEZFi}W}y3`hFGV0|j6_1lQCVG=M%sM>f?sOrR5C$z3aYPN3i zd0;gEGuQrm9Nm&)xq)07@);Kvu59yxrWI=o(!?4w3-k_bf+ch9!1ChgiBpOhKc_=H z5=KjE@yWkpta+%@e^@!PYVXX`vI@N4!UhwzGQ>`U%!gpcV!QK-w=Qo1`Y*o`@LzA0 zcvV43LB;^1&K{f_Y(*Flq}_wY>gbg=^J-gDQAUbNhE#4M{1DE8nZ`pClobcGd@y7* z##HDV7gSf#LCGKPzN$bo5gb2Dm%yq;Q;q5gXm)K=PvH_VahZR{ZqK?F@Z97DZDq0~ ze5;arl(D@6w2;tHKFHO#dMl*;v+=}fZcVIkT%W2kjk%C0yT=qfwXH+pa=g;C>BUWA zzJqvq#xjj|N%QzKL6&VjbUAaHk<7^)^gp7qQ>8y4+-N%X!{0iEHQj28M}DdWq(~-u z*NAaMei4IO(sg2tq02NWRMz1JH|6TOpW<0(l1d>Af+qI#=?Q?xI3$5d7tjtauwNJ% z@zQ@nhhO)a`A%bl=KL8jB6*>3?p&n@N@ebA>^!%%EABzHWVx?e!$-U~DGwyNTn~K* z?c06<`~9aOC9EYaOdw^V-s(x?5m9UJKLVMH^>>;={nz427)Z<+GE<%%Si`A6!4RJjBuEIguZJWCir3T+Yo@nrGr^gQdV{EK*NUcZ(;UJus4l zWcA0&YGH@&`kCP(HmE^Vlup!RkXJpEeNg>wcUDmLIZ_sEYpPUiNu}BS_E;}7-Tv(m z92AM;v#dkAJOnFuNWQU7`8gE@XdUaME(-8&$@0VWh@@^jgmaKr2*Hy}`8NsvUibBa zc=T+>><3-#Kqqif;+C_Fjd8C;R6z5A6V25&cEqRL8R>W~=)XEtRd<EcG2GDZ8w> zqwJiWI~S3u2K}yt(c@~YS;nKxBqj;gw<8dWvE?|r6MZ_?F`K4-1@JKE8~FU8^4!Ltx~YZ-us zge*s*^qST+b0Km`VA$PvMtvki_jWy@S1ckEE@#LBI+vjyZ2_5L8j6_>{vh*m0^Rko zee%qL!~8S%l#U8!k=c*G{-pv@*>rN4c%$u}R>$FtAhPAPl44Y0T6CC4U*9AdcO(rD zto)qBW=c+%(y};ZXA)LEH9i*Lis79DLHZ7gW>@D44a-x~a^J~IDe^R;L(ONxwY{~N z7X|c^Q#{GJP4quPS5H*h4Tx8$V~kO59Si+7pZp!NxMvUjnn_l;(SGDeemhmIf0lcf znl5(L10U%eA|-#JBSLSEX-pgwj`N}rI;6$fAs8G0Zx)2SBi_6lk4y(Ha(at^M}-FTtNkaOdc!;Q8;6Nf;t zRsp3RZ$4D-jpusSHzYbRXI$S$c0NEpgfxh_NCUW6PP$#2GGc|7?m$nracjG088*Z; zj@WrqZ~zMwGs=#2%vYdK_>2!B1%kZY?f1)R$^%DF-=hUa08XEa5f8w942Ohb|wk>^dV zIGK*D=|^9c?7`x)L}jzZ6%bMw)uX4G)H%k_m4A~zX@_Q7IeXn|Mf`{DgNbUQVe3ve zf&5nrisJ!wa0@_{yjl8bbWIoy?xJx$V({LJ*Fx7&$mwlp^JB76Y4cU{8#~uj(vXR!j1jnNygHAs`*!(M990*B(IVZpozs z^@^>loqJeb`1LY$t~3zPIo5y2IuMGSu7_N!oIg%Vl-fq%_%8g=bcX$=aFuY zFI`Z;jka;G=T06}WyqK1Rw*23?qh?8AGQ7~4-b|rnd-)tg1b*mB{oLWK(lL%i-+S& z5<$$SXm8M?=lb5?q+v4Kfa|I5az|d(Wn`|j?j(x5 z7r@|&0kAdFORQLHVksAh=ObvUP++n+jS|@DbY6`l7N0+a;?P@`v+iW6MnX<1OYlkB zax|k(Ua}^RMJ)q{0DIXM=}=|HIrO{bU+3K5(tM_W{H9##EPs&L^_bYVZ4pTOF*QEH z#n=e8zA~+XhluQ#k*bj8+BU4a1lgr z9Y5*;HdM|ithn9FqioN}!^gX7rumXr!KyqNY=sGOZai^q#Po{sy6IeLJ!H%v(W)Y- zg_{k8<(W)a?yB5dS-)xAu|1iHbT3YdLcTn0EpS-ain zJ$+F%@@}pTCRB zx{7I!{I=gSt@!(>7vKSuP^SOLtRGGP550nS3flaCP#Y`9QYbLw%KvFI{r7L@KRf?F zdga$HR?KyGN(j_SUWe@pGbwOSi!G z)mD`x%fSi~8U$Ew5!Dw}A}5!@?wJrB3W}chE?U5#^C7pkwgsi<0s*6A@m9%;D5?)J z%<_e~?DLsi73|)*pcoWY@~`elt1|)8KLO$RNT|ScxcoUzkeoc&?BamxxOZNJ;$p7q z;!Q3ob@8K++5&rS=~>p|xPNA8Gsfa0E4z@=JidSOjnWW1KIGyTR)X}^x@iI!MUn%` zbFjjJGRaXNii|PjQIx;p<%3G=l0l0L)MbDne6R)iGBEHnFaV^O@E5kukK0iVVW{?HN~SMR#ZaFqs(o-^P{(IlM4WkBTP2*~vo9<@EV8qYm7ax$B?H!D zAcen(6u`nFz>KqqN>3O!>MV*zDKUzuHN`zJ!+l2r-bTh(W^8nP8CQ!e8fr z86}XxT5%SUi+WRmx zJ{bE~8!B`rLEYW}zX%D#g>{;z@9Bcq{6uXS+KPGks5G)k&pg_By!xsLvptCUM+yrS zOrdg}5$y7hMg#BAiGv|9995;aS=+LDy;$CZ{1uSPH%hWx=FO>dU zsQ9tas@k;6$vOl7u2#OnHUlDr>WKURRPc;h<~>-eY8BXIG3tok`dt}FN+%n1_TWzl z>mQOWsavc=h%(Om9Y1%}%JSpUnd{W~K zDW&@cUKVdql4;nKmmG6sj=@QvsiM>^Q{opk+_-%gZ^3=K(}0Rn=0GcPH1UF#fQC3B zV+$KFkTtHoB|l6S(YTh;aT)WRN;fQq*b@WT4bo4k>)jx&fz4s7s%6l(tl`zqePc#HYSi{DDgk&+hr zQ+Oa|tYL3bL|Y9Wq!}6GByE)3x8LjpUkZ|Ofk@~(l*fUg(AD#mR8Y6|MncH0{})cA zyEBc^S7R~{1DH$KO|tZ8s(?JC=9sn@`B@`RynWd$M-UPur;4M#5qowr&?^j_H7n5h zEg5rJ$~i^yPvrsR#wwyys2{jq5^lv|Mv%TsxPN=45)81XyPm;(oRsjr5xS_cmsj~6 zu2lA-(3dq?5cw*tS}525lT#}jA-Pw2TMm5Av5=?Xm=Wq%xic;oLt=hY6MLb8NKBOS zQ4u3-^2TPH`g~%$mRX{>Gu|~N8^@D99$AE#?nOsqbSRNc8k|{`zG{vZ8AnZGU1!W{ z`L_fN4nL7hq78Q9WbYl1Z6`B{0Pd-&!PM`4+by#I_jJ>qe0fdP0CWH)+tq^F>6mek z{{}bue1Lib)n%5F9T}Kd`Yje4&LkhuOpW>L;9vq0rq_qFvTW+Vg);!u?BO0J^o=G9 z7O8djX1IC-E5NN2n597=B9s+>K-)Cp?d_jhhuu3~_C7aUtfWx?{FOFfdq9E7ZNes! zI(>a-HBSedY($xQUnmBIpS5{^gE}!pW1zLEFbm?{4FTavI+T~KB|SOqhk8k7KLoTi zdT@k2$Ti?kin>Y5TtYbqFru(TG5y7HUG}>mRw9W% zLFQ#q(G|$xgeHY%5y&yxp=3)OROuswmd8Lv=c%Ff9eN$ob*A^&)Wa}(oQ=g|2g?q7 zhz?VgU7}^&uT|6hC*wVm2%;|E4LM-A8SsM#Ppom_Sdj43bWc4hh8ePWYdOYSeefGS z6a{_(Bs><917T@f?6v6C?);ATy+Q3kk0&Sj9O8ysvIu3I4LKz`ZSf{6YA!FV-A=fC zQ|R2(g(=7SU-vJ>Wu3!@VwO2*M01|&QQ*8DBX%#e0+E?&zKI0G7{Z*l09q5nij#+C z0vLOrNu@;F8#!0wVgZvblFQ@H4P9K8C0;7yhG0Un-{}S=k%$GN!-V;%KFj$h11598 znq^OwL2e$OJHWkavp;a%4U~}rzko+u>^e-5GAcd4a%DFzsXe%4+apxT;8(xlkf3p0e3zR#(DcOoW8=(ToU#CKInrkZ#!&g18`$s6 z_RWv`ax+jwN!Z%`&b?)`^!scGBAxjP(un$NC0?a#XrNom*!Sw~1iY#M*y(_4^+!wV zK12$$`3n>RW5Z*v-P0k4bh;K3629)dlyeYnokS5r^oT`qQb_haqxw&ZGJq`%I%0K% z>5oQz2Yn#frz!;22@D!g%tZ$O$0lq(lNuiuVUaGj-Ky6y0VwdV)VJcmhy31*%i>fE z4h$9v38fS-NAqSCK*NPsv4CE6>U4=)e<9k^sX zO@D+>F{+y?7`4ZRTXlhmmYM1xGjq1kwiu2Ls&b|B{+(L)_{qTc>FRvA{L9WvkGTn_ zM%N7;&N&076eqr}>UGh~8G%DV4lN&jz1J5+)0tx@hLriHh&)#KK;Up2=rm>t5zPq3 z{d=JQ!QpAA3UNQSBJeXV#+Vm% zoy1TOmWgTe^5&F=D~uf5jH^?^=D4MTKfVnIY4s_U3`oUpKg}ksGO~$kQgl^vt|QBhZN=8OkWIq-Ah+? zPq&M96U7VRES7S97cdUYz2JDQ5tz)KUm1zbvGAn2EAJRUakj9uy|t%ahsndboA^;P zZRnyqbh<-Bcq#a0YWrWIf9;~@H+lq4C3z5Rfj|gu9SRv8KXlsJ-(9%c&41Xc%owF( zdUPNm%G)+9lBl5yMj0LQRy0~)Ndyu{)u*CaF4sb9QN-vZv~N`_m1$x0oj6@cRn>q4 zxDBX&r3G7_%?pD_I6XY%v8Ybsme_mg;i=t~+anFOYikp$k<;WvrarCLBs1_7I?NCl z23xOihjnmZeTBfY8G*fo@ysT;Qxb6`I8NG3a0a>2pNb?UW5H(d2m6RCS&^^y8PY`TLj*cfm@-Y2ypBU^*9mi?MH$)7SxA#o}$fi z*wa{f&eur=f4mm7HFB%sFV2za^`pKsy?GSiIJU^Q_=kLxk70Ym`L3c^_M=-(;XQhO z`qWD|l~3$1)-TL8RVgwNZEzbKYm1seUvG1XLi{Fp%HW3vgFj@1N%e~6Hq#J)JrYK8 z+igOZWxa8#eh?niE!0ok)-ZMKZmmZmbmvf_5&BU7Uce9|fN^En2YxV62oNk{WU@Ww z{8kO~=^0ZWXXjo!L^nmqvhORaI$szndk7AmJaze42pjB#t{-fAR?>xlnHaoF{c!Z93R#9 zO?Zdr{&3S7U8lRx&KdkY21{cvuNWLW+AxTEh{ljI6ZuaTSP{l#j(1VTF7P23?|^^a zLu9{=20@GH@>ig>d&pCxVfI5dZ1(CeoNb2+H5|6``R@jE)If4oo-_6K_sNi-3Qy7r z#=n5BZO7$hQ{=R1)4z-hgz#C)P71x&I9vhH8_mWrA$M1tn{pezay5ZFxCK!k&;FKu z$S7nQS#-SX2aSN3(SkR_n`qpGem*$rp9V}rM&91IRZ<#_23z7or?4kGaTS+9E9og=aHHn0NIvwb zfWMPKyvT*vlRTy6p==0gN!d#9??GOlj87PN*~|4E1x~HhJ^UnvH>?B zteg?&yb#4$Fev*WnY6T=+|P;HG2r{#-j{4~J17RD(< zY58{?{hANe=UX&&31Rr@!|xlg=~c+|f)WnaDI$CHQiHX>~CW zAp9c@FI#j#1{`yk6fMV{V1Nz6!!aWLIN4d~NS|ic3>yG}FLdXX>|5OmSnhP*3LJ&2 z&r6W)$3SBsLO)(DTLsNY=ZeOxsz_?w8zpcOFOb@>2k@``e&yf!&AL1O+|;6Ihcsz} zmgHKHep+D7D0>X?`GldpW7)r{ST*=Y00D`@>s`v(7y7TOmRdV&vu8H*ja)T$rY10w zcxe45U%Rc+NQt-59Dhi;Dq!3A31FOka{2eER{VGKV2?hrM4k=`#6A@}+>kqyO~_2| z#i!-_3<@u9dx=*JHdYVYuSeoL@&}d_LFh3&uB#JczSU*xEc%KLQA}q*naZRHo}{F6 z;NO=>aGeIZi>4;l8uiHH!;W)h7#`KPiO z6Avp#QewDV50@sQeGVAli#qaEmalcZ$7Ki{PL zt753mLcHFUa20^M$2ggK8mRLx7Ze*91b!$e!iT?4bz6s6js4L-d@-)?g{c-6!$IXZgdBZjB{GYbxN`T4 z5GBkkK01Ddfh42AVT;c}u8keyI zCk$JKkP44MRxWZXXsN@w=G%iL+a$e8&KR(@069}PZlw+x{*E~b22c5gWDPM{_nf!O zgVEd*Ou_w0E;Mi%S?1*-4tCLHRoLD|a6P)L+ujB~lpKUd6k*T#)3Nqv2&XkyZ&z@C z5trc>qMiQ5rTp?@F2>fEJz<`5KMVu*wzEQQm|`ou3k%MqeQ$PW8i%>OfL!x9lWjQ>pR-X8ItXt2|MUtqr&Aaa&qn^ycDQT>O?1zeu8tS=N8`^ z@uH%nF7lukD_Pvnq6l^>8JjU-ZfRv>h)KmRD9YQ1MaA~Qc!uP?CCO;ca?4SAm_m#2 zl_a}8GXyEJP(g%O=MR_(2#jGoD1JRQq$Ly&GR$~8EjNUxm-+Z4udewS} zp-m0FXnB-=oFd|0vI9Ji({``Ko2fDp$AV7|W2Br9t3-3z&vDiy(-BT2E5#e-FRcEc z8&vgn_VrMm$b#UHWvEnhmR1_h439oWmw*}(9s35ghpYZhjKk4?;E9z+6dGRTLln`+ zzE~{6%qf>hUCf)u19ebbr^{GB3{M5VX-Rh#Cy>;SE*O?F*~R!3pJDnK;fSl1x1iS! zQeyely0OFSy#$-mb^B+xvzJJ|(H6`-S=X|Ik;+>T+51EL5o&@z7(DugX#?pqTQhx& zKi7Ig_r)dU2i|(g6-eq|S7#lI4;vVRh1>)cGe0)&`0DMTAx>g2k5_uelLgrM-pz-6 zO|5b^Z!%ZC6VCqlv|lRoYU@LOmL}q+5@l7~>(rfjGtQQXSam^2LiPO>egm=LLQ~#} zZQE-kbH9tX>}VR(1UYH4n5hQgUUYzDCov@`?a~gX)P(C(5;1Z*q8V@X{psonF#yTi zla^i*u|Iww@j|lhlJG`yZ|hm>j-tUZxx5O;?6Z+fgj+hoU%ngSOOR zgxq}bH)j+K+|53G`?caRmBk^}!dm?qi^(L(eCS*&P9wH&U+gi^CT2Z2IST{V-5hli zD)euCHY{ZG8K^Lz4A|(<-dWelaruuA_dYW|HiR)Q7EP3td1#t*!~#=oIRp{wO=?Mz z<}>Dp)fzgGiB6wKnJL_H{$^fPVIG6!%Lc++4P-M+Pm||{bG@$s-pJ2x*&`C+BR3Tx zY!e@Rbl)S2pFu!^4N-%nAz(2!^-43;c>>bvCAc0wt|s5TqyH@_Rj{J=IK znwLWE-Zn7A6@^rw!IE2^OlY8}L(@?82>J`VbeKX|=AGjOIQ0KVXm78FpD#XEZM8$I zAf9hDZxQ`VypIiRw2x-J%K?^^+3{V7Di&#$iXpaU+5y=ESNAPnI!1VBogZ52@K1Yn zjocJ`Tn7>VVN<$Cwe7=xV7M(FlH~eA;CdnQp}x_w^F{68B{IL~x|PO)smdrD8!Iy^ zss(uLBnQj_YHAO{K2jqRNrHHA;P_EE6sH9}Ci4MHc)1Xufo?+<0Rn&)uWS*)Ha3>rlNef-GzKnxF(-k8q1QAX=Z!y^zR$d1 zGyn=_a9r=whqx)Na7_8|7>^7ClLROrFN7-7_;{p)XTG=ZL4Z-jQ)%O$&vX~K4pJez znk?4mgiN{;#oP&DShx=<{>cq+WI@K%8W;R0v!-UxR<2WIP4c zTHnRBW_xTfILRgZAXoN4|umaFXYX5b=`oEh@tPJMO2mKJ)k zW*J`T%8ku{J2YeLBN#(+#3CHmw5#OKF##tO0LgGkYXVKnwW-@jLhdXDfi~E*6#x!a zMv(Dfr{34Ul9;YFP?S<5ncet&pSDU0!~2go{By zgi&e5?0T5PRQAAxWOE4oXq~JEX@EHT634325PXJP8bJc`>TfXOU&vwz3Dn@Zf67b? zRP+32c!y-v&uYS=vTcV#9J?{OjY82s1F!2YbKonUDl+H1f7w$kQ;T%0oL3rF!cvHP zX>Imy>;!nY@D>**VTBX5oc=aFU}D#9_wUp9uA!eJZE-W}6|j_}5ibx`rb|u`W{S2u z1JsnI43UucBX3M+1ZLN$Bqp$`B;=7g8CxMT14OAJ&+sBLSvvU_aSH8bfnLld3{@)B zIka@-!)MRGnFF$N_eD95!gAvvKTKpb6jRgR^mPQds}UoWTaZJhc{9gr8W}j5PBe?i zlzHE|zTrD-wWO^yt?p6*v>L8-77~rocGn9%>2jSzphgQ{XQdq&o2ckn2#}E21 z(^z)-cpEWUs?rC1QC+hthemmOTd+s*=D%(|C{#n>pwr+Th5s*sPboB342CWC>p%6E z{~iBJfjoZv`*8c;$z8JRILvz!J1;^Hi__o$QKarE*|q#I)vdC-nuA)Kjlk~u?M%(i z?Q6dsy1#=mOnp~xPZ@isI_J5o=T!=Yay%-JHjnTT$C5N`&;*T^#J9X5j37 zqy->gZbU?)t?(DuA%OV=OotjPf@S3pK$4s#Um%wZ;VSdOyvri-R-LyfJu5xTEq_x~ zHIq|)pnXevR9q5}9CuJYBPSV|2QM##VxL7_2TKRbfG`!7L-&lKJx=zI^a5p;Cf``+ zE^#g{E{v6*HnXs>n+vd%@T*#z38~U7EY9VBV9uR_;S+}@Eds#sVT!$hNU(Alu%mE= zrEhT(0(f}rOG1*#_aY@iYER`yTorbTs$*!t40a8O2s_AmBr8z8%Rd zI~Xgm(!!Um4-U$NUStOYFz4o5C7_D1j~mIwC{jF7#VF3(!GSQ){mYXUzA z|IhsY3~(}WRB}-hBPwzc5Y0Mu0V|-N88q)1Ef>Ns_VaK-5@c0mrAYR@Jo#2G)7$Ed zippAm93Z!7)s!x5ks0*076qi3NJ2zIri5wJMyU_M9}@-#5@_Zgo`BI%!8kZ@g5BJw zqtyx}D%}f;fuG`hpZ7LgMght57(i*D8Moh+?T#O&r_?a6@F!d;DdYR64d_|3t%zW&6nx)v*HrR$l8laWxCWqXqBXsS}@YU z({%*$T#D?DRiUk8Tv@kMjgfVe4X{cu$yB7X4(0~X<8>-2RNUR1=}KBy8d?fMh;8#J zX3MMs0-2*Mg{rf|LKKw=ZoGU>*)vH)on91 z|EW`piR0I+>(3-z9GV~mTG5VDtq}yfLq7Kq-dVd6%kHHP){O)7{;f^kTn&Ao#+~AN zKeC^5R(oTgZ>h;ThTs@$mi~QEdqc|J4-jp$ZoN1x+tH(=N5-=8A!$@(FVJh1Aj)dZ z>~L5k+X~9c=z#2E*#z4j%N$x78H5@w&1%G@FHG^xz0Z!QBMXMXbh6R`=ks*_^|oy) z>r_gTHJ5pz7Tl=xX2qOEDwoX;&U!}^C&B;|A(` z!TRA5f+Mv6{3VC-YUIFe%=#;~6x*R;A3o+Mu82Shnna$LC!V^BLtb=|F4)Fhz+*So zpDiyPYPXQ1j$?rW1d2cf02_7`eIRKfj|zJ~A%=Z0xbEFXP@(ZO7SCFRF9WCO3dk#n z#3&8Vot(*AYE+)G6mdi5Pp*adSt( zfRg4}lwUqc>m;~!Y_;`VweBtYwU~EFS7@>0qTj>&CcSLS zkiOL66?4}B8pDT32$G>ny;(dEcoFRF#lR89wyB|o7^TVq?(%#-ah2&Rc82qy#HtR) z@xz9Acwu-gi7aK6UE!VGp|Jj*vEyEH^YNtU6aCuvZ{q6vIhf#9P|kIH4zA^dqVBFw z7m>ZK18WZ<1Ah}35Wek;txhBXC92!^G)1cFn^kS?NL#Ag2*fZ%?OND(O2v+xPgG4o zkaYxtgxuKZ5B)+>^VZSrQmu^HOL6)&AEn^{yJOame}{ka2*H8 z+L*J|x4M)e?MGqBXvKvkyX3Sy43=cae!k{6rf!6YO?ww?DaYZ~*h|xu{sCq=&I(G< zR_tskII`3*&ZZeYF?c_#9wOJQO6Cv1dm^D?IO}U3v;JOuNcly$MAyq&6gCQ%xVYQk z)}gD82XC zMdhWXJ-H}5w?z2X$VT9z>GvPX{|Fj2>*xjRm+BkN~DA3oxJnD7!b4sj5MUU(id;XRF>4rS`xM9Mo%~IT& ztTz>#Od9j?TYfSGt?rpHpPdiJAnFC8oLEt6aIA2uc)8!=Zp_a80ZvA={|7P}G-@2` zPUt4F0XK_=9pq^E!Re}iJTGOvg-<^BhOmF=+d*ZmIBXz7R#F&xvUrRJJ30~knD+!F z2nH9Egyojem@fK9ZK;W++lr3pP;pl&TC1pLDVe5TjY2!A9FEpMs30uJS45xgP`esOEkXU$%W0E58nb&($qX|(Fu~G6R9%=@4HmQ*z@6dW;s**+J({tOa2-4MQ z7meG_O_|cUIGrg~Tj9U$uBJ;H4eR#VSa9^1+dtZqEvl2<3nEVYl1mQ;p>9g}g&g%% zlI2>`(F0Z>ItPT?&8#OVphGNPWZyx5s&V6al*N-<7383%1B5AvynEY; zI6Nw}tlwt~R>%8nSVQqLsOX8iQsY6h7;b5Ytz(IHz@FX|S!f&ZMB(f!P-Br=9mD>Z zjszwpjkwYlOK94$bVZ*^xlG-PmI!h1Lp79C&p;%KjtCx-876`7BCps6>}yohB&v$! zqVt@!FcsqGp%6}Rs|$S7(YgYv&qiqu5|?-yzPB^>vDZtcMzOk8jKiyk(i{btt&0g7 zjxwTEVooLzE@5CcmDx|CBs!ODbmNVuMFZNe9C92#-?nY)hKo||&1&uAWLe^BSH8}o zfyc2z&<|AWftScF!UQCAb1bHNU@YBG#QCTsWPC;#}Y)0}lt z@`?!IMhYd#kQPDYjynJ5=b+ybP}6p_+iw0E-WR^mn9%3flL$4}of#9?cJtDT3e5d; zuV86raVTa6#ZEN@kM)-@D-@}|i3uSzePm<-dX=-z!_~m$jzWzS+XU>Q!H<@RsfX3Q z6XxTi`BLy#`HFR8vHgK@Utql!db*J%K1(@Mbc+<~=z%nCtOxM9tf;(3#phX3BvrfH zFA5n!^63sGdE%)H8&k02kg2=G;RK4LyTi7^08Uaj@n90p%%VMIs%`S@$Qp~z7 z>s$>*HapAFOTO`WwI1W_4mGDv5;MnBgc}ZgOwJjC-YPZPO8*nCJYK6Rvld<(ORL`C zK29deZz`j3!g>Hf@HmJEL-bg8dgI#MkAfYSGLJa|t&ovzokG&PpI0yU%7l)l(QprQ zRh(HoYeWGa{Pn84FBKjU9s`9pRT)*fSG8i8nFni;ABX*q5)nc^ScM3Ll$u^2XI0@n z3Q#{$?-Ub;H5&n5AmX~(KlcGoTWz)Nd!zZ=)7qBj59&cs@W<^{eApO-c7OXF<*T*m07Nikco)8lK!|?HnhPimUW3!05 z1OsJJ!rk1dnM!AW;B;{d+Mx$()>w;mXE5qkG*^5aUZJ3Qr(EHfT7GyOFM+_z<#{m7 zP1nrxR-;o-!xGJ6T}I@t=9rd-CZMAd;-oaAdee<#XH0u<-xkbMLeKf$IqBrFc(L_-FmG z1^(7xh1kkU%ra#}Zt_;ty4Ncf0{MTGTaxNy5q-OQSr=Fp!7z7wL02a%o z>9n)8)qlu`-Bo3X9(GMPuWJ>W>C9QhcI??cOoNVpK`NF@F?SZ)4hjfWw3CiVkGZ~1 zRKzEFjj85GQoC=4?r~W$L%nNi)&RT7Eli)oV}nYJiEK2Z#br2h(?72?Nk7-bWDrydeBW>~qvik{135~&0@!7lSr;x7xCGnvRion>r@Y7T76(r^DzN9|DgS^<) zrlyQ>5D2wH+r6yUsoYg2#`^Lv-2R**kSVs32AYVhnIE~n&p34zb)M7C9+?%05jUzO}x=0OU5RT6KhYe`Nq4r<`6Zjb``H(=fWM_ET}jopai8y z6)As{jFw~dlgE#V=CIl;15d3%xKoTgg$bN@+e6bQJ4N(+ClAP6KiX%HPakEiB`VEd zBMX@gVBDoX4#=WCSjj058agR?=3iXk3P5a0sI+{XQmoeTlxY}N-zZr6HpUFmB?n%R z=ts5i+vetLWN=pnY=Zq&cl;TAp?iJ>xV%tzKYE|7U)px|*Bz-X%=KT`wfFf}$1YSy zp%A;iQGu*YNTf$t%^3#TNfL$2u+v#HFmPu_&5%F~Hq9gTa)K#Z7TY#nQE1;FGp3Le z*8)f!6FxQQ-f`fmxI#{y`UFJo6h~Rt)BEO`r19O67uVqOz1#8QV@0l1WO+}@hq(~! zsbFbwC8Kcn#kC?s%_|gYB-m&r)JyS_@V=@Lec%pxoXTM*D9b$FWqtYC`DAuW*zkq? zk;Nkc%?DTujD2^jTZH^KHDsnO)ANU*7$mu7GDcx-uh}|IN6(Ay?ikI{s1-9i0p%;O z>-0q?WnJYFeX4?23X1gXaN*T#d7&}zn_63_8K<>myr?yAJ9PIaZTkAL(`*dE9*4#l zwl{+a&2JqkFBEEQjEDEc zP^9ZqHO`TDeb?`WCSFYyqI1yrp{BpSX~7i`$PhZJeNRi3-a;^ou{}p69&S z>3vQ$g|tZxn$IlfG#D|alyNCAg<05?{b^5n${WT0tUE!B|yz%?GweRX?8XzP-1PQ zQNN>}4MUjp<+-%!9=UdFnJELfc7b~xkDU!WxtCT;*o^_DmX{pwMe4++ZVw1aXg`>5 zt^*(`Z+|OHk2S(TipC9ux%(EChCAz_($8skyu^xC8xE)P(e z7p`fz)(wsv0yPm8k&Dq^1j+b-LM1gv0R%XR+h~+&qm&6a`N5NXJ17rb;(0+RKM+YN z28`VA%TKG{!S++B+<4tFpybD~>-9Sk`=I++whMYkV-FA?U`kXm-0mZREt>j*6^*`$LuX(#Grj1!%S=CLT7qXl2g&X zf9h!|4Va?50(5H^l`SNZXQbxkFIl@A%l1r0b5UywmHkF#ZXN7fth=Tjqsz@y?-~ec zztS4~>FyBL5FkUq1E=3eQ1Q(`L}5rp))&vKm+H52Hgo6qut?+m4-^7fpeM$O+)LlJ zKyCBpYxqH?-;&>p#~2hN)49{UsAu)!PDgXa14xO2@}{SI(!F!7hNPMqdNV5#S7t?z zJOq3mt~8jhfcviu)5`0%gTDO*8H>d+#_)hPWu_F#jF;_Ow5cQibFCsW>;@s!BzX`d z8vbJ;v^9>BzLhkU4yuLQ+%31S0}Kz}i)%Vp8_{&-R6H>cz0Pn&xaRJmcCksJbyWPF zVXt$1F3&(m+@EZyrkql?TpZq3mQYN=dFhsV(#{#7!%u;o{bILD!{ZaF#goQ!M_;Go z=Q=)&xLjPC-Gkm9o|j;6AQ=PVTg#XtE0!b>B%Q{6MQq@zPm2lM&0o{*fy zh`bUt|4<^;v_Evc6E8k2Y&-MphM(O#tJnF{NkVZoPzW|0d_GGRl1&7##dIx@e^byK}5=>l3rDenz%gsFa4bUC@R z@JT)1ZOqqtrW|@b`Qx-uSn+DGJ(bBSn0L$)1hwus%i|Av!;Cp`^7*BVohj%zHRbxRGme&^BdFV)W%e+}J_>#;ld#&t zCC2^0&jx;BPh~&feOQq!T$jt<`W!3u-#z6h2f&QMdttYVRv3kjnoZl8A1q6ui$P+pRmW=?(M=O1 z^?VG3-Br@^xRVITWzdRtd!|!}$S}Q0UOkrGm+v&xNu%xwisVL4u-VR>@Qqj!@Z@Sq z`s&dCPL(xp%1bV)R_ju@`4dVuMma~j9VzLvj`lkx+CE2!+l%4fND)d`OpZ@v-}y~4 zu`s`)`Xc+Td^!rXqqEKOP>k&k_>+f0<~M?3Jo79+?NKn>{QRo}nNJm0B~%tW{5%S< zr$0yhCw#STp1kqiZP3$HPdk2bUsC2R3+Zmd$rIV!y0cTqgzW0UitOx-%|=A4<&wZ=(l@w=+ze5v9w2 zBhZ~~TC&G@@em1Yt)i_W62Dra55=W+?msDBY~o_f<{-deW7r0UoB*8@qzO5+x5W@| zF6VVhbAk|5qiF=uH781MWN9|)-Gd{e^>J?pA5$1VREA`L7NL6=C=jYTr;H(yH;<&L z7qIifOkjfL;JLDWh+Q=-5JhBp+P^0I3_Fge?qK3Nv9bLX_UTS_n*P?jJf-sjq7f~z zpbfbooFJ4B{`AugN~CM_7j2wz$l+*=pAlJH&^b|=;qP5h09@TOm)RKetYuDqU`>bV zlxBJHc8J~LP~J!?JtEW?P1h=2(m3HCC?vU{A|?{12`0;YMz>tc2YDIU^vl9&P+Nj7rLvy&6 zLfpu!<+g&yxL+Y`5na1C+P)zv^$SW)kIqVKoIgD#E0@<}k`w=E$@L&1?fk7ELWUhae-4@Ez(kfM{C&XBp#9RI#dd zMXt9bSa=K2cIP6K%n}+mn(REi>Q?5s?ABA=R*$j`zlhfrG5AW_Eif_(Jaj7aRUca7{bF~>bZ_I#s#<0+h?hkqUIj|{Nbl^rOjhQUEz>4U7+C3wqyG*VXVe2zbR$& z2!@7)3~qZuOn>ZaH~90|6I%n&+G^ym>6Jws|!){8!VqkG9|w{D7$ z;vARLwT3y zEr{GahnSh^;#`|n?j+qRJgryVBuT;P>i&~{g3<1#$CUyu(UqkJNmhMKk z_8dSljmAqxF$D* z70n!9t~eJA-NF9Q#>Ou3^}EM47 z1yj#@AY7L*Kv)LmQfCasA5>-|H%AMluEiYrQ#Y4qjT z>@)=EM{|C&ciGv7l&6igeMPR;Rb@_!f2CK&u?tz2Ca6lnR1IIfvD;sq`vX&GUro4E zHtf<@9E^KNp#l8~ll{97UI%kXGkr331YZl{#tMM+YE0n~2^V;pxe4E21ighB>;)0K zTg`)LJb)CQd;4_7UAIY9X34zA(nb9Q4Sh^%!I51gVqZVd&w6t_rfql2HvB7PC~sfj zAlr!&PQ?8hc8FOc9j;k=8mjhdWYq7iV+-epE-^ag?zhAlZxkX2L5+h>C%2dNWUIQ` z8=|)^e>3D{P3>2~BU;V+(WcO1#B_*NWpYIl##kF~6ORifKVR=N5>4HhXzp(bMk-3X zd^mE1gcF>`WyPc_oQOr(PlYbjB#9uYcZcQJog2Si>R-iEDCEnsUeO4IR#jMF^>1M- zPvVo#o{r)aVg#_ssTJq&9?1k z?*uva5Xt`(@M$+Y+hmh{tM~nv)E+>fbB|_5#m;B}Dgkc4?G^$lg|Sb_DpD|Mw&9d$ z@sMm+%W6`sPeJpfFrN9_UlRqe1bd)YKRP>Xri>@drqC*r>iM?@Qy1+MY?NxUBBTk; z79*91t4+XbnfKB^yUj9n`x^T=&L0DISU6%bp-tRWh z2=7r^556@bKtO*zx5jRsmOJWOfkl+!aDl9-DK#TVSgr ze}PSp)(g$mla!^MwSF#j#nJXzG|^g7#x*1$0$p6?y5X!SE!ULet=Qi{y!D)Oe9j#t zH{A!JK4Uc|Q^Cn!+TnhV-03YT&bbJ>dd_7TDa0VKl{QtqE3Xp>_|r)3JLE9R%x%6CJj-=q_(47T7|N(--p zl#q_5(c^6yhI4Fe(jFYZVb#cRe~KOamSFYK9Ae3t9$eo(l*R99>m7bIQ~94B&HOsA z4h|=+o0y=j)RjeO*>!By(NKk>-@T&8?9N-~zb=*NzC3xaGw;cMdqg$ujKsYyF+@Y{(fYRd{jiNS6uHVPO44=ICZ4{gw4D~yS0Q5> z@!Qm$<8MJ@^H;LIMmZ~D$U1tjna`hQ^)1JPsKmRE(Ez${A&DQpr1 z)Sg4|f}ABWFJorN=x9YbtLQkj87~RgdnO!zc8r5c7)kzW3hb%_;jPF6_|8k2$bYQZ zCVu}7+Ql{pAQC-988grbqBB&ERf`{Sc8RNe14RPj zG_mdB8b8b1X$NE6{$6x4&D|kWHYb*N4~x5(3HU97f#0E*svzS-lEp^L`q zGPPE_a}Gp z3a7d%M${DiHs>4kQ(x?Nu!bsIq)5WT2C3`%E9@tseE;(YL6)xI+P!PbCoiIZE#zf0 z`Gv26V=(%IqDUBWw97V?i(*ht2C-Ax>+Wv+3W*H6tG!X%1;sw<>j_R7ewB%V4Z8dj zKRedA9$0HEC^r3((XH2VvP3OQL#AthQ@sAl!pBQ0?RUXUnM8{r$EOoa`&RFrd4}mo znKN3}XxYt81BYlCklE+Q;rsBDaG2@JJjSBxDb#pU`S+-LEBhVF(o8A&wWD{VK8J!_aDWJ|kADk8ElQMOO zTFkluzAt(+*KZ^lSKq7>+|LwDb7w2dNRnRAsl8zYXrVcif_SYDxx)Bx)mJlp7(v46 zr2~-PWYa^Z@}qzVB@2j&B~5)}i>YG%GG(*+Wd8G*1ho608PC!nP8GJgyrhV8hBUWgA|<)uj(mn`+MQ_Ek*z;j zD}l+SoELZS%o&y`!OauCv24%<*Cf~qcNNC=dqwTPx_9U0um>qoT)$VG>>69o zrenBE_E(yrR>SPL4|4|k>_2hdNB%$1SOfSA5W~KjZ|;c5@7UG)RZ;^kvzzY z-knR@1olJz@UOh15>ltRO_IT2&5ojg#39!JX`+nsGnP0>F>b->=XxXO;9bmNK8XsA z4569mSqyURPm6!ohK$a5D#Fh%1fF#u8HLh5J~HddIBxI4l;toaJSQ7;=|@vo64~q@ zPrq$A(MnCryWzzpwKDWUZ9rlR=-OTWP{hHr-T`DsrI4>+Xi9f3ARLG!>>E`EQtdgP z72A=}15Q=INd3jMMwVvv%fNDXF!8TzDs{8MMi2ms#)<-PO ze}i8xJngW}J&B^04LPEq9*b}0g`*@vbJ-7{rA57W742~k=2Lf441v4LQK1qPHG`XLl%}MZp_&o3tdkgSyO+}+TxrU^cCtO?g z*0}^3sdFU1?|4?e`GsSv=O-|B8~X7?-@qo{1_e}bzuj=GcA5r8YiPsJOfW^(vhD+YV0lmIdlbgRqZLBAt~ zB3Z8(s873QXO!-dQB(MU-h(MjHai~B}i#!c`9OT zPszkv_Kqk@hA|kRq=HbWFOLiBFN!2>)8nw`R~I~eut9PwG-0KF`4q{{WY*97(Gt$F zSGdZgE_QMQR08;)l-vX=Z$LLO?5)L(z@;&%JCpJ!YQC(VfT?uVR zX#g~=2w(sJKqLpS1u%jo8{opA{�g+#Sq;|DT<|^WdHU5Yher-~7%0Q~BQqAWgsj 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

S7^5k)2xe-dAGr@H58`j$L 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%0DaTwTF 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�Y!dR}TrZ&G$4rr_G& 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%0D>Vm9{O8;xLDzP3EOp2DqW#oPq*6aKT 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 K-D1V$@qYl;SgH~L diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index 5f5d3b5bc6..27fc4d4dd4 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -6218,41 +6218,6 @@ Info_plist = """ """ -MAC_POSTINSTALL = """#!/usr/bin/python -import os, sys, plistlib -home = os.environ['HOME'] -if not os.path.isdir(os.path.join(home, '.MacOSX')): - sys.exit() -plist = dict() -envfile = os.path.join(home, '.MacOSX', 'environment.plist') -if os.path.exists(envfile): - try: - plist = plistlib.readPlist(envfile) - except: sys.exit(0) -else: - sys.exit(0) -paths = {'PATH' : '/Developer/Tools/Panda3D', 'DYLD_LIBRARY_PATH' : '/Developer/Panda3D/lib', 'PYTHONPATH' : '/Developer/Panda3D/lib', - 'MAYA_SCRIPT_PATH' : '/Developer/Panda3D/plugins', 'MAYA_PLUG_IN_PATH' : '/Developer/Panda3D/plugins'} -for env, path in dict(paths).items(): - if env in plist: - paths = plist[env].split(':') - if '' in paths: paths.remove('') - if path in paths: paths.remove(path) - if len(paths) == 0: - del plist[env] - else: - plist[env] = ':'.join(paths) -if len(plist) == 0: - os.remove(envfile) -else: - plistlib.writePlist(plist, envfile) -""" - -MAC_POSTFLIGHT = """#!/usr/bin/env bash" -RESULT=`/usr/bin/open 'http://www.panda3d.org/wiki/index.php/Getting_Started_on_OSX'`" -exit 0 -""" - # FreeBSD pkg-descr INSTALLER_PKG_DESCR_FILE = """ Panda3D is a game engine which includes graphics, audio, I/O, collision detection, and other abilities relevant to the creation of 3D games. Panda3D is open source and free software under the revised BSD license, and can be used for both free and commercial game development at no financial cost. @@ -6364,9 +6329,6 @@ def MakeInstallerOSX(): if (os.path.exists("dstroot")): oscmd("rm -rf dstroot") if (os.path.exists("Panda3D-rw.dmg")): oscmd('rm -f Panda3D-rw.dmg') - #TODO: add postflight script - #oscmd("sed -e 's@\\$1@%s@' < direct/src/directscripts/profilepaths-osx.command >> Panda3D-tpl-rw/panda3dpaths.command" % VERSION) - oscmd("mkdir -p dstroot/base/Developer/Panda3D/lib") oscmd("mkdir -p dstroot/base/Developer/Panda3D/panda3d") oscmd("mkdir -p dstroot/base/Developer/Panda3D/etc") @@ -6402,17 +6364,6 @@ def MakeInstallerOSX(): if 'lib/' + os.path.basename(libdep) in install_libs: oscmd("install_name_tool -change %s /Developer/Panda3D/lib/%s %s" % (libdep, os.path.basename(libdep), libname), True) - # Temporary script that should clean up the poison that the early 1.7.0 builds injected into environment.plist - oscmd("mkdir -p dstroot/scripts/base/") - postinstall = open("dstroot/scripts/base/postinstall", "w") - postinstall.write(MAC_POSTINSTALL) - postinstall.close() - postflight = open("dstroot/scripts/base/postflight", "w") - postflight.write(MAC_POSTFLIGHT) - postflight.close() - oscmd("chmod +x dstroot/scripts/base/postinstall") - oscmd("chmod +x dstroot/scripts/base/postflight") - oscmd("mkdir -p dstroot/tools/Developer/Tools/Panda3D") oscmd("mkdir -p dstroot/tools/Developer/Panda3D") oscmd("mkdir -p dstroot/tools/etc/paths.d") @@ -6457,31 +6408,6 @@ def MakeInstallerOSX(): oscmd("mkdir -p dstroot/samples/Developer/Examples/Panda3D") oscmd("cp -R samples/* dstroot/samples/Developer/Examples/Panda3D/") - # Dummy package uninstall16 which just contains a preflight script to remove /Applications/Panda3D/ . - oscmd("mkdir -p dstroot/scripts/uninstall16/") - preflight = open("dstroot/scripts/uninstall16/preflight", "w") - preflight.write( - "#!/usr/bin/python\n" - "import os, re, sys, shutil\n" - "if os.path.isdir('/Applications/Panda3D'): shutil.rmtree('/Applications/Panda3D')\n" - "bash_profile = os.path.join(os.environ['HOME'], '.bash_profile')\n" - "if not os.path.isfile(bash_profile): sys.exit(0)\n" - "pattern = re.compile('''PANDA_VERSION=[0-9][.][0-9][.][0-9]\n" - "PANDA_PATH=/Applications/Panda3D/[$A-Z.0-9_]+\n" - "if \[ -d \$PANDA_PATH \]\n" - "then(.+?)fi\n" - "''', flags = re.DOTALL | re.MULTILINE)\n" - "bpfile = open(bash_profile, 'r')\n" - "bpdata = bpfile.read()\n" - "bpfile.close()\n" - "newbpdata = pattern.sub('', bpdata)\n" - "if newbpdata == bpdata: sys.exit(0)\n" - "bpfile = open(bash_profile, 'w')\n" - "bpfile.write(newbpdata)\n" - "bpfile.close()\n") - preflight.close() - oscmd("chmod +x dstroot/scripts/uninstall16/preflight") - oscmd("chmod -R 0775 dstroot/*") DeleteCVS("dstroot") DeleteBuildFiles("dstroot") @@ -6492,7 +6418,7 @@ def MakeInstallerOSX(): oscmd("mkdir -p dstroot/Panda3D/Panda3D.mpkg/Contents/Packages/") oscmd("mkdir -p dstroot/Panda3D/Panda3D.mpkg/Contents/Resources/en.lproj/") - pkgs = ["base", "tools", "headers", "uninstall16"] + pkgs = ["base", "tools", "headers"] if PkgSkip("PYTHON")==0: pkgs.append("pythoncode") if os.path.isdir("samples"): pkgs.append("samples") for pkg in pkgs: @@ -6504,12 +6430,8 @@ def MakeInstallerOSX(): if os.path.exists("/Developer/usr/bin/packagemaker"): cmd = '/Developer/usr/bin/packagemaker --info /tmp/Info_plist --version ' + VERSION + ' --out dstroot/Panda3D/Panda3D.mpkg/Contents/Packages/' + pkg + '.pkg --target 10.4 --domain system --root dstroot/' + pkg + '/ --no-relocate' - if os.path.isdir("dstroot/scripts/" + pkg): - cmd += ' --scripts dstroot/scripts/' + pkg elif os.path.exists("/Applications/Xcode.app/Contents/Applications/PackageMaker.app/Contents/MacOS/PackageMaker"): cmd = '/Applications/Xcode.app/Contents/Applications/PackageMaker.app/Contents/MacOS/PackageMaker --info /tmp/Info_plist --version ' + VERSION + ' --out dstroot/Panda3D/Panda3D.mpkg/Contents/Packages/' + pkg + '.pkg --target 10.4 --domain system --root dstroot/' + pkg + '/ --no-relocate' - if os.path.isdir("dstroot/scripts/" + pkg): - cmd += ' --scripts dstroot/scripts/' + pkg elif os.path.exists("/Developer/Tools/packagemaker"): cmd = '/Developer/Tools/packagemaker -build -f dstroot/' + pkg + '/ -p dstroot/Panda3D/Panda3D.mpkg/Contents/Packages/' + pkg + '.pkg -i /tmp/Info_plist' else: @@ -6525,14 +6447,8 @@ def MakeInstallerOSX(): dist.write('\n') dist.write(' Panda3D\n') dist.write(' \n') - dist.write(' \n') dist.write(' %s\n' % ReadFile("doc/LICENSE")) dist.write(' \n') - dist.write(' \n') dist.write(' \n') dist.write(' \n') if PkgSkip("PYTHON")==0: @@ -6541,9 +6457,6 @@ def MakeInstallerOSX(): dist.write(' \n') dist.write(' \n') dist.write(' \n') - dist.write(' \n' % VERSION) - dist.write(' \n') - dist.write(' \n') dist.write(' \n') dist.write(' \n') dist.write(' \n') @@ -6561,7 +6474,6 @@ def MakeInstallerOSX(): dist.write(' \n') dist.write(' \n') dist.write(' \n') - dist.write(' file:./Contents/Packages/uninstall16.pkg\n') dist.write(' file:./Contents/Packages/base.pkg\n' % (GetDirectorySize("dstroot/base") // 1024)) dist.write(' file:./Contents/Packages/tools.pkg\n' % (GetDirectorySize("dstroot/tools") // 1024)) if PkgSkip("PYTHON")==0: From 0b99086e8c41f62984bcdc26d35d09030ca387ec Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 8 Mar 2014 20:58:03 +0000 Subject: [PATCH 65/71] Improve key events on Cocoa: add support for the brand new raw key event system, distinguish between left/right modifier keys, fix the lack of down event on help/insert key, fix the lack of up-event while command is pressed --- panda/src/cocoadisplay/Sources.pp | 3 +- panda/src/cocoadisplay/cocoaGraphicsPipe.mm | 3 +- panda/src/cocoadisplay/cocoaGraphicsWindow.h | 4 +- panda/src/cocoadisplay/cocoaGraphicsWindow.mm | 254 ++++++++++++++---- panda/src/cocoadisplay/cocoaPandaApp.h | 21 ++ panda/src/cocoadisplay/cocoaPandaApp.mm | 29 ++ .../cocoadisplay/p3cocoadisplay_composite1.mm | 3 +- 7 files changed, 267 insertions(+), 50 deletions(-) create mode 100644 panda/src/cocoadisplay/cocoaPandaApp.h create mode 100644 panda/src/cocoadisplay/cocoaPandaApp.mm diff --git a/panda/src/cocoadisplay/Sources.pp b/panda/src/cocoadisplay/Sources.pp index 3aed55d405..0a3a5b4e44 100644 --- a/panda/src/cocoadisplay/Sources.pp +++ b/panda/src/cocoadisplay/Sources.pp @@ -17,13 +17,14 @@ cocoaGraphicsPipe.h cocoaGraphicsPipe.I \ cocoaGraphicsWindow.h cocoaGraphicsWindow.I \ cocoaGraphicsStateGuardian.h cocoaGraphicsStateGuardian.I \ - cocoaPandaView.h cocoaPandaWindowDelegate.h + cocoaPandaApp.h cocoaPandaView.h cocoaPandaWindowDelegate.h #define INCLUDED_SOURCES \ config_cocoadisplay.mm \ cocoaGraphicsPipe.mm \ cocoaGraphicsStateGuardian.mm \ cocoaGraphicsWindow.mm \ + cocoaPandaApp.mm \ cocoaPandaView.mm \ cocoaPandaWindow.mm \ cocoaPandaWindowDelegate.mm diff --git a/panda/src/cocoadisplay/cocoaGraphicsPipe.mm b/panda/src/cocoadisplay/cocoaGraphicsPipe.mm index 8a66bfd87a..8c6d86ef17 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsPipe.mm +++ b/panda/src/cocoadisplay/cocoaGraphicsPipe.mm @@ -16,6 +16,7 @@ //#include "cocoaGraphicsBuffer.h" #include "cocoaGraphicsWindow.h" #include "cocoaGraphicsStateGuardian.h" +#include "cocoaPandaApp.h" #include "config_cocoadisplay.h" #include "frameBufferProperties.h" @@ -32,7 +33,7 @@ TypeHandle CocoaGraphicsPipe::_type_handle; static void init_app() { if (NSApp == nil) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - [NSApplication sharedApplication]; + [CocoaPandaApp sharedApplication]; #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; diff --git a/panda/src/cocoadisplay/cocoaGraphicsWindow.h b/panda/src/cocoadisplay/cocoaGraphicsWindow.h index 6fdc2a4497..b50edbf173 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsWindow.h +++ b/panda/src/cocoadisplay/cocoaGraphicsWindow.h @@ -80,7 +80,9 @@ protected: private: NSImage *load_image(const Filename &filename); - ButtonHandle map_key(unsigned short keycode); + void handle_modifier(NSUInteger modifierFlags, NSUInteger mask, ButtonHandle button); + ButtonHandle map_key(unsigned short c); + ButtonHandle map_raw_key(unsigned short keycode); private: NSWindow *_window; diff --git a/panda/src/cocoadisplay/cocoaGraphicsWindow.mm b/panda/src/cocoadisplay/cocoaGraphicsWindow.mm index e314fbe4a0..a5c63f3d50 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsWindow.mm +++ b/panda/src/cocoadisplay/cocoaGraphicsWindow.mm @@ -1495,54 +1495,73 @@ void CocoaGraphicsWindow:: handle_key_event(NSEvent *event) { NSUInteger modifierFlags = [event modifierFlags]; - if ((modifierFlags ^ _modifier_keys) & NSAlphaShiftKeyMask) { - if (modifierFlags & NSAlphaShiftKeyMask) { - _input_devices[0].button_down(KeyboardButton::caps_lock()); - } else { - _input_devices[0].button_up(KeyboardButton::caps_lock()); - } - } + //NB. This is actually a on-off toggle, not up-down. + // Should we instead rapidly fire two successive up-down events? + handle_modifier(modifierFlags, NSAlphaShiftKeyMask, KeyboardButton::caps_lock()); - if ((modifierFlags ^ _modifier_keys) & NSShiftKeyMask) { - if (modifierFlags & NSShiftKeyMask) { - _input_devices[0].button_down(KeyboardButton::shift()); - } else { - _input_devices[0].button_up(KeyboardButton::shift()); - } - } + // Check if any of the modifier keys have changed. + handle_modifier(modifierFlags, NSShiftKeyMask, KeyboardButton::shift()); + handle_modifier(modifierFlags, NSControlKeyMask, KeyboardButton::control()); + handle_modifier(modifierFlags, NSAlternateKeyMask, KeyboardButton::alt()); + handle_modifier(modifierFlags, NSCommandKeyMask, KeyboardButton::meta()); - if ((modifierFlags ^ _modifier_keys) & NSControlKeyMask) { - if (modifierFlags & NSControlKeyMask) { - _input_devices[0].button_down(KeyboardButton::control()); - } else { - _input_devices[0].button_up(KeyboardButton::control()); - } - } - - if ((modifierFlags ^ _modifier_keys) & NSAlternateKeyMask) { - if (modifierFlags & NSAlternateKeyMask) { - _input_devices[0].button_down(KeyboardButton::alt()); - } else { - _input_devices[0].button_up(KeyboardButton::alt()); - } - } - - if ((modifierFlags ^ _modifier_keys) & NSCommandKeyMask) { - if (modifierFlags & NSCommandKeyMask) { - _input_devices[0].button_down(KeyboardButton::meta()); - } else { - _input_devices[0].button_up(KeyboardButton::meta()); - } - } - - // I'd add the help key too, but something else in Cocoa messes - // around with it. The up event is registered fine below, but - // the down event isn't, and the modifier flag gets stuck after 1 press. - // More testing is needed, but I don't think it's worth it until - // we encounter someone who requires support for the help key. + // These are not documented, but they seem to be a reliable indicator + // of the status of the left/right modifier keys. + handle_modifier(modifierFlags, 0x0002, KeyboardButton::lshift()); + handle_modifier(modifierFlags, 0x0004, KeyboardButton::rshift()); + handle_modifier(modifierFlags, 0x0001, KeyboardButton::lcontrol()); + handle_modifier(modifierFlags, 0x2000, KeyboardButton::rcontrol()); + handle_modifier(modifierFlags, 0x0020, KeyboardButton::lalt()); + handle_modifier(modifierFlags, 0x0040, KeyboardButton::ralt()); + handle_modifier(modifierFlags, 0x0008, KeyboardButton::lmeta()); + handle_modifier(modifierFlags, 0x0010, KeyboardButton::rmeta()); _modifier_keys = modifierFlags; + // Get the raw button and send it. + ButtonHandle raw_button = map_raw_key([event keyCode]); + if (raw_button != ButtonHandle::none()) { + // This is not perfect. Eventually, this whole thing should + // probably be replaced with something that uses IOKit or so. + // In particular, the flaws are: + // - OS eats unmodified F11, F12, scroll lock, pause + // - no up events for caps lock + // - no robust way to distinguish up/down for modkeys + if ([event type] == NSKeyUp) { + _input_devices[0].raw_button_up(raw_button); + + } else if ([event type] == NSFlagsChanged) { + bool down = false; + if (raw_button == KeyboardButton::lshift()) { + down = (modifierFlags & 0x0002); + } else if (raw_button == KeyboardButton::rshift()) { + down = (modifierFlags & 0x0004); + } else if (raw_button == KeyboardButton::lcontrol()) { + down = (modifierFlags & 0x0001); + } else if (raw_button == KeyboardButton::rcontrol()) { + down = (modifierFlags & 0x2000); + } else if (raw_button == KeyboardButton::lalt()) { + down = (modifierFlags & 0x0020); + } else if (raw_button == KeyboardButton::ralt()) { + down = (modifierFlags & 0x0040); + } else if (raw_button == KeyboardButton::lmeta()) { + down = (modifierFlags & 0x0008); + } else if (raw_button == KeyboardButton::rmeta()) { + down = (modifierFlags & 0x0010); + } else if (raw_button == KeyboardButton::caps_lock()) { + // Emulate down-up, annoying hack! + _input_devices[0].raw_button_down(raw_button); + } + if (down) { + _input_devices[0].raw_button_down(raw_button); + } else { + _input_devices[0].raw_button_up(raw_button); + } + } else if (![event isARepeat]) { + _input_devices[0].raw_button_down(raw_button); + } + } + // FlagsChanged events only carry modifier key information. if ([event type] == NSFlagsChanged) { return; @@ -1597,6 +1616,23 @@ handle_key_event(NSEvent *event) { } } +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsWindow::handle_modifier +// Access: Private +// Description: Called by handle_key_event to read the state of +// a modifier key. +//////////////////////////////////////////////////////////////////// +void CocoaGraphicsWindow:: +handle_modifier(NSUInteger modifierFlags, NSUInteger mask, ButtonHandle button) { + if ((modifierFlags ^ _modifier_keys) & mask) { + if (modifierFlags & mask) { + _input_devices[0].button_down(button); + } else { + _input_devices[0].button_up(button); + } + } +} + //////////////////////////////////////////////////////////////////// // Function: CocoaGraphicsWindow::handle_mouse_button_event // Access: Public @@ -1697,11 +1733,11 @@ handle_wheel_event(double x, double y) { //////////////////////////////////////////////////////////////////// // Function: CocoaGraphicsWindow::map_key // Access: Private -// Description: +// Description: Maps a Cocoa key character to a ButtonHandle. //////////////////////////////////////////////////////////////////// ButtonHandle CocoaGraphicsWindow:: -map_key(unsigned short keycode) { - switch (keycode) { +map_key(unsigned short c) { + switch (c) { case NSEnterCharacter: return KeyboardButton::enter(); case NSBackspaceCharacter: @@ -1713,6 +1749,11 @@ map_key(unsigned short keycode) { // BackTabCharacter is sent when shift-tab is used. return KeyboardButton::tab(); + case 16: + // No idea where this constant comes from, but it + // is sent whenever the menu key is pressed. + return KeyboardButton::menu(); + case NSUpArrowFunctionKey: return KeyboardButton::up(); case NSDownArrowFunctionKey: @@ -1824,3 +1865,124 @@ map_key(unsigned short keycode) { } return ButtonHandle::none(); } + +//////////////////////////////////////////////////////////////////// +// Function: CocoaGraphicsWindow::map_raw_key +// Access: Private +// Description: Maps a keycode to a ButtonHandle. +//////////////////////////////////////////////////////////////////// +ButtonHandle CocoaGraphicsWindow:: +map_raw_key(unsigned short keycode) { + if (keycode > 0x7f) { + return ButtonHandle::none(); + } + switch ((unsigned char) keycode) { + /* See HIToolBox/Events.h */ + case 0x00: return KeyboardButton::ascii_key('a'); + case 0x01: return KeyboardButton::ascii_key('s'); + case 0x02: return KeyboardButton::ascii_key('d'); + case 0x03: return KeyboardButton::ascii_key('f'); + case 0x04: return KeyboardButton::ascii_key('h'); + case 0x05: return KeyboardButton::ascii_key('g'); + case 0x06: return KeyboardButton::ascii_key('z'); + case 0x07: return KeyboardButton::ascii_key('x'); + case 0x08: return KeyboardButton::ascii_key('c'); + case 0x09: return KeyboardButton::ascii_key('v'); + case 0x0B: return KeyboardButton::ascii_key('b'); + case 0x0C: return KeyboardButton::ascii_key('q'); + case 0x0D: return KeyboardButton::ascii_key('w'); + case 0x0E: return KeyboardButton::ascii_key('e'); + case 0x0F: return KeyboardButton::ascii_key('r'); + case 0x10: return KeyboardButton::ascii_key('y'); + case 0x11: return KeyboardButton::ascii_key('t'); + case 0x12: return KeyboardButton::ascii_key('1'); + case 0x13: return KeyboardButton::ascii_key('2'); + case 0x14: return KeyboardButton::ascii_key('3'); + case 0x15: return KeyboardButton::ascii_key('4'); + case 0x16: return KeyboardButton::ascii_key('6'); + case 0x17: return KeyboardButton::ascii_key('5'); + case 0x18: return KeyboardButton::ascii_key('='); + case 0x19: return KeyboardButton::ascii_key('9'); + case 0x1A: return KeyboardButton::ascii_key('7'); + case 0x1B: return KeyboardButton::ascii_key('-'); + case 0x1C: return KeyboardButton::ascii_key('8'); + case 0x1D: return KeyboardButton::ascii_key('0'); + case 0x1E: return KeyboardButton::ascii_key(']'); + case 0x1F: return KeyboardButton::ascii_key('o'); + case 0x20: return KeyboardButton::ascii_key('u'); + case 0x21: return KeyboardButton::ascii_key('['); + case 0x22: return KeyboardButton::ascii_key('i'); + case 0x23: return KeyboardButton::ascii_key('p'); + case 0x24: return KeyboardButton::enter(); + case 0x25: return KeyboardButton::ascii_key('l'); + case 0x26: return KeyboardButton::ascii_key('j'); + case 0x27: return KeyboardButton::ascii_key('\''); + case 0x28: return KeyboardButton::ascii_key('k'); + case 0x29: return KeyboardButton::ascii_key(';'); + case 0x2A: return KeyboardButton::ascii_key('\\'); + case 0x2B: return KeyboardButton::ascii_key(','); + case 0x2C: return KeyboardButton::ascii_key('/'); + case 0x2D: return KeyboardButton::ascii_key('n'); + case 0x2E: return KeyboardButton::ascii_key('m'); + case 0x2F: return KeyboardButton::ascii_key('.'); + case 0x30: return KeyboardButton::tab(); + case 0x31: return KeyboardButton::ascii_key(' '); + case 0x32: return KeyboardButton::ascii_key('`'); + case 0x33: return KeyboardButton::backspace(); + case 0x35: return KeyboardButton::escape(); + case 0x36: return KeyboardButton::rmeta(); + case 0x37: return KeyboardButton::lmeta(); + case 0x38: return KeyboardButton::lshift(); + case 0x39: return KeyboardButton::caps_lock(); + case 0x3A: return KeyboardButton::lalt(); + case 0x3B: return KeyboardButton::lcontrol(); + case 0x3C: return KeyboardButton::rshift(); + case 0x3D: return KeyboardButton::ralt(); + case 0x3E: return KeyboardButton::rcontrol(); + case 0x41: return KeyboardButton::ascii_key('.'); + case 0x43: return KeyboardButton::ascii_key('*'); + case 0x45: return KeyboardButton::ascii_key('+'); + case 0x47: return KeyboardButton::num_lock(); + case 0x4B: return KeyboardButton::ascii_key('/'); + case 0x4C: return KeyboardButton::enter(); + case 0x4E: return KeyboardButton::ascii_key('-'); + case 0x51: return KeyboardButton::ascii_key('='); + case 0x52: return KeyboardButton::ascii_key('0'); + case 0x53: return KeyboardButton::ascii_key('1'); + case 0x54: return KeyboardButton::ascii_key('2'); + case 0x55: return KeyboardButton::ascii_key('3'); + case 0x56: return KeyboardButton::ascii_key('4'); + case 0x57: return KeyboardButton::ascii_key('5'); + case 0x58: return KeyboardButton::ascii_key('6'); + case 0x59: return KeyboardButton::ascii_key('7'); + case 0x5B: return KeyboardButton::ascii_key('8'); + case 0x5C: return KeyboardButton::ascii_key('9'); + case 0x60: return KeyboardButton::f5(); + case 0x61: return KeyboardButton::f6(); + case 0x62: return KeyboardButton::f7(); + case 0x63: return KeyboardButton::f3(); + case 0x64: return KeyboardButton::f8(); + case 0x65: return KeyboardButton::f9(); + case 0x67: return KeyboardButton::f11(); + case 0x69: return KeyboardButton::print_screen(); + case 0x6B: return KeyboardButton::scroll_lock(); + case 0x6D: return KeyboardButton::f10(); + case 0x6E: return KeyboardButton::menu(); + case 0x6F: return KeyboardButton::f12(); + case 0x71: return KeyboardButton::pause(); + case 0x72: return KeyboardButton::insert(); + case 0x73: return KeyboardButton::home(); + case 0x74: return KeyboardButton::page_up(); + case 0x75: return KeyboardButton::del(); + case 0x76: return KeyboardButton::f4(); + case 0x77: return KeyboardButton::end(); + case 0x78: return KeyboardButton::f2(); + case 0x79: return KeyboardButton::page_down(); + case 0x7A: return KeyboardButton::f1(); + case 0x7B: return KeyboardButton::left(); + case 0x7C: return KeyboardButton::right(); + case 0x7D: return KeyboardButton::down(); + case 0x7E: return KeyboardButton::up(); + default: return ButtonHandle::none(); + } +} diff --git a/panda/src/cocoadisplay/cocoaPandaApp.h b/panda/src/cocoadisplay/cocoaPandaApp.h new file mode 100644 index 0000000000..a15d77a543 --- /dev/null +++ b/panda/src/cocoadisplay/cocoaPandaApp.h @@ -0,0 +1,21 @@ +// Filename: cocoaPandaApp.h +// Created by: rdb (08Mar14) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +#import + +// This class solely exists so that we can override sendEvent in order +// to prevent NSApplication from eating certain keyboard events. +@interface CocoaPandaApp : NSApplication +- (void) sendEvent: (NSEvent *) event; +@end diff --git a/panda/src/cocoadisplay/cocoaPandaApp.mm b/panda/src/cocoadisplay/cocoaPandaApp.mm new file mode 100644 index 0000000000..95efc8fe2d --- /dev/null +++ b/panda/src/cocoadisplay/cocoaPandaApp.mm @@ -0,0 +1,29 @@ +// Filename: cocoaPandaApp.mm +// Created by: rdb (08Mar14) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +#import "cocoaPandaApp.h" + +@implementation CocoaPandaApp +- (void) sendEvent: (NSEvent *) event { + // This is a hack that allows us to receive cmd-key-up events correctly. + // Also prevent it from eating the insert/help key. + if (([event type] == NSKeyUp && ([event modifierFlags] & NSCommandKeyMask)) + ||([event type] == NSKeyDown && [event keyCode] == 0x72)) { + + [[self keyWindow] sendEvent: event]; + } else { + [super sendEvent: event]; + } +} +@end diff --git a/panda/src/cocoadisplay/p3cocoadisplay_composite1.mm b/panda/src/cocoadisplay/p3cocoadisplay_composite1.mm index fe8d7d03de..28755a66dd 100644 --- a/panda/src/cocoadisplay/p3cocoadisplay_composite1.mm +++ b/panda/src/cocoadisplay/p3cocoadisplay_composite1.mm @@ -2,6 +2,7 @@ #include "cocoaGraphicsPipe.mm" #include "cocoaGraphicsStateGuardian.mm" #include "cocoaGraphicsWindow.mm" +#include "cocoaPandaApp.mm" #include "cocoaPandaView.mm" #include "cocoaPandaWindow.mm" -#include "cocoaPandaWindowDelegate.mm" \ No newline at end of file +#include "cocoaPandaWindowDelegate.mm" From ad3706e6f12af75c68390cbf44ae13d66f8dba8a Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 9 Mar 2014 00:05:53 +0000 Subject: [PATCH 66/71] Don't copy .pyc and .pyo files to built/direct --- makepanda/makepandacore.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makepanda/makepandacore.py b/makepanda/makepandacore.py index 1046aa29e4..0c760d30a0 100644 --- a/makepanda/makepandacore.py +++ b/makepanda/makepandacore.py @@ -2395,7 +2395,7 @@ def CopyPythonTree(dstdir, srcdir, lib2to3_fixers=[]): dstpth = os.path.join(dstdir, entry) if (os.path.isfile(srcpth)): base, ext = os.path.splitext(entry) - if (entry != ".cvsignore" and ext not in SUFFIX_INC): + if (entry != ".cvsignore" and ext not in SUFFIX_INC + ['.pyc', '.pyo']): if (NeedsBuild([dstpth], [srcpth])): WriteBinaryFile(dstpth, ReadBinaryFile(srcpth)) From e38b97b26faee2d1bc8932e3fdf97f232989792b Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 9 Mar 2014 00:57:09 +0000 Subject: [PATCH 67/71] fix for Python versions before 2.6 --- makepanda/makepanda.py | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index 27fc4d4dd4..b9bf4ee274 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -1641,7 +1641,11 @@ def RunGenPyCode(target, inputs, opts): if (PkgSkip("PYTHON") != 0): return - cmdstr = sys.executable + " -B " + os.path.join(GetOutputDir(), "direct", "ffi", "jGenPyCode.py") + cmdstr = sys.executable + " " + if sys.version_info >= (2, 6): + cmdstr += "-B " + + cmdstr += os.path.join(GetOutputDir(), "direct", "ffi", "jGenPyCode.py") if (GENMAN): cmdstr += " -d" cmdstr += " -r" for i in inputs: @@ -1661,7 +1665,11 @@ def RunGenPyCode(target, inputs, opts): def FreezePy(target, inputs, opts): assert len(inputs) > 0 # Make sure this function isn't called before genpycode is run. - cmdstr = sys.executable + " -B " + os.path.join("direct", "src", "showutil", "pfreeze.py") + cmdstr = sys.executable + " " + if sys.version_info >= (2, 6): + cmdstr += "-B " + + cmdstr += os.path.join("direct", "src", "showutil", "pfreeze.py") src = inputs.pop(0) for i in inputs: cmdstr += " -i " + os.path.splitext(i)[0] @@ -1685,10 +1693,14 @@ def FreezePy(target, inputs, opts): def Package(target, inputs, opts): assert len(inputs) == 1 # Invoke the ppackage script. - command = sys.executable - if (GetOptimizeOption(opts) >= 4): - command += " -OO" - command += " -B direct/src/p3d/ppackage.py" + command = sys.executable + " " + if GetOptimizeOption(opts) >= 4: + command += "-OO " + + if sys.version_info >= (2, 6): + command += "-B " + + command += "direct/src/p3d/ppackage.py" if GetTarget() == "darwin": if SDK.get("MACOSX") is not None: @@ -6321,7 +6333,12 @@ def MakeInstallerOSX(): if (RUNTIME): # Invoke the make_installer script. AddToPathEnv("DYLD_LIBRARY_PATH", GetOutputDir() + "/plugins") - oscmd(sys.executable + " -B direct/src/plugin_installer/make_installer.py --version %s" % VERSION) + cmdstr = sys.executable + " " + if sys.version_info >= (2, 6): + cmdstr += "-B " + + cmdstr += "direct/src/plugin_installer/make_installer.py --version %s" % VERSION + oscmd(cmdstr) return import compileall From ae4bddd7f7fe95dd45e408c1642bcad70dfd83b7 Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 9 Mar 2014 01:00:23 +0000 Subject: [PATCH 68/71] Fix Python 3 compile issue --- dtool/src/interrogate/interfaceMakerPythonNative.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dtool/src/interrogate/interfaceMakerPythonNative.cxx b/dtool/src/interrogate/interfaceMakerPythonNative.cxx index d8b1331efd..2e089b09d8 100755 --- a/dtool/src/interrogate/interfaceMakerPythonNative.cxx +++ b/dtool/src/interrogate/interfaceMakerPythonNative.cxx @@ -1942,7 +1942,7 @@ write_module_class(ostream &out, Object *obj) { out << " Dtool_" << ClassName << ".As_PyTypeObject().tp_flags |= Py_TPFLAGS_HAVE_ITER;\n"; } if (has_local_getbuffer) { - out << "#if PY_VERSION_HEX >= 0x02060000\n"; + out << "#if PY_VERSION_HEX >= 0x02060000 && PY_VERSION_HEX < 0x03000000\n"; out << " Dtool_" << ClassName << ".As_PyTypeObject().tp_flags |= Py_TPFLAGS_HAVE_NEWBUFFER;\n"; out << "#endif"; } From 1b93b8fbb309ece001579f25c56f9eb165a64379 Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 9 Mar 2014 01:05:34 +0000 Subject: [PATCH 69/71] Add methods to efficiently access PfmFile data from Python --- makepanda/makepanda.py | 2 + panda/src/pnmimage/Sources.pp | 2 + panda/src/pnmimage/pfmFile.h | 5 +- panda/src/pnmimage/pfmFile_ext.cxx | 132 +++++++++++++++++++++++++++++ panda/src/pnmimage/pfmFile_ext.h | 44 ++++++++++ 5 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 panda/src/pnmimage/pfmFile_ext.cxx create mode 100644 panda/src/pnmimage/pfmFile_ext.h diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index b9bf4ee274..5f15299514 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -3116,6 +3116,7 @@ if (not RUNTIME): TargetAdd('libp3pnmimage.in', opts=OPTS, input=IGATEFILES) TargetAdd('libp3pnmimage.in', opts=['IMOD:core', 'ILIB:libp3pnmimage', 'SRCDIR:panda/src/pnmimage']) TargetAdd('libp3pnmimage_igate.obj', input='libp3pnmimage.in', opts=["DEPENDENCYONLY"]) + TargetAdd('p3pnmimage_pfmFile_ext.obj', opts=OPTS, input='pfmFile_ext.cxx') # # DIRECTORY: panda/src/nativenet/ @@ -3540,6 +3541,7 @@ if (not RUNTIME): TargetAdd('libpanda.dll', input='libp3dtool.dll') TargetAdd('libpanda.dll', input='p3putil_typedWritable_ext.obj') + TargetAdd('libpanda.dll', input='p3pnmimage_pfmFile_ext.obj') TargetAdd('libpanda.dll', input='p3gobj_geomVertexArrayData_ext.obj') TargetAdd('libpanda.dll', input='p3pgraph_nodePath_ext.obj') TargetAdd('libpanda.dll', input='p3pgraph_nodePathCollection_ext.obj') diff --git a/panda/src/pnmimage/Sources.pp b/panda/src/pnmimage/Sources.pp index 76f67ed774..8ab8efb55b 100644 --- a/panda/src/pnmimage/Sources.pp +++ b/panda/src/pnmimage/Sources.pp @@ -12,6 +12,7 @@ #define SOURCES \ config_pnmimage.h \ pfmFile.I pfmFile.h \ + pfmFile_ext.cxx pfmFile_ext.h \ pnmbitio.h \ pnmBrush.h pnmBrush.I \ pnmFileType.h pnmFileTypeRegistry.h pnmImage.I \ @@ -36,6 +37,7 @@ #define INSTALL_HEADERS \ config_pnmimage.h \ pfmFile.I pfmFile.h \ + pfmFile_ext.cxx pfmFile_ext.h \ pnmBrush.h pnmBrush.I \ pnmFileType.h pnmFileTypeRegistry.h pnmImage.I \ pnmImage.h pnmImageHeader.I pnmImageHeader.h \ diff --git a/panda/src/pnmimage/pfmFile.h b/panda/src/pnmimage/pfmFile.h index 9a3cd444c4..1a212fbfb1 100644 --- a/panda/src/pnmimage/pfmFile.h +++ b/panda/src/pnmimage/pfmFile.h @@ -95,7 +95,7 @@ PUBLISHED: BLOCKING bool calc_autocrop(int &x_begin, int &x_end, int &y_begin, int &y_end) const; BLOCKING INLINE bool calc_autocrop(LVecBase4f &range) const; BLOCKING INLINE bool calc_autocrop(LVecBase4d &range) const; - + bool is_row_empty(int y, int x_begin, int x_end) const; bool is_column_empty(int x, int y_begin, int y_end) const; @@ -140,6 +140,9 @@ PUBLISHED: void output(ostream &out) const; + EXTENSION(PyObject *get_points() const); + EXTENSION(int __getbuffer__(PyObject *self, Py_buffer *view, int flags) const); + public: INLINE const vector_float &get_table() const; INLINE void swap_table(vector_float &table); diff --git a/panda/src/pnmimage/pfmFile_ext.cxx b/panda/src/pnmimage/pfmFile_ext.cxx new file mode 100644 index 0000000000..c28ed79b57 --- /dev/null +++ b/panda/src/pnmimage/pfmFile_ext.cxx @@ -0,0 +1,132 @@ +// Filename: pfmFile_ext.I +// Created by: rdb (26Feb14) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +#include "pfmFile_ext.h" + +#ifdef HAVE_PYTHON + +#ifndef CPPPARSER +IMPORT_THIS struct Dtool_PyTypedObject Dtool_LPoint2f; +IMPORT_THIS struct Dtool_PyTypedObject Dtool_LPoint3f; +IMPORT_THIS struct Dtool_PyTypedObject Dtool_LPoint4f; +#endif + +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::get_points +// Access: Published +// Description: Returns a list of all of the points. +//////////////////////////////////////////////////////////////////// +PyObject *Extension:: +get_points() const { + int num_points = _this->get_x_size() * _this->get_y_size(); + PyObject *list = PyList_New(num_points); + const vector_float &table = _this->get_table(); + + switch (_this->get_num_channels()) { + case 1: + for (int i = 0; i < num_points; ++i) { + PyList_SET_ITEM(list, i, PyFloat_FromDouble(table[i])); + } + break; + + case 2: + for (int i = 0; i < num_points; ++i) { + LPoint2f *point = (LPoint2f *) &(table[i * 2]); + PyObject *item = DTool_CreatePyInstance((void *)point, Dtool_LPoint2f, false, true); + PyList_SET_ITEM(list, i, item); + } + break; + + case 3: + for (int i = 0; i < num_points; ++i) { + LPoint3f *point = (LPoint3f *) &(table[i * 3]); + PyObject *item = DTool_CreatePyInstance((void *)point, Dtool_LPoint3f, false, true); + PyList_SET_ITEM(list, i, item); + } + break; + + case 4: + for (int i = 0; i < num_points; ++i) { + LPoint4f *point = (LPoint4f *) &(table[i * 4]); + PyObject *item = DTool_CreatePyInstance((void *)point, Dtool_LPoint4f, false, true); + PyList_SET_ITEM(list, i, item); + } + break; + + default: + Py_DECREF(list); + Py_INCREF(Py_None); + return Py_None; + } + + return list; +} + +#if PY_VERSION_HEX >= 0x02060000 +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::__getbuffer__ +// Access: Published +// Description: This is a very low-level function that returns a +// read-only multiview into the internal table of +// floating-point numbers. Use this method at your own +// risk. +//////////////////////////////////////////////////////////////////// +int Extension:: +__getbuffer__(PyObject *self, Py_buffer *view, int flags) const { + + if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE) { + PyErr_SetString(PyExc_BufferError, + "Object is not writable."); + return -1; + } + + // Since we have absolutely no guarantees about the lifetime + // of this object or the continued validity of the data pointer, + // we should arguably make a copy of the data. However, since + // the whole point of this method is to provide fast access to + // the underlying data, perhaps we can trust the user to handle + // the copy operation himself if he needs to. + const vector_float &table = _this->get_table(); + int channels = _this->get_num_channels(); + int num_pixels = _this->get_x_size() * _this->get_y_size(); + + if (self != NULL) { + Py_INCREF(self); + } + view->obj = self; + view->buf = (void *) &(table[0]); + view->len = 4 * table.size(); + view->readonly = 1; + view->itemsize = 4; + view->format = NULL; + if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) { + view->format = (char *) "f"; + } + view->ndim = 2; + view->shape = NULL; + if ((flags & PyBUF_ND) == PyBUF_ND) { + // If you're leaking and you know it, clap your hands! + view->shape = new Py_ssize_t[2]; + view->shape[0] = num_pixels; + view->shape[1] = channels; + } + view->strides = NULL; + view->suboffsets = NULL; + + return 0; +} + +#endif // PY_VERSION_HEX >= 0x02060000 + +#endif // HAVE_PYTHON diff --git a/panda/src/pnmimage/pfmFile_ext.h b/panda/src/pnmimage/pfmFile_ext.h new file mode 100644 index 0000000000..2018d8e394 --- /dev/null +++ b/panda/src/pnmimage/pfmFile_ext.h @@ -0,0 +1,44 @@ +// Filename: pfmFile_ext.h +// Created by: rdb (26Feb14) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +#ifndef PFMFILE_EXT_H +#define PFMFILE_EXT_H + +#include "dtoolbase.h" + +#ifdef HAVE_PYTHON + +#include "extension.h" +#include "pfmFile.h" +#include "py_panda.h" + +//////////////////////////////////////////////////////////////////// +// Class : Extension +// Description : This class defines the extension methods for +// PfmFile, which are called instead of +// any C++ methods with the same prototype. +//////////////////////////////////////////////////////////////////// +template<> +class Extension : public ExtensionBase { +public: + PyObject *get_points() const; + +#if PY_VERSION_HEX >= 0x02060000 + int __getbuffer__(PyObject *self, Py_buffer *view, int flags) const; +#endif +}; + +#endif // HAVE_PYTHON + +#endif // PFMFILE_EXT_H From a0f8208dc3b1dfcb203dfe93cae5dce90c974283 Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 9 Mar 2014 11:21:54 +0000 Subject: [PATCH 70/71] Add missing PY_VERSION_HEX guard --- panda/src/pnmimage/pfmFile.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/panda/src/pnmimage/pfmFile.h b/panda/src/pnmimage/pfmFile.h index 1a212fbfb1..7f22ef79c0 100644 --- a/panda/src/pnmimage/pfmFile.h +++ b/panda/src/pnmimage/pfmFile.h @@ -141,7 +141,10 @@ PUBLISHED: void output(ostream &out) const; EXTENSION(PyObject *get_points() const); + +#if PY_VERSION_HEX >= 0x02060000 EXTENSION(int __getbuffer__(PyObject *self, Py_buffer *view, int flags) const); +#endif public: INLINE const vector_float &get_table() const; From 31a192f27be2444149748705c855e24f0e44a20d Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 9 Mar 2014 11:43:08 +0000 Subject: [PATCH 71/71] Fix interrogate understanding of PY_VERSION_HEX --- dtool/src/parser-inc/Python.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dtool/src/parser-inc/Python.h b/dtool/src/parser-inc/Python.h index f0c9d32511..d63928fdfe 100755 --- a/dtool/src/parser-inc/Python.h +++ b/dtool/src/parser-inc/Python.h @@ -25,4 +25,7 @@ class PyThreadState; typedef int Py_ssize_t; struct Py_buffer; +// This file defines PY_VERSION_HEX, which is used in some places. +#include "patchlevel.h" + #endif // PYTHON_H