diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index 4f2abef8a9..4b20b479df 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -2325,7 +2325,6 @@ DTOOL_CONFIG=[ ("COMPILE_IN_DEFAULT_FONT", '1', '1'), ("STDFLOAT_DOUBLE", 'UNDEF', 'UNDEF'), ("HAVE_MAYA", '1', '1'), - ("HAVE_SOFTIMAGE", 'UNDEF', 'UNDEF'), ("REPORT_OPENSSL_ERRORS", '1', '1'), ("USE_PANDAFILESTREAM", '1', '1'), ("USE_DELETED_CHAIN", '1', '1'), diff --git a/panda/src/device/linuxInputDeviceManager.cxx b/panda/src/device/linuxInputDeviceManager.cxx index 8eed3e1cc3..7b9b9fdf6f 100644 --- a/panda/src/device/linuxInputDeviceManager.cxx +++ b/panda/src/device/linuxInputDeviceManager.cxx @@ -61,6 +61,9 @@ LinuxInputDeviceManager() { // We'll want to sort the devices by index, since the order may be // meaningful (eg. for the Xbox wireless receiver). + if (indices.empty()) { + return; + } std::sort(indices.begin(), indices.end()); _evdev_devices.resize(indices.back() + 1, nullptr); diff --git a/panda/src/express/zStreamBuf.cxx b/panda/src/express/zStreamBuf.cxx index 877254918d..57a64bed7a 100644 --- a/panda/src/express/zStreamBuf.cxx +++ b/panda/src/express/zStreamBuf.cxx @@ -202,8 +202,8 @@ seekoff(streamoff off, ios_seekdir dir, ios_openmode which) { gbump(n); - _source->seekg(0, ios::beg); - if (_source->tellg() == (streampos)0) { + if (_source->rdbuf()->pubseekpos(0, ios::in) == (streampos)0) { + _source->clear(); _z_source.next_in = Z_NULL; _z_source.avail_in = 0; _z_source.next_out = Z_NULL; diff --git a/panda/src/movies/movieTypeRegistry.cxx b/panda/src/movies/movieTypeRegistry.cxx index 5bc8228bb4..dca0ef9400 100644 --- a/panda/src/movies/movieTypeRegistry.cxx +++ b/panda/src/movies/movieTypeRegistry.cxx @@ -31,6 +31,12 @@ PT(MovieAudio) MovieTypeRegistry:: make_audio(const Filename &name) { string ext = downcase(name.get_extension()); +#ifdef HAVE_ZLIB + if (ext == "pz" || ext == "gz") { + ext = Filename(name.get_basename_wo_extension()).get_extension(); + } +#endif + _audio_lock.lock(); // Make sure that the list of audio types has been read in. @@ -154,6 +160,12 @@ PT(MovieVideo) MovieTypeRegistry:: make_video(const Filename &name) { string ext = downcase(name.get_extension()); +#ifdef HAVE_ZLIB + if (ext == "pz" || ext == "gz") { + ext = Filename(name.get_basename_wo_extension()).get_extension(); + } +#endif + _video_lock.lock(); // Make sure that the list of video types has been read in. diff --git a/panda/src/movies/opusAudioCursor.cxx b/panda/src/movies/opusAudioCursor.cxx index 8554215088..b5fb8b31b5 100644 --- a/panda/src/movies/opusAudioCursor.cxx +++ b/panda/src/movies/opusAudioCursor.cxx @@ -56,6 +56,22 @@ int cb_seek(void *stream, opus_int64 offset, int whence) { break; case SEEK_CUR: + // opusfile uses a seek with offset 0 to determine whether seeking is + // supported, but this is not good enough. We seek to the end and back. + if (offset == 0) { + std::streambuf *buf = in->rdbuf(); + std::streampos pos = buf->pubseekoff(0, std::ios::cur, std::ios::in); + if (pos < 0) { + return -1; + } + if (buf->pubseekoff(0, std::ios::end, std::ios::in) >= 0) { + // It worked; seek back to the previous location. + buf->pubseekpos(pos, std::ios::in); + return 0; + } else { + return -1; + } + } in->seekg(offset, std::ios::cur); break; diff --git a/panda/src/movies/vorbisAudioCursor.cxx b/panda/src/movies/vorbisAudioCursor.cxx index d3210ae46e..80c5613b0c 100644 --- a/panda/src/movies/vorbisAudioCursor.cxx +++ b/panda/src/movies/vorbisAudioCursor.cxx @@ -91,19 +91,46 @@ seek(double t) { t = std::max(t, 0.0); // Use ov_time_seek_lap if cross-lapping is enabled. + int result; if (vorbis_seek_lap) { - if (ov_time_seek_lap(&_ov, t) != 0) { - movies_cat.error() - << "Seek failed. Ogg Vorbis stream may not be seekable.\n"; - return; - } + result = ov_time_seek_lap(&_ov, t); } else { - if (ov_time_seek(&_ov, t) != 0) { - movies_cat.error() - << "Seek failed. Ogg Vorbis stream may not be seekable.\n"; + result = ov_time_seek(&_ov, t); + } + + // Special case for seeking to the beginning; if normal seek fails, we may + // be able to explicitly seek to the beginning of the file and call ov_open + // again. This allows looping compressed .ogg files. + if (result == OV_ENOSEEK && t == 0.0) { + std::istream *stream = (std::istream *)_ov.datasource; + + if (stream->rdbuf()->pubseekpos(0, std::ios::in) == 0) { + // Back up the callbacks, then destroy the stream, making sure to first + // unset the datasource so that it won't close the file. + ov_callbacks callbacks = _ov.callbacks; + _ov.datasource = nullptr; + ov_clear(&_ov); + + if (ov_open_callbacks((void *)stream, &_ov, nullptr, 0, callbacks) != 0) { + movies_cat.error() + << "Failed to reopen Ogg Vorbis file to seek to beginning.\n"; + return; + } + + // Reset these fields for good measure, just in case the file changed. + vorbis_info *vi = ov_info(&_ov, -1); + _audio_channels = vi->channels; + _audio_rate = vi->rate; + + _last_seek = 0.0; + _samples_read = 0; return; } } + if (result != 0) { + movies_cat.error() + << "Seek failed. Ogg Vorbis stream may not be seekable.\n"; + } _last_seek = ov_time_tell(&_ov); _samples_read = 0; @@ -199,6 +226,22 @@ cb_seek_func(void *datasource, ogg_int64_t offset, int whence) { break; case SEEK_CUR: + // Vorbis uses a seek with offset 0 to determine whether seeking is + // supported, but this is not good enough. We seek to the end and back. + if (offset == 0) { + std::streambuf *buf = stream->rdbuf(); + std::streampos pos = buf->pubseekoff(0, std::ios::cur, std::ios::in); + if (pos < 0) { + return -1; + } + if (buf->pubseekoff(0, std::ios::end, std::ios::in) >= 0) { + // It worked; seek back to the previous location. + buf->pubseekpos(pos, std::ios::in); + return 0; + } else { + return -1; + } + } stream->seekg(offset, std::ios::cur); break; diff --git a/panda/src/movies/wavAudioCursor.cxx b/panda/src/movies/wavAudioCursor.cxx index 381ce2d81b..4b58794d46 100644 --- a/panda/src/movies/wavAudioCursor.cxx +++ b/panda/src/movies/wavAudioCursor.cxx @@ -294,27 +294,61 @@ seek(double t) { t = std::max(t, 0.0); std::streampos pos = _data_start + (std::streampos) std::min((size_t) (t * _byte_rate), _data_size); + std::streambuf *buf = _stream->rdbuf(); + if (_can_seek_fast) { - _stream->seekg(pos); - if (_stream->tellg() != pos) { + if (buf->pubseekpos(pos, std::ios::in) != pos) { // Clearly, we can't seek fast. Fall back to the case below. _can_seek_fast = false; } } - if (!_can_seek_fast) { - std::streampos current = _stream->tellg(); + // Get the current position of the cursor in the file. + std::streampos current = buf->pubseekoff(0, std::ios::cur, std::ios::in); + if (!_can_seek_fast) { if (pos > current) { // It is ahead of our current position. Skip ahead. - _reader.skip_bytes(pos - current); + _stream->ignore(pos - current); + current = pos; } else if (pos < current) { - // We'll have to reopen the file. TODO + // Can we seek to the beginning? Some streams, such as ZStream, let us + // rewind the stream. + if (buf->pubseekpos(0, std::ios::in) == 0) { + if (pos > _data_start && movies_cat.is_info()) { + Filename fn = get_source()->get_filename(); + movies_cat.info() + << "Unable to seek backwards in " << fn.get_basename() + << "; seeking to beginning and skipping " << pos << " bytes.\n"; + } + _stream->ignore(pos); + current = pos; + } else { + // No; close and reopen the file. + Filename fn = get_source()->get_filename(); + movies_cat.warning() + << "Unable to seek backwards in " << fn.get_basename() + << "; reopening and skipping " << pos << " bytes.\n"; + + VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); + std::istream *stream = vfs->open_read_file(get_source()->get_filename(), true); + if (stream != nullptr) { + vfs->close_read_file(_stream); + stream->ignore(pos); + _stream = stream; + _reader = StreamReader(stream, false); + current = pos; + } else { + movies_cat.error() + << "Unable to reopen " << fn << ".\n"; + _can_seek = false; + } + } } } - _data_pos = _stream->tellg() - _data_start; + _data_pos = (size_t)current - _data_start; _last_seek = _data_pos / _byte_rate; _samples_read = 0; } diff --git a/panda/src/ode/odeBody.h b/panda/src/ode/odeBody.h index f1cd9dae68..5cdfd4c8d1 100644 --- a/panda/src/ode/odeBody.h +++ b/panda/src/ode/odeBody.h @@ -133,6 +133,7 @@ PUBLISHED: OdeJoint get_joint(int index) const; MAKE_SEQ(get_joints, get_num_joints, get_joint); EXTENSION(INLINE PyObject *get_converted_joint(int i) const); + MAKE_SEQ_PROPERTY(joints, get_num_joints, get_converted_joint); INLINE void enable(); INLINE void disable(); diff --git a/panda/src/ode/odeJoint.h b/panda/src/ode/odeJoint.h index eda2a98b96..47233a9361 100644 --- a/panda/src/ode/odeJoint.h +++ b/panda/src/ode/odeJoint.h @@ -83,7 +83,7 @@ PUBLISHED: INLINE void set_feedback(bool flag = true); INLINE OdeJointFeedback *get_feedback(); - EXTENSION(void attach(const OdeBody *body1, const OdeBody *body2)); + EXTENSION(void attach(PyObject *body1, PyObject *body2)); void attach_bodies(const OdeBody &body1, const OdeBody &body2); void attach_body(const OdeBody &body, int index); void detach(); diff --git a/panda/src/ode/odeJoint_ext.cxx b/panda/src/ode/odeJoint_ext.cxx index 6f284e1213..57669b7644 100644 --- a/panda/src/ode/odeJoint_ext.cxx +++ b/panda/src/ode/odeJoint_ext.cxx @@ -29,6 +29,7 @@ #include "odePlane2dJoint.h" #ifndef CPPPARSER +extern Dtool_PyTypedObject Dtool_OdeBody; extern Dtool_PyTypedObject Dtool_OdeJoint; extern Dtool_PyTypedObject Dtool_OdeBallJoint; extern Dtool_PyTypedObject Dtool_OdeHingeJoint; @@ -48,7 +49,23 @@ extern Dtool_PyTypedObject Dtool_OdePlane2dJoint; * attached to the environment. */ void Extension:: -attach(const OdeBody *body1, const OdeBody *body2) { +attach(PyObject *param1, PyObject *param2) { + const OdeBody *body1 = nullptr; + if (param1 != Py_None) { + body1 = (const OdeBody *)DTOOL_Call_GetPointerThisClass(param1, &Dtool_OdeBody, 1, "OdeJoint.attach", true, true); + if (body1 == nullptr) { + return; + } + } + + const OdeBody *body2 = nullptr; + if (param2 != Py_None) { + body2 = (const OdeBody *)DTOOL_Call_GetPointerThisClass(param2, &Dtool_OdeBody, 2, "OdeJoint.attach", true, true); + if (body2 == nullptr) { + return; + } + } + if (body1 && body2) { _this->attach_bodies(*body1, *body2); diff --git a/panda/src/ode/odeJoint_ext.h b/panda/src/ode/odeJoint_ext.h index b61938506f..84dbf4ac93 100644 --- a/panda/src/ode/odeJoint_ext.h +++ b/panda/src/ode/odeJoint_ext.h @@ -30,7 +30,7 @@ template<> class Extension : public ExtensionBase { public: - void attach(const OdeBody *body1, const OdeBody *body2); + void attach(PyObject *body1, PyObject *body2); PyObject *convert() const; }; diff --git a/tests/ode/conftest.py b/tests/ode/conftest.py new file mode 100644 index 0000000000..289304cf0f --- /dev/null +++ b/tests/ode/conftest.py @@ -0,0 +1,7 @@ +import pytest + + +@pytest.fixture +def world(): + ode = pytest.importorskip("panda3d.ode") + return ode.OdeWorld() diff --git a/tests/ode/test_ode_joints.py b/tests/ode/test_ode_joints.py new file mode 100644 index 0000000000..d1e202412d --- /dev/null +++ b/tests/ode/test_ode_joints.py @@ -0,0 +1,43 @@ +import pytest + + +def test_odejoint_attach_both(world): + from panda3d import ode + + body1 = ode.OdeBody(world) + body2 = ode.OdeBody(world) + + assert len(body1.joints) == 0 + assert len(body2.joints) == 0 + + joint = ode.OdeBallJoint(world) + joint.attach(body1, body2) + + assert tuple(body1.joints) == (joint,) + assert tuple(body2.joints) == (joint,) + + +def test_odejoint_attach_0(world): + from panda3d import ode + + body = ode.OdeBody(world) + + assert len(body.joints) == 0 + + joint = ode.OdeBallJoint(world) + joint.attach(body, None) + + assert tuple(body.joints) == (joint,) + + +def test_odejoint_attach_1(world): + from panda3d import ode + + body = ode.OdeBody(world) + + assert len(body.joints) == 0 + + joint = ode.OdeBallJoint(world) + joint.attach(None, body) + + assert tuple(body.joints) == (joint,)