diff --git a/CloneSource.cpp b/CloneSource.cpp index 963aafa..eae8d2a 100644 --- a/CloneSource.cpp +++ b/CloneSource.cpp @@ -17,38 +17,53 @@ * along with this program. If not, see . **/ +#include + #include +#include #include "Log.h" #include "Resource.h" #include "Visitor.h" +#include "FrameBuffer.h" #include "Decorations.h" #include "CloneSource.h" -CloneSource *Source::clone(uint64_t id) -{ - CloneSource *s = new CloneSource(this, id); - - clones_.push_back(s); - - return s; -} - - -CloneSource::CloneSource(Source *origin, uint64_t id) : Source(id), origin_(origin) + +CloneSource::CloneSource(Source *origin, uint64_t id) : Source(id), origin_(origin), cloningsurface_(nullptr), + read_index_(0), write_index_(0), delay_(0.0), paused_(false), image_mode_(CLONE_TEXTURE) { + // initial name copies the origin name: diplucates are namanged in session name_ = origin->name(); + // set symbol symbol_ = new Symbol(Symbol::CLONE, glm::vec3(0.75f, 0.75f, 0.01f)); symbol_->scale_.y = 1.5f; + + // init array + stack_.fill(nullptr); + timestamp_.fill(0.0); + + timer_ = g_timer_new (); } CloneSource::~CloneSource() { if (origin_) origin_->clones_.remove(this); + + // delete all frame buffers + for (size_t i = 0; i < stack_.size(); ++i){ + if ( stack_[i] != nullptr ) + delete stack_[i]; + } + + if (cloningsurface_) + delete cloningsurface_; + + g_free(timer_); } CloneSource *CloneSource::clone(uint64_t id) @@ -64,15 +79,29 @@ void CloneSource::init() { if (origin_ && origin_->mode_ > Source::UNINITIALIZED) { - // get the texture index from framebuffer of view, apply it to the surface - texturesurface_->setTextureIndex( origin_->texture() ); + // create frame buffers where to copy frames of the origin source + glm::vec3 res = origin_->frame()->resolution(); + for (size_t i = 0; i < stack_.size(); ++i){ + stack_[i] = new FrameBuffer( res, origin_->frame()->use_alpha() ); + } - // create Frame buffer matching size of session - FrameBuffer *renderbuffer = new FrameBuffer( origin_->frame()->resolution(), true); + g_timer_start(timer_); + + cloningsurface_ = new Surface; + cloningsurface_->setTextureIndex( origin()->texture() ); + + // set initial texture surface + texturesurface_->setTextureIndex( stack_[read_index_]->texture() ); + + // create Frame buffer matching size of images + FrameBuffer *renderbuffer = new FrameBuffer( res, true); // set the renderbuffer of the source and attach rendering nodes attach(renderbuffer); + // force update of activation mode + active_ = true; + // deep update to reorder ++View::need_deep_update_; @@ -83,6 +112,9 @@ void CloneSource::init() void CloneSource::setActive (bool on) { + // request update + need_update_ |= active_ != on; + active_ = on; groups_[View::RENDERING]->visible_ = active_; @@ -98,17 +130,90 @@ void CloneSource::setActive (bool on) if (active_) activesurface_->setTextureIndex(Resource::getTextureTransparent()); else - activesurface_->setTextureIndex(origin_->texture()); + activesurface_->setTextureIndex(stack_[read_index_]->texture()); } } } +void CloneSource::replay() +{ + read_index_ = (write_index_ + 1 )%(stack_.size()); +} + +void CloneSource::update(float dt) +{ + if (active_ && !paused_ && cloningsurface_ != nullptr) { + + double now = g_timer_elapsed (timer_, NULL) ; + + // increment enplacement of write index + write_index_ = (write_index_+1)%(stack_.size()); + timestamp_[write_index_] = now; + + // CLONE_RENDER : blit rendered framebuffer in the stack + if (image_mode_ == CLONE_RENDER) + origin_->frame()->blit(stack_[write_index_]); + // CLONE_TEXTURE : render origin texture in the stack + else { + stack_[write_index_]->begin(); + cloningsurface_->draw(glm::identity(), stack_[write_index_]->projection()); + stack_[write_index_]->end(); + } + + // define emplacement of read index + if (delay_ < 0.001) + // minimal difference if no delay + read_index_ = write_index_; + else + { + // starting where we are at, get the next index that satisfies the delay + size_t previous_index = read_index_; + while ( now - timestamp_[read_index_] > delay_) { + // usually, one frame increment suffice + read_index_ = (read_index_ + 1 )%(stack_.size()); + // break the loop if running infinite (never happens) + if (previous_index == read_index_) + break; + } + } + + // update the source surface to be rendered + texturesurface_->setTextureIndex( stack_[read_index_]->texture() ); + + } + + Source::update(dt); +} + + +void CloneSource::setDelay(double second) +{ + delay_ = CLAMP(second, 0.0, 1.0); +} + +void CloneSource::play (bool on) +{ + // if a different state is asked + if (paused_ == on) { + + // restart clean if was paused + if (paused_) { + g_timer_reset(timer_); + timestamp_.fill(0.0); + write_index_ = 0; + read_index_ = 1; + } + // toggle state + paused_ = !on; + } +} + uint CloneSource::texture() const { - if (origin_ != nullptr) - return origin_->texture(); + if (cloningsurface_ != nullptr) + return stack_[read_index_]->texture(); else - return Resource::getTextureBlack(); + return Resource::getTextureTransparent(); } void CloneSource::accept(Visitor& v) diff --git a/CloneSource.h b/CloneSource.h index 4ee4c8a..0e6e7a2 100644 --- a/CloneSource.h +++ b/CloneSource.h @@ -1,6 +1,10 @@ #ifndef CLONESOURCE_H #define CLONESOURCE_H +#include + +#define DELAY_ARRAY_SIZE 70 + #include "Source.h" class CloneSource : public Source @@ -11,11 +15,12 @@ public: ~CloneSource(); // implementation of source API + void update (float dt) override; void setActive (bool on) override; - bool playing () const override { return true; } - void play (bool) override {} - bool playable () const override { return false; } - void replay () override {} + bool playing () const override { return !paused_; } + void play (bool on) override; + bool playable () const override { return true; } + void replay () override; uint texture() const override; bool failed() const override { return origin_ == nullptr; } void accept (Visitor& v) override; @@ -24,6 +29,17 @@ public: inline void detach() { origin_ = nullptr; } inline Source *origin() const { return origin_; } + typedef enum { + CLONE_TEXTURE = 0, + CLONE_RENDER + } CloneImageMode; + + void setImageMode(CloneImageMode m) { image_mode_ = m; } + CloneImageMode imageMode() const { return image_mode_; } + + void setDelay(double second); + double delay() const { return delay_; } + glm::ivec2 icon() const override; std::string info() const override; @@ -33,6 +49,19 @@ protected: void init() override; Source *origin_; + + // cloning + std::array stack_; + Surface *cloningsurface_; + size_t read_index_, write_index_; + + GTimer *timer_; + std::array timestamp_; + double delay_; + + // control + bool paused_; + CloneImageMode image_mode_; }; diff --git a/ImGuiVisitor.cpp b/ImGuiVisitor.cpp index 4c2ca27..df87fc6 100644 --- a/ImGuiVisitor.cpp +++ b/ImGuiVisitor.cpp @@ -714,6 +714,20 @@ void ImGuiVisitor::visit (CloneSource& s) Mixer::manager().setCurrentSource(s.origin()); ImGui::SameLine(); ImGui::Text("Source"); + + ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN); + int m = (int) s.imageMode(); + if (ImGui::Combo("Image", &m, "Original\0Post-processed\0") ) + s.setImageMode((CloneSource::CloneImageMode)m); + + + ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN); + float d = s.delay(); +// if (ImGui::SliderFloat("Delay", &d, 0.f, 1.f, "%.2f s")){ + if (ImGui::SliderFloat("Delay", &d, 0.f, 1.f, d < 0.01f ? "None" : "%.2f s")){ + s.setDelay(d); + } + } void ImGuiVisitor::visit (PatternSource& s) diff --git a/InfoVisitor.cpp b/InfoVisitor.cpp index 02139e6..6b030b0 100644 --- a/InfoVisitor.cpp +++ b/InfoVisitor.cpp @@ -178,10 +178,18 @@ void InfoVisitor::visit (RenderSource& s) void InfoVisitor::visit (CloneSource& s) { - if (current_id_ == s.id()) + if (current_id_ == s.id() || s.origin() == nullptr) return; - information_ = "Clone of " + s.origin()->name(); + std::ostringstream oss; + oss << "Clone of '" << s.origin()->name() << "' " << std::endl; + + if (s.frame()){ + oss << s.frame()->width() << " x " << s.frame()->height() << ", "; + oss << "RGBA"; + } + + information_ = oss.str(); current_id_ = s.id(); } diff --git a/SessionCreator.cpp b/SessionCreator.cpp index a89fd64..87ec47a 100644 --- a/SessionCreator.cpp +++ b/SessionCreator.cpp @@ -407,7 +407,7 @@ void SessionLoader::load(XMLElement *sessionNode) // found the orign source if (origin != session_->end()) { // create a new source of type Clone - Source *clone_source = (*origin)->clone(id_xml_); + CloneSource *clone_source = (*origin)->clone(id_xml_); // add source to session session_->addSource(clone_source); @@ -1082,3 +1082,16 @@ void SessionLoader::visit (GenericStreamSource& s) } } + +void SessionLoader::visit (CloneSource& s) +{ + // set attributes + int imagemode = 0; + xmlCurrent_->QueryIntAttribute("imageMode", &imagemode); + s.setImageMode((CloneSource::CloneImageMode)imagemode); + + double delay = 0.0; + xmlCurrent_->QueryDoubleAttribute("delay", &delay); + s.setDelay(delay); +} + diff --git a/SessionCreator.h b/SessionCreator.h index f2cb582..e223e49 100644 --- a/SessionCreator.h +++ b/SessionCreator.h @@ -55,6 +55,7 @@ public: void visit (SessionFileSource& s) override; void visit (SessionGroupSource& s) override; void visit (RenderSource& s) override; + void visit (CloneSource& s) override; void visit (PatternSource& s) override; void visit (DeviceSource& s) override; void visit (NetworkSource& s) override; diff --git a/SessionVisitor.cpp b/SessionVisitor.cpp index 3eab5c2..10a309c 100644 --- a/SessionVisitor.cpp +++ b/SessionVisitor.cpp @@ -634,12 +634,16 @@ void SessionVisitor::visit (RenderSource&) void SessionVisitor::visit (CloneSource& s) { xmlCurrent_->SetAttribute("type", "CloneSource"); + xmlCurrent_->SetAttribute("imageMode", (int) s.imageMode()); + xmlCurrent_->SetAttribute("delay", (double) s.delay()); XMLElement *origin = xmlDoc_->NewElement("origin"); origin->SetAttribute("id", s.origin()->id()); + xmlCurrent_->InsertEndChild(origin); XMLText *text = xmlDoc_->NewText( s.origin()->name().c_str() ); origin->InsertEndChild( text ); + } void SessionVisitor::visit (PatternSource& s) diff --git a/Source.cpp b/Source.cpp index 0cfc40c..d9790d9 100644 --- a/Source.cpp +++ b/Source.cpp @@ -649,6 +649,17 @@ void Source::call(SourceCallback *callback, bool override) } } + +CloneSource *Source::clone(uint64_t id) +{ + CloneSource *s = new CloneSource(this, id); + + clones_.push_back(s); + + return s; +} + + void Source::update(float dt) { // keep delta-t