From be247e1be9236cadb9a7359e77cab86993d203e6 Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Mon, 15 Apr 2019 16:46:06 -0600 Subject: [PATCH 1/9] makepanda: Remove config entry for defunct HAVE_SOFTIMAGE --- makepanda/makepanda.py | 1 - 1 file changed, 1 deletion(-) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index 25ee8a3a80..6a10cae138 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -2364,7 +2364,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'), From 186d8feef4f168b1b30b7b1bd07de397a01c0cc0 Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 28 Apr 2019 21:49:23 +0200 Subject: [PATCH 2/9] movies: properly detect extension of pz/gz audio/video files --- panda/src/movies/movieTypeRegistry.cxx | 12 ++++++++++++ 1 file changed, 12 insertions(+) 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. From ca5b4e7b54d3f7bdf373400a985bbf33484264c4 Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 28 Apr 2019 22:28:55 +0200 Subject: [PATCH 3/9] ode: fix OdeJoint.attach with None parameters Fixes #633 --- panda/src/ode/odeJoint.h | 2 +- panda/src/ode/odeJoint_ext.cxx | 19 ++++++++++++++- panda/src/ode/odeJoint_ext.h | 2 +- tests/ode/conftest.py | 7 ++++++ tests/ode/test_ode_joints.py | 43 ++++++++++++++++++++++++++++++++++ 5 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 tests/ode/conftest.py create mode 100644 tests/ode/test_ode_joints.py 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,) From 00b3fbdb1ab9b9dce6ca8a88287563a7be687c9d Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 28 Apr 2019 22:57:06 +0200 Subject: [PATCH 4/9] movies: fix for loading unseekable ogg vorbis files Now, compressed/encrypted ogg files will properly be detected as unseekable, and will still be able to be played. --- panda/src/movies/vorbisAudioCursor.cxx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/panda/src/movies/vorbisAudioCursor.cxx b/panda/src/movies/vorbisAudioCursor.cxx index d3210ae46e..97666d0056 100644 --- a/panda/src/movies/vorbisAudioCursor.cxx +++ b/panda/src/movies/vorbisAudioCursor.cxx @@ -199,6 +199,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; From bfb50ab7ff85b409b93908e14897a3abd2a4a032 Mon Sep 17 00:00:00 2001 From: Rishabh Tewari Date: Sun, 28 Apr 2019 13:03:17 -0500 Subject: [PATCH 5/9] device: don't crash on Linux if no devices are found Fixes #634 Closes #635 --- panda/src/device/linuxInputDeviceManager.cxx | 3 +++ 1 file changed, 3 insertions(+) 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); From 00b5faca2d5905c514fadff08ef745636592a0b0 Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 29 Apr 2019 01:06:44 +0200 Subject: [PATCH 6/9] ode: add OdeBody.joints property --- panda/src/ode/odeBody.h | 1 + 1 file changed, 1 insertion(+) 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(); From a7265922448436ce9c02aff3d04eaef788a76465 Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 29 Apr 2019 10:48:04 +0200 Subject: [PATCH 7/9] movies: support looping compressed .wav files Any kind of skipping is supported on any kind of stream, actually, but some uses may require reopening the file and skipping some number of bytes, so a warning will be displayed in some cases. I figure this feature is particularly interesting for .wav files since they are (1) often used for short samples, where skipping bytes is not really a big deal, and (2) aren't inherently compressed so would benefit particularly from zlib compression. --- panda/src/express/zStreamBuf.cxx | 4 +-- panda/src/movies/wavAudioCursor.cxx | 48 ++++++++++++++++++++++++----- 2 files changed, 43 insertions(+), 9 deletions(-) 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/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; } From 2c55663472f50f5e50ea0a605a9f15bf90822fb0 Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 29 Apr 2019 11:39:30 +0200 Subject: [PATCH 8/9] movies: allow seeking compressed Ogg Vorbis file to beginning --- panda/src/movies/vorbisAudioCursor.cxx | 43 +++++++++++++++++++++----- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/panda/src/movies/vorbisAudioCursor.cxx b/panda/src/movies/vorbisAudioCursor.cxx index 97666d0056..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; From fa1ff3d48914ba115565e04babb8fda82ad7f32b Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 29 Apr 2019 11:41:13 +0200 Subject: [PATCH 9/9] movies: fix for loading unseekable Opus files Same fix as 00b3fbdb1ab9b9dce6ca8a88287563a7be687c9d but for Opus files. --- panda/src/movies/opusAudioCursor.cxx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) 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;