/* * This file is part of vimix - video live mixer * * **Copyright** (C) 2019-2022 Bruno Herbelin * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . **/ #include #include #include #include "Decorations.h" #include "Visitor.h" #include "BoundingBoxVisitor.h" #include "ImageShader.h" #include "GlmToolkit.h" #include "Resource.h" #include "Log.h" #include "imgui.h" #include Frame::Frame(CornerType corner, BorderType border, ShadowType shadow) : Node(), right_(nullptr), left_(nullptr), top_(nullptr), shadow_(nullptr), square_(nullptr) { static Mesh *shadows[3] = {nullptr}; if (shadows[0] == nullptr) { shadows[0] = new Mesh("mesh/glow.ply", "images/glow.dds"); shadows[1] = new Mesh("mesh/shadow.ply", "images/shadow.dds"); shadows[2] = new Mesh("mesh/shadow_perspective.ply", "images/shadow_perspective.dds"); } static Mesh *frames[9] = {nullptr}; if (frames[0] == nullptr) { frames[0] = new Mesh("mesh/border_round.ply"); frames[1] = new Mesh("mesh/border_round_left.ply"); frames[2] = new Mesh("mesh/border_top.ply"); frames[3] = new Mesh("mesh/border_large_round.ply"); frames[4] = new Mesh("mesh/border_large_round_left.ply"); frames[5] = new Mesh("mesh/border_large_top.ply"); frames[6] = new Mesh("mesh/border_perspective_round.ply"); frames[7] = new Mesh("mesh/border_perspective_round_left.ply"); frames[8] = new Mesh("mesh/border_perspective_top.ply"); } static LineSquare *sharpframethin = new LineSquare( 4.f ); static LineSquare *sharpframelarge = new LineSquare( 6.f ); // Round corners if (corner == ROUND){ if (border == THIN) { right_ = frames[0]; left_ = frames[1]; top_ = frames[2]; } else{ right_ = frames[3]; left_ = frames[4]; top_ = frames[5]; } } // Group corners else if (corner == GROUP){ if (border == THIN) { right_ = frames[6]; left_ = frames[7]; top_ = frames[8]; } else{ right_ = frames[6]; left_ = frames[7]; top_ = frames[8]; } } // Sharp corner else { if (border == LARGE) square_ = sharpframelarge; else square_ = sharpframethin; } switch (shadow) { default: case NONE: break; case GLOW: shadow_ = shadows[0]; break; case DROP: shadow_ = shadows[1]; break; case PERSPECTIVE: shadow_ = shadows[2]; break; } color = glm::vec4( 1.f, 1.f, 1.f, 1.f); } Frame::~Frame() { } void Frame::update( float dt ) { Node::update(dt); if(top_) top_->update(dt); if(right_) right_->update(dt); if(left_) left_->update(dt); if(shadow_) shadow_->update(dt); if(square_) square_->update(dt); } void Frame::draw(glm::mat4 modelview, glm::mat4 projection) { if ( !initialized() ) { if(right_ && !right_->initialized()) right_->init(); if(left_ && !left_->initialized()) left_->init(); if(top_ && !top_->initialized()) top_->init(); if(shadow_ && !shadow_->initialized()) shadow_->init(); if(square_ && !square_->initialized()) square_->init(); init(); } if ( visible_ ) { glm::mat4 ctm = modelview * transform_; // sharp border (scaled) if(square_) { square_->setColor(color); square_->draw( ctm, projection); } // shadow (scaled) if(shadow_){ shadow_->shader()->color.a = 0.98f; shadow_->draw( ctm, projection); } // round top (scaled) if(top_) { top_->shader()->color = color; top_->draw( ctm, projection); } // round sides if(right_) { right_->shader()->color = color; // get scale glm::vec4 scale = ctm * glm::vec4(1.f, 1.0f, 0.f, 0.f); // get rotation glm::vec3 rot(0.f); glm::vec4 vec = ctm * glm::vec4(1.f, 0.f, 0.f, 0.f); rot.z = glm::orientedAngle( glm::vec3(1.f, 0.f, 0.f), glm::normalize(glm::vec3(vec)), glm::vec3(0.f, 0.f, 1.f) ); // right side vec = ctm * glm::vec4(1.f, 0.f, 0.f, 1.f); right_->draw( GlmToolkit::transform(vec, rot, glm::vec3(scale.y, scale.y, 1.f)), projection ); } if(left_) { left_->shader()->color = color; // get scale glm::vec4 scale = ctm * glm::vec4(1.f, 1.0f, 0.f, 0.f); // get rotation glm::vec3 rot(0.f); glm::vec4 vec = ctm * glm::vec4(1.f, 0.f, 0.f, 0.f); rot.z = glm::orientedAngle( glm::vec3(1.f, 0.f, 0.f), glm::normalize(glm::vec3(vec)), glm::vec3(0.f, 0.f, 1.f) ); // right side vec = ctm * glm::vec4(-1.f, 0.f, 0.f, 1.f); left_->draw( GlmToolkit::transform(vec, rot, glm::vec3(scale.y, scale.y, 1.f)), projection ); } } } void Frame::accept(Visitor& v) { Node::accept(v); v.visit(*this); } Handles::Handles(Type type) : Node(), type_(type) { static Mesh *handle_rotation = new Mesh("mesh/border_handles_rotation.ply"); static Mesh *handle_corner = new Mesh("mesh/border_handles_overlay.ply"); static Mesh *handle_scale = new Mesh("mesh/border_handles_scale.ply"); static Mesh *handle_crop = new Mesh("mesh/border_handles_crop.ply"); static Mesh *handle_menu = new Mesh("mesh/border_handles_menu.ply"); static Mesh *handle_lock = new Mesh("mesh/border_handles_lock.ply"); static Mesh *handle_unlock = new Mesh("mesh/border_handles_lock_open.ply"); static Mesh *handle_shadow = new Mesh("mesh/border_handles_shadow.ply", "images/soft_shadow.dds"); if ( type_ == Handles::ROTATE ) { handle_ = handle_rotation; } else if ( type_ == Handles::SCALE ) { handle_ = handle_scale; } else if ( type_ == Handles::MENU ) { handle_ = handle_menu; } else if ( type_ == Handles::CROP ) { handle_ = handle_crop; } else if ( type_ == Handles::LOCKED ) { handle_ = handle_lock; } else if ( type_ == Handles::UNLOCKED ) { handle_ = handle_unlock; } else { handle_ = handle_corner; } color = glm::vec4( 1.f, 1.f, 1.f, 1.f); corner_ = glm::vec2(0.f, 0.f); shadow_ = handle_shadow; } Handles::~Handles() { } void Handles::update( float dt ) { Node::update(dt); handle_->update(dt); } void Handles::draw(glm::mat4 modelview, glm::mat4 projection) { if ( !initialized() ) { if(handle_ && !handle_->initialized()) handle_->init(); if(shadow_ && !shadow_->initialized()) shadow_->init(); init(); } if ( visible_ && handle_) { static Mesh *handle_active = new Mesh("mesh/border_handles_overlay_filled.ply"); // set color handle_->shader()->color = color; handle_active->shader()->color = color; glm::mat4 ctm; glm::vec4 vec; glm::vec3 tra, rot, sca; // get rotation and mirroring from the modelview GlmToolkit::inverse_transform(modelview, tra, rot, sca); glm::vec3 mirror = glm::sign(sca); if ( type_ == Handles::RESIZE ) { // 4 corners vec = modelview * glm::vec4(1.f, -1.f, 0.f, 1.f); ctm = GlmToolkit::transform(vec, rot, glm::vec3(1.f)); handle_->draw( ctm, projection ); vec = modelview * glm::vec4(1.f, 1.f, 0.f, 1.f); ctm = GlmToolkit::transform(vec, rot, glm::vec3(1.f)); handle_->draw( ctm, projection ); vec = modelview * glm::vec4(-1.f, -1.f, 0.f, 1.f); ctm = GlmToolkit::transform(vec, rot, glm::vec3(1.f)); handle_->draw( ctm, projection ); vec = modelview * glm::vec4(-1.f, 1.f, 0.f, 1.f); ctm = GlmToolkit::transform(vec, rot, glm::vec3(1.f)); handle_->draw( ctm, projection ); if ( glm::length(corner_) > 0.f ) { vec = modelview * glm::vec4(corner_.x, corner_.y, 0.f, 1.f); ctm = GlmToolkit::transform(vec, rot, glm::vec3(1.f)); handle_active->draw( ctm, projection ); } } else if ( type_ == Handles::RESIZE_H ){ // left and right vec = modelview * glm::vec4(1.f, 0.f, 0.f, 1.f); ctm = GlmToolkit::transform(vec, rot, glm::vec3(1.f)); handle_->draw( ctm, projection ); vec = modelview * glm::vec4(-1.f, 0.f, 0.f, 1.f); ctm = GlmToolkit::transform(vec, rot, glm::vec3(1.f)); handle_->draw( ctm, projection ); if ( glm::length(corner_) > 0.f ) { vec = modelview * glm::vec4(corner_.x, corner_.y, 0.f, 1.f); ctm = GlmToolkit::transform(vec, rot, glm::vec3(1.f)); handle_active->draw( ctm, projection ); } } else if ( type_ == Handles::RESIZE_V ){ // top and bottom vec = modelview * glm::vec4(0.f, 1.f, 0.f, 1.f); ctm = GlmToolkit::transform(vec, rot, glm::vec3(1.f)); handle_->draw( ctm, projection ); vec = modelview * glm::vec4(0.f, -1.f, 0.f, 1.f); ctm = GlmToolkit::transform(vec, rot, glm::vec3(1.f)); handle_->draw( ctm, projection ); if ( glm::length(corner_) > 0.f ) { vec = modelview * glm::vec4(corner_.x, corner_.y, 0.f, 1.f); ctm = GlmToolkit::transform(vec, rot, glm::vec3(1.f)); handle_active->draw( ctm, projection ); } } else if ( type_ == Handles::ROTATE ){ // one icon in top right corner // 1. Fixed displacement by (0.12,0.12) along the rotation.. ctm = GlmToolkit::transform(glm::vec4(0.f), rot, mirror); glm::vec4 pos = ctm * glm::vec4( 0.12f, 0.12f, 0.f, 1.f); // 2. ..from the top right corner (1,1) vec = ( modelview * glm::vec4(1.f, 1.f, 0.f, 1.f) ) + pos; ctm = GlmToolkit::transform(vec, rot, glm::vec3(1.f)); // 3. draw shadow_->draw( ctm, projection ); handle_->draw( ctm, projection ); } else if ( type_ == Handles::SCALE ){ // one icon in bottom right corner // 1. Fixed displacement by (0.12,0.12) along the rotation.. ctm = GlmToolkit::transform(glm::vec4(0.f), rot, mirror); glm::vec4 pos = ctm * glm::vec4(mirror.x * 0.12f, mirror.x * -0.12f, 0.f, 1.f); // 2. ..from the bottom right corner (1,1) vec = ( modelview * glm::vec4(1.f, -1.f, 0.f, 1.f) ) + pos; ctm = GlmToolkit::transform(vec, rot, mirror); // 3. draw shadow_->draw( ctm, projection ); handle_->draw( ctm, projection ); } else if ( type_ == Handles::CROP ){ // one icon in bottom right corner // 1. Fixed displacement by (0.12,0.12) along the rotation.. ctm = GlmToolkit::transform(glm::vec4(0.f), rot, mirror); glm::vec4 pos = ctm * glm::vec4(mirror.x * 0.12f, mirror.x * 0.12f, 0.f, 1.f); // 2. ..from the bottom right corner (1,-1) vec = ( modelview * glm::vec4(-1.f, -1.f, 0.f, 1.f) ) + pos; ctm = GlmToolkit::transform(vec, rot, mirror); // 3. draw shadow_->draw( ctm, projection ); handle_->draw( ctm, projection ); } else if ( type_ == Handles::MENU ){ // one icon in top left corner // 1. Fixed displacement by (-0.12,0.12) along the rotation.. ctm = GlmToolkit::transform(glm::vec4(0.f), rot, mirror); glm::vec4 pos = ctm * glm::vec4( -0.12f, 0.12f, 0.f, 1.f); // 2. ..from the top right corner (1,1) vec = ( modelview * glm::vec4(-1.f, 1.f, 0.f, 1.f) ) + pos; ctm = GlmToolkit::transform(vec, rot, mirror); // 3. draw shadow_->draw( ctm, projection ); handle_->draw( ctm, projection ); } else if ( type_ == Handles::LOCKED || type_ == Handles::UNLOCKED ){ // one icon in top left corner // 1. Fixed displacement by (-0.12,0.12) along the rotation.. ctm = GlmToolkit::transform(glm::vec4(0.f), rot, mirror); glm::vec4 pos = ctm * glm::vec4( -0.12f, 0.12f, 0.f, 1.f); // 2. ..from the bottom right corner (1,-1) vec = ( modelview * glm::vec4(1.f, -1.f, 0.f, 1.f) ) + pos; ctm = GlmToolkit::transform(vec, rot, mirror); // 3. draw shadow_->draw( ctm, projection ); handle_->draw( ctm, projection ); } } } void Handles::accept(Visitor& v) { Node::accept(v); v.visit(*this); } Symbol::Symbol(Type t, glm::vec3 pos) : Node(), type_(t) { static Mesh *shadow= new Mesh("mesh/border_handles_shadow.ply", "images/soft_shadow.dds"); static Mesh *shadows[(int)EMPTY+1] = {nullptr}; static Mesh *icons[(int)EMPTY+1] = {nullptr}; if (icons[0] == nullptr) { icons[CIRCLE_POINT] = new Mesh("mesh/point.ply"); shadows[CIRCLE_POINT] = nullptr; icons[SQUARE_POINT] = new Mesh("mesh/square_point.ply"); shadows[SQUARE_POINT] = nullptr; icons[IMAGE] = new Mesh("mesh/icon_image.ply"); shadows[IMAGE] = shadow; icons[SEQUENCE] = new Mesh("mesh/icon_sequence.ply"); shadows[SEQUENCE]= shadow; icons[VIDEO] = new Mesh("mesh/icon_video.ply"); shadows[VIDEO] = shadow; icons[SESSION] = new Mesh("mesh/icon_vimix.ply"); shadows[SESSION]= shadow; icons[CLONE] = new Mesh("mesh/icon_clone.ply"); shadows[CLONE] = shadow; icons[RENDER] = new Mesh("mesh/icon_render.ply"); shadows[RENDER] = shadow; icons[GROUP] = new Mesh("mesh/icon_group_vimix.ply"); shadows[GROUP] = shadow; icons[PATTERN] = new Mesh("mesh/icon_gear.ply"); shadows[PATTERN]= shadow; icons[CAMERA] = new Mesh("mesh/icon_camera.ply"); shadows[CAMERA] = shadow; icons[CUBE] = new Mesh("mesh/icon_cube.ply"); shadows[CUBE] = shadow; icons[SHARE] = new Mesh("mesh/icon_share.ply"); shadows[SHARE] = shadow; icons[RECEIVE] = new Mesh("mesh/icon_receive.ply"); shadows[RECEIVE]= shadow; icons[DOTS] = new Mesh("mesh/icon_dots.ply"); shadows[DOTS] = nullptr; icons[BUSY] = new Mesh("mesh/icon_circles.ply"); shadows[BUSY] = nullptr; icons[LOCK] = new Mesh("mesh/icon_lock.ply"); shadows[LOCK] = shadow; icons[UNLOCK] = new Mesh("mesh/icon_unlock.ply"); shadows[UNLOCK] = shadow; icons[EYE] = new Mesh("mesh/icon_eye.ply"); shadows[EYE] = shadow; icons[EYESLASH] = new Mesh("mesh/icon_eye_slash.ply"); shadows[EYESLASH] = shadow; icons[VECTORSLASH] = new Mesh("mesh/icon_vector_square_slash.ply"); shadows[VECTORSLASH] = shadow; icons[ARROWS] = new Mesh("mesh/icon_rightarrow.ply"); shadows[ARROWS] = shadow; icons[ROTATION] = new Mesh("mesh/border_handles_rotation.ply"); shadows[ROTATION] = shadow; icons[CIRCLE] = new Mesh("mesh/icon_circle.ply"); shadows[CIRCLE] = nullptr; icons[CLOCK] = new Mesh("mesh/icon_clock.ply"); shadows[CLOCK] = nullptr; icons[CLOCK_H] = new Mesh("mesh/icon_clock_hand.ply"); shadows[CLOCK_H]= nullptr; icons[SQUARE] = new Mesh("mesh/icon_square.ply"); shadows[SQUARE] = nullptr; icons[CROSS] = new Mesh("mesh/icon_cross.ply"); shadows[CROSS] = nullptr; icons[GRID] = new Mesh("mesh/icon_grid.ply"); shadows[GRID] = nullptr; icons[EMPTY] = new Mesh("mesh/icon_empty.ply"); shadows[EMPTY] = shadow; } symbol_ = icons[type_]; shadow_ = shadows[type_]; translation_ = pos; color = glm::vec4( 1.f, 1.f, 1.f, 1.f); } Symbol::~Symbol() { } void Symbol::draw(glm::mat4 modelview, glm::mat4 projection) { if ( !initialized() ) { if(symbol_ && !symbol_->initialized()) symbol_->init(); if(shadow_ && !shadow_->initialized()) shadow_->init(); init(); } if ( visible_ && symbol_) { // set color symbol_->shader()->color = color; // rebuild a matrix with rotation (see handles) and translation from modelview + translation_ // and define scale to be 1, 1 glm::mat4 ctm; glm::vec3 rot(0.f); glm::vec4 vec = modelview * glm::vec4(1.f, 0.f, 0.f, 0.f); rot.z = glm::orientedAngle( glm::vec3(1.f, 0.f, 0.f), glm::normalize(glm::vec3(vec)), glm::vec3(0.f, 0.f, 1.f) ); // extract scaling ctm = glm::rotate(glm::identity(), -rot.z, glm::vec3(0.f, 0.f, 1.f)) * modelview ; vec = ctm * glm::vec4(1.f, 1.f, 0.f, 0.f); glm::vec3 sca = glm::vec3(vec.y , vec.y, 1.f) * glm::vec3(scale_.y, scale_.y, 1.f); // extract translation glm::vec3 tran = glm::vec3(modelview[3][0], modelview[3][1], modelview[3][2]) ; tran += translation_ * glm::vec3(vec); // apply local rotation rot.z += rotation_.z; // generate matrix ctm = GlmToolkit::transform(tran, rot, sca); if (shadow_) shadow_->draw( ctm, projection ); symbol_->draw( ctm, projection); } } void Symbol::accept(Visitor& v) { Node::accept(v); v.visit(*this); } Mesh *Disk::disk_ = nullptr; Disk::Disk() : Node() { if (Disk::disk_ == nullptr) Disk::disk_ = new Mesh("mesh/disk.ply"); color = glm::vec4( 1.f, 1.f, 1.f, 1.f); } void Disk::draw(glm::mat4 modelview, glm::mat4 projection) { if ( !initialized() ) { if (!Disk::disk_->initialized()) Disk::disk_->init(); init(); } if ( visible_ ) { // set color Disk::disk_->shader()->color = color; glm::mat4 ctm = modelview * transform_; Disk::disk_->draw( ctm, projection); } } void Disk::accept(Visitor& v) { Node::accept(v); v.visit(*this); } Surface *Glyph::font_ = nullptr; Glyph::Glyph(int imgui_font_index) : Node(), character_(' '), font_index_(imgui_font_index), baseline_(0.f), shape_(1.f, 1.f) { if (Glyph::font_ == nullptr) Glyph::font_ = new Surface; uvTransform_ = glm::identity(); color = glm::vec4( 1.f, 1.f, 1.f, 1.f); } void Glyph::draw(glm::mat4 modelview, glm::mat4 projection) { if ( !initialized() ) { if (!Glyph::font_->initialized()) { uint tex = (uint)(intptr_t)ImGui::GetIO().Fonts->TexID; if ( tex > 0) { Glyph::font_->init(); font_->setTextureIndex(tex); } } if (Glyph::font_->initialized()) { init(); setChar(character_); } } if ( visible_ ) { // set color Glyph::font_->shader()->color = color; // modify the shader iTransform to select UVs of the desired glyph Glyph::font_->shader()->iTransform = uvTransform_; // rebuild a matrix with rotation (see handles) and translation from modelview + translation_ // and define scale to be 1, 1 glm::mat4 ctm; glm::vec3 rot(0.f); glm::vec4 vec = modelview * glm::vec4(1.f, 0.f, 0.f, 0.f); rot.z = glm::orientedAngle( glm::vec3(1.f, 0.f, 0.f), glm::normalize(glm::vec3(vec)), glm::vec3(0.f, 0.f, 1.f) ); // extract scaling ctm = glm::rotate(glm::identity(), -rot.z, glm::vec3(0.f, 0.f, 1.f)) * modelview ; vec = ctm * glm::vec4(1.f, 1.f, 0.f, 0.f); glm::vec2 sca = glm::vec2(vec.y) * glm::vec2(scale_.y) * shape_; // extract translation glm::vec3 tran = glm::vec3(modelview[3][0], modelview[3][1], modelview[3][2]) ; tran += (translation_ + glm::vec3(0.f, baseline_ * scale_.y, 0.f) ) * glm::vec3(vec); // apply local rotation rot.z += rotation_.z; // generate matrix ctm = GlmToolkit::transform(tran, rot, glm::vec3(sca, 1.f)); Glyph::font_->draw( ctm, projection); } } void Glyph::setChar(char c) { character_ = c; if (initialized()) { // get from imgui the UVs of the given char const ImGuiIO& io = ImGui::GetIO(); ImFont* myfont = io.Fonts->Fonts[0]; if (font_index_ > 0 && font_index_ < io.Fonts->Fonts.size() ) myfont = io.Fonts->Fonts[font_index_]; const ImFontGlyph* glyph = myfont->FindGlyph(character_); if (glyph) { // create a texture UV transform to get the UV coordinates of the glyph const glm::vec3 uv_t = glm::vec3( glyph->U0, glyph->V0, 0.f); const glm::vec3 uv_s = glm::vec3( glyph->U1 - glyph->U0, glyph->V1 - glyph->V0, 1.f); const glm::vec3 uv_r = glm::vec3(0.f, 0.f, 0.f); uvTransform_ = GlmToolkit::transform(uv_t, uv_r, uv_s); // remember glyph shape shape_ = glm::vec2(glyph->X1 - glyph->X0, glyph->Y1 - glyph->Y0) / myfont->FontSize; baseline_ = -glyph->Y0 / myfont->FontSize; } } } Mesh *DotLine::dot_ = nullptr; Mesh *DotLine::arrow_ = nullptr; DotLine::DotLine() : Node() { if (DotLine::dot_ == nullptr) DotLine::dot_ = new Mesh("mesh/point.ply"); if (DotLine::arrow_ == nullptr) DotLine::arrow_ = new Mesh("mesh/triangle_point.ply"); spacing = 0.3f; target = glm::vec3( 0.f, 0.f, 0.f); color = glm::vec4( 1.f, 1.f, 1.f, 1.f); } void DotLine::draw(glm::mat4 modelview, glm::mat4 projection) { if ( !initialized() ) { if (!DotLine::dot_->initialized()) DotLine::dot_->init(); if (!DotLine::arrow_->initialized()) DotLine::arrow_->init(); init(); } if ( visible_ ) { // set color DotLine::dot_->shader()->color = color; DotLine::arrow_->shader()->color = color; glm::mat4 ctm = modelview; float angle = glm::orientedAngle( glm::normalize(glm::vec2(0.f,-1.f)), glm::normalize(glm::vec2(target))); glm::mat4 R = glm::rotate(glm::identity(), angle, glm::vec3(0.f, 0.f, 1.f) ); R *= glm::scale(glm::identity(), glm::vec3(1.0f, 1.5f, 1.f)); // draw start point DotLine::dot_->draw( ctm, projection); // draw equally spaced intermediate points glm::vec3 inc = target; glm::vec3 space = spacing * glm::normalize(target); while ( glm::length(inc) > spacing ) { inc -= space; ctm *= glm::translate(glm::identity(), space); DotLine::arrow_->draw( ctm * R, projection); } // draw target point ctm = modelview * glm::translate(glm::identity(), target); DotLine::dot_->draw( ctm, projection); } } void DotLine::accept(Visitor& v) { Node::accept(v); v.visit(*this); }