mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-04-22 11:18:22 -05:00
Add animationmode realtime looping
This commit is contained in:
@@ -59,6 +59,31 @@ namespace {
|
||||
"'YYYY MM DD hh:mm:ss'."
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo AnimationModeInfo = {
|
||||
"AnimationMode",
|
||||
"Animation Mode",
|
||||
"Determines the way the video should be played. The start and end time of the "
|
||||
"video can be set, or the video can be played as a loop in real time."
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo PlayInfo = {
|
||||
"Play",
|
||||
"Play",
|
||||
"Play video"
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo PauseInfo = {
|
||||
"Pause",
|
||||
"Pause",
|
||||
"Pause video"
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo GoToStartInfo = {
|
||||
"GoToStart",
|
||||
"Go To Start",
|
||||
"Go to start in video"
|
||||
};
|
||||
|
||||
struct [[codegen::Dictionary(VideoTileProvider)]] Parameters {
|
||||
// [[codegen::verbatim(FileInfo.description)]]
|
||||
std::filesystem::path file;
|
||||
@@ -71,8 +96,7 @@ namespace {
|
||||
|
||||
enum class AnimationMode {
|
||||
MapToSimulationTime = 0,
|
||||
RealTimeLoopFromStart,
|
||||
RealTimeLoopInfinitely
|
||||
RealTimeLoop
|
||||
};
|
||||
|
||||
// The mode of how the animation should be played back.
|
||||
@@ -85,123 +109,136 @@ namespace {
|
||||
|
||||
namespace openspace::globebrowsing {
|
||||
|
||||
int VideoTileProvider::_wakeup = 0;
|
||||
int VideoTileProvider::_wakeup = 0;
|
||||
|
||||
bool checkMpvError(int status) {
|
||||
if (status < 0) {
|
||||
LERROR(fmt::format("Libmpv API error: {}", mpv_error_string(status)));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
bool checkMpvError(int status) {
|
||||
if (status < 0) {
|
||||
LERROR(fmt::format("Libmpv API error: {}", mpv_error_string(status)));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void* getOpenGLProcAddress(void*, const char* name) {
|
||||
return reinterpret_cast<void*>(
|
||||
global::windowDelegate->openGLProcedureAddress(name)
|
||||
);
|
||||
void* getOpenGLProcAddress(void*, const char* name) {
|
||||
return reinterpret_cast<void*>(
|
||||
global::windowDelegate->openGLProcedureAddress(name)
|
||||
);
|
||||
}
|
||||
|
||||
void VideoTileProvider::on_mpv_render_update(void*) {
|
||||
// The wakeup flag is set here to enable the mpv_render_context_render
|
||||
// path in the main loop.
|
||||
_wakeup = 1;
|
||||
}
|
||||
|
||||
void VideoTileProvider::observePropertyMpv(std::string name, mpv_format format,
|
||||
LibmpvPropertyKey key) {
|
||||
mpv_observe_property(
|
||||
_mpvHandle,
|
||||
static_cast<uint64_t>(key),
|
||||
name.c_str(),
|
||||
format
|
||||
);
|
||||
}
|
||||
|
||||
void VideoTileProvider::setPropertyStringMpv(std::string name, std::string value) {
|
||||
int result = mpv_set_property_string(_mpvHandle, name.c_str(), value.c_str());
|
||||
if (!checkMpvError(result)) {
|
||||
LWARNING(fmt::format("Error setting property {}", name));
|
||||
}
|
||||
}
|
||||
|
||||
void VideoTileProvider::on_mpv_render_update(void*) {
|
||||
// The wakeup flag is set here to enable the mpv_render_context_render
|
||||
// path in the main loop.
|
||||
_wakeup = 1;
|
||||
}
|
||||
documentation::Documentation VideoTileProvider::Documentation() {
|
||||
return codegen::doc<Parameters>("globebrowsing_videotileprovider");
|
||||
}
|
||||
|
||||
void VideoTileProvider::observePropertyMpv(std::string name, mpv_format format,
|
||||
LibmpvPropertyKey key) {
|
||||
mpv_observe_property(
|
||||
_mpvHandle,
|
||||
static_cast<uint64_t>(key),
|
||||
name.c_str(),
|
||||
format
|
||||
);
|
||||
}
|
||||
VideoTileProvider::VideoTileProvider(const ghoul::Dictionary& dictionary)
|
||||
: _play(PlayInfo)
|
||||
, _pause(PauseInfo)
|
||||
, _goToStart(GoToStartInfo)
|
||||
{
|
||||
ZoneScoped
|
||||
|
||||
void VideoTileProvider::setPropertyStringMpv(std::string name, std::string value) {
|
||||
int result = mpv_set_property_string(_mpvHandle, name.c_str(), value.c_str());
|
||||
if (!checkMpvError(result)) {
|
||||
LWARNING(fmt::format("Error setting property {}", name));
|
||||
const Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
|
||||
_videoFile = p.file;
|
||||
|
||||
if (p.animationMode.has_value()) {
|
||||
switch (*p.animationMode) {
|
||||
case Parameters::AnimationMode::RealTimeLoop:
|
||||
_animationMode = AnimationMode::RealTimeLoop;
|
||||
break;
|
||||
case Parameters::AnimationMode::MapToSimulationTime:
|
||||
_animationMode = AnimationMode::MapToSimulationTime;
|
||||
break;
|
||||
default:
|
||||
throw ghoul::MissingCaseException();
|
||||
}
|
||||
}
|
||||
|
||||
documentation::Documentation VideoTileProvider::Documentation() {
|
||||
return codegen::doc<Parameters>("globebrowsing_videotileprovider");
|
||||
if (_animationMode == AnimationMode::RealTimeLoop) {
|
||||
// Video interaction. Only valid for real time looping
|
||||
_play.onChange([this]() { play(); });
|
||||
addProperty(_play);
|
||||
_pause.onChange([this]() { pause(); });
|
||||
addProperty(_pause);
|
||||
_goToStart.onChange([this]() { goToStart(); });
|
||||
addProperty(_goToStart);
|
||||
}
|
||||
|
||||
VideoTileProvider::VideoTileProvider(const ghoul::Dictionary& dictionary) {
|
||||
ZoneScoped
|
||||
|
||||
const Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
|
||||
_videoFile = p.file;
|
||||
|
||||
if (p.animationMode.has_value()) {
|
||||
switch (*p.animationMode) {
|
||||
case Parameters::AnimationMode::RealTimeLoopFromStart:
|
||||
_animationMode = AnimationMode::RealTimeLoopFromStart;
|
||||
break;
|
||||
case Parameters::AnimationMode::RealTimeLoopInfinitely:
|
||||
_animationMode = AnimationMode::RealTimeLoopInfinitely;
|
||||
break;
|
||||
case Parameters::AnimationMode::MapToSimulationTime:
|
||||
_animationMode = AnimationMode::MapToSimulationTime;
|
||||
break;
|
||||
default:
|
||||
throw ghoul::MissingCaseException();
|
||||
}
|
||||
}
|
||||
else if (_animationMode == AnimationMode::MapToSimulationTime) {
|
||||
_startJ200Time = Time::convertTime(p.startTime);
|
||||
_endJ200Time = Time::convertTime(p.endTime);
|
||||
ghoul_assert(_endJ200Time > _startJ200Time, "Invalid times for video");
|
||||
|
||||
global::callback::postSyncPreDraw->emplace_back([this]() {
|
||||
// Initialize mpv here to ensure that the opengl context is the same as in for
|
||||
// the rendering
|
||||
if (!_isInitialized) {
|
||||
initializeMpv();
|
||||
}
|
||||
else {
|
||||
renderMpv();
|
||||
}
|
||||
});
|
||||
|
||||
global::callback::postDraw->emplace_back([this]() {
|
||||
swapBuffersMpv();
|
||||
});
|
||||
}
|
||||
|
||||
Tile VideoTileProvider::tile(const TileIndex& tileIndex) {
|
||||
ZoneScoped
|
||||
|
||||
|
||||
global::callback::postSyncPreDraw->emplace_back([this]() {
|
||||
// Initialize mpv here to ensure that the opengl context is the same as in for
|
||||
// the rendering
|
||||
if (!_isInitialized) {
|
||||
return Tile();
|
||||
}
|
||||
|
||||
// Always check that our framebuffer is ok
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||
LINFO("Framebuffer is not complete");
|
||||
}
|
||||
|
||||
return Tile{ _frameTexture, std::nullopt, Tile::Status::OK };
|
||||
}
|
||||
|
||||
Tile::Status VideoTileProvider::tileStatus(const TileIndex& tileIndex) {
|
||||
if (tileIndex.level > maxLevel()) {
|
||||
return Tile::Status::OutOfRange;
|
||||
}
|
||||
else if (_tileIsReady) {
|
||||
return Tile::Status::OK;
|
||||
initializeMpv();
|
||||
}
|
||||
else {
|
||||
return Tile::Status::Unavailable;
|
||||
renderMpv();
|
||||
}
|
||||
});
|
||||
|
||||
global::callback::postDraw->emplace_back([this]() {
|
||||
swapBuffersMpv();
|
||||
});
|
||||
}
|
||||
|
||||
Tile VideoTileProvider::tile(const TileIndex& tileIndex) {
|
||||
ZoneScoped
|
||||
|
||||
if (!_isInitialized) {
|
||||
return Tile();
|
||||
}
|
||||
|
||||
TileDepthTransform VideoTileProvider::depthTransform() {
|
||||
return { 0.f, 1.f };
|
||||
// Always check that our framebuffer is ok
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||
LINFO("Framebuffer is not complete");
|
||||
}
|
||||
|
||||
void VideoTileProvider::update() {}
|
||||
return Tile{ _frameTexture, std::nullopt, Tile::Status::OK };
|
||||
}
|
||||
|
||||
Tile::Status VideoTileProvider::tileStatus(const TileIndex& tileIndex) {
|
||||
if (tileIndex.level > maxLevel()) {
|
||||
return Tile::Status::OutOfRange;
|
||||
}
|
||||
else if (_tileIsReady) {
|
||||
return Tile::Status::OK;
|
||||
}
|
||||
else {
|
||||
return Tile::Status::Unavailable;
|
||||
}
|
||||
}
|
||||
|
||||
TileDepthTransform VideoTileProvider::depthTransform() {
|
||||
return { 0.f, 1.f };
|
||||
}
|
||||
|
||||
void VideoTileProvider::update() {}
|
||||
|
||||
ChunkTile VideoTileProvider::chunkTile(TileIndex tileIndex, int parents, int maxParents) {
|
||||
ZoneScoped
|
||||
@@ -253,6 +290,11 @@ void VideoTileProvider::play() {
|
||||
}
|
||||
}
|
||||
|
||||
void VideoTileProvider::goToStart() {
|
||||
seekToTime(0.0);
|
||||
}
|
||||
|
||||
|
||||
void VideoTileProvider::initializeMpv() {
|
||||
_mpvHandle = mpv_create();
|
||||
if (!_mpvHandle) {
|
||||
@@ -391,7 +433,7 @@ void VideoTileProvider::pauseVideoIfOutsideValidTime() {
|
||||
|
||||
void VideoTileProvider::seekToTime(double time) {
|
||||
bool isPlaying = !_isPaused;
|
||||
bool seekIsDifferent = abs(time - _lastSeek) > _frameTime;
|
||||
bool seekIsDifferent = abs(time - _currentVideoTime) > _frameTime;
|
||||
if (seekIsDifferent) {
|
||||
// Pause while seeking
|
||||
pause();
|
||||
@@ -408,7 +450,6 @@ void VideoTileProvider::seekToTime(double time) {
|
||||
LINFO("Seek resulted in invalid operation");
|
||||
}
|
||||
|
||||
_lastSeek = time;
|
||||
// Play if video was playing before seek
|
||||
if (isPlaying) {
|
||||
play();
|
||||
@@ -431,16 +472,19 @@ void VideoTileProvider::updateStretchingOfTime() {
|
||||
|
||||
void VideoTileProvider::renderMpv() {
|
||||
|
||||
pauseVideoIfOutsideValidTime();
|
||||
if (_animationMode == AnimationMode::MapToSimulationTime) {
|
||||
pauseVideoIfOutsideValidTime();
|
||||
|
||||
handleMpvEvents();
|
||||
|
||||
double time = correctVideoPlaybackTime();
|
||||
bool shouldSeek = abs(time - _currentVideoTime) > _frameTime;
|
||||
|
||||
if (shouldSeek) {
|
||||
seekToTime(time);
|
||||
double time = correctVideoPlaybackTime();
|
||||
bool shouldSeek = abs(time - _currentVideoTime) > _frameTime;
|
||||
|
||||
if (shouldSeek) {
|
||||
seekToTime(time);
|
||||
}
|
||||
}
|
||||
|
||||
handleMpvEvents();
|
||||
|
||||
|
||||
if (_wakeup) {
|
||||
if ((mpv_render_context_update(_mpvRenderContext) & MPV_RENDER_UPDATE_FRAME)) {
|
||||
@@ -605,7 +649,10 @@ void VideoTileProvider::handleMpvProperties(mpv_event* event) {
|
||||
}
|
||||
|
||||
_videoDuration = *duration;
|
||||
updateStretchingOfTime();
|
||||
if (_animationMode == AnimationMode::MapToSimulationTime) {
|
||||
updateStretchingOfTime();
|
||||
}
|
||||
|
||||
LINFO(fmt::format("Duration: {}", *duration));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include <modules/globebrowsing/src/tileprovider/tileprovider.h>
|
||||
|
||||
#include <openspace/properties/triggerproperty.h>
|
||||
#include <ghoul/glm.h>
|
||||
|
||||
// libmpv
|
||||
@@ -58,10 +59,15 @@ public:
|
||||
|
||||
void pause();
|
||||
void play();
|
||||
void goToStart();
|
||||
|
||||
static documentation::Documentation Documentation();
|
||||
|
||||
private:
|
||||
properties::TriggerProperty _play;
|
||||
properties::TriggerProperty _pause;
|
||||
properties::TriggerProperty _goToStart;
|
||||
|
||||
void createFBO(int width, int height);
|
||||
void resizeFBO(int width, int height);
|
||||
double correctVideoPlaybackTime() const;
|
||||
@@ -84,11 +90,10 @@ private:
|
||||
|
||||
enum class AnimationMode {
|
||||
MapToSimulationTime = 0,
|
||||
RealTimeLoopFromStart,
|
||||
RealTimeLoopInfinitely
|
||||
RealTimeLoop
|
||||
};
|
||||
|
||||
AnimationMode _animationMode = AnimationMode::MapToSimulationTime;
|
||||
AnimationMode _animationMode = AnimationMode::RealTimeLoop; // Default is to loop
|
||||
std::filesystem::path _videoFile;
|
||||
double _startJ200Time = 0.0;
|
||||
double _endJ200Time = 0.0;
|
||||
@@ -110,8 +115,6 @@ private:
|
||||
GLuint _fbo = 0;
|
||||
TileTextureInitData::HashKey _frameTextureHashKey;
|
||||
static int _wakeup;
|
||||
double _lastFrameTime = 0.0;
|
||||
double _lastSeek = -1.0;
|
||||
bool _didRender = false;
|
||||
bool _isPaused = false;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user