From 92e1b315a32da123b2f8d7bb633375033a10c66d Mon Sep 17 00:00:00 2001 From: John Tsiombikas Date: Sat, 20 Jan 2018 13:40:41 +0200 Subject: [PATCH] new render target class while working on the exhibit UI --- src/app.cc | 22 +++- src/post.cc | 12 +-- src/rtarg.cc | 289 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/rtarg.h | 63 ++++++++++++ src/texture.cc | 11 ++ src/texture.h | 1 + src/ui_exhibit.cc | 135 +++++++++++++++++++++++-- src/ui_exhibit.h | 3 + 8 files changed, 512 insertions(+), 24 deletions(-) create mode 100644 src/rtarg.cc create mode 100644 src/rtarg.h diff --git a/src/app.cc b/src/app.cc index 620ec52..7fc5c13 100644 --- a/src/app.cc +++ b/src/app.cc @@ -21,6 +21,7 @@ #include "blob_exhibit.h" #include "dbg_gui.h" #include "geomdraw.h" +#include "ui_exhibit.h" #define NEAR_CLIP 5.0 #define FAR_CLIP 10000.0 @@ -170,6 +171,11 @@ bool app_init(int argc, char **argv) //return false; } */ + if(!exui_init()) { + error_log("failed to initialize exhibit ui system\n"); + return false; + } + exui_setnode(&exslot_left.node); if(!(sdr_ltmap_notex = create_program_load("sdr/lightmap.v.glsl", "sdr/lightmap-notex.p.glsl"))) { return false; @@ -217,6 +223,8 @@ void app_cleanup() delete rend; + exui_shutdown(); + /* this must be destroyed before the scene graph to detach exhibit nodes * before the scene tries to delete them recursively */ @@ -253,6 +261,7 @@ static void update(float dt) mscn->update(dt); exman->update(dt); + exui_update(dt); float speed = walk_speed * dt; Vec3 dir; @@ -351,13 +360,18 @@ static void update(float dt) exsel_hover = exman->select(ray); } - if(!exslot_left.empty()) exslot_left.node.update(dt); - if(!exslot_right.empty()) exslot_right.node.update(dt); - // update hand-tracking if(have_handtracking) { update_vrhands(&avatar); + } else { + // set the position of the left hand at a suitable position for the exhibit UI + Vec3 dir = transpose(mouse_view_matrix.upper3x3()) * Vec3(0, 0, -1); + exslot_left.node.set_position(avatar.pos + dir * 30); // magic: distance in front } + + if(!exslot_right.empty()) exslot_right.node.update(dt); + // always update the left slot, because it's the anchor point of the exhibit ui + exslot_left.node.update(dt); } void app_display() @@ -498,6 +512,8 @@ static void draw_scene() glPopAttrib(); } + exui_draw(); + print_text(Vec2(9 * win_width / 10, 20), Vec3(1, 1, 0), "fps: %.1f", framerate); draw_ui(); } diff --git a/src/post.cc b/src/post.cc index b675f5b..885a212 100644 --- a/src/post.cc +++ b/src/post.cc @@ -1,5 +1,6 @@ #include "opengl.h" #include "app.h" +#include "texture.h" // next_pow2 static void update_fbtex(); @@ -36,17 +37,6 @@ void slow_post(unsigned int sdr) glPopAttrib(); } -static int next_pow2(int x) -{ - x--; - x = (x >> 1) | x; - x = (x >> 2) | x; - x = (x >> 4) | x; - x = (x >> 8) | x; - x = (x >> 16) | x; - return x + 1; -} - static void update_fbtex() { if(win_width <= fb_tex_width && win_height <= fb_tex_height) { diff --git a/src/rtarg.cc b/src/rtarg.cc new file mode 100644 index 0000000..f90c1d3 --- /dev/null +++ b/src/rtarg.cc @@ -0,0 +1,289 @@ +#include "rtarg.h" + +struct RTStackItem { + RenderTarget *rt; + int saved_vp[4]; +}; + +static void attach_depth_texture(Texture *tex); + +#define MAX_STACK_SIZE 16 +static RTStackItem rstack[MAX_STACK_SIZE]; +static int rtop; + +void set_render_target(RenderTarget *rt) +{ + if(rt) { + if(!rstack[rtop].rt) { + glGetIntegerv(GL_VIEWPORT, rstack[rtop].saved_vp); + } + rt->bind(); + + } else { + int *vp = rstack[rtop].saved_vp; + glViewport(vp[0], vp[1], vp[2], vp[3]); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } + + rstack[rtop].rt = rt; +} + +RenderTarget *current_render_target() +{ + return rstack[rtop].rt; +} + +bool push_render_target(RenderTarget *rt) +{ + if(!rt) { + error_log("push_render_target(0) is invalid\n"); + return false; + } + if(rtop >= MAX_STACK_SIZE - 1) { + warning_log("push_render_target: overflow\n"); + return false; + } + int *vp = rstack[rtop + 1].saved_vp; + RenderTarget *prev = current_render_target(); + + if(prev) { + vp[0] = vp[1] = 0; + vp[2] = prev->get_width(); + vp[3] = prev->get_height(); + } else { + memcpy(vp, rstack[rtop].saved_vp, 4 * sizeof(int)); + } + rstack[++rtop].rt = rt; + return true; +} + +bool pop_render_target() +{ + if(rtop <= 0) { + error_log("pop_render_target: undeflow\n"); + return false; + } + + int *vp = rstack[rtop].saved_vp; + glViewport(vp[0], vp[1], vp[2], vp[3]); + + if(rstack[--rtop].rt) { + rstack[rtop].rt->bind(); + } else { + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } + return true; +} + +RenderTarget::RenderTarget() +{ + fbo = 0; + rbdepth = 0; + width = height = tex_width = tex_height = 0; + auto_vport = true; + rtcount = 0; + + for(int i=0; i<4; i++) { + tex[i] = 0; + own_texture[i] = true; + } +} + +RenderTarget::~RenderTarget() +{ + destroy(); +} + +bool RenderTarget::create(int xsz, int ysz, unsigned int fmt, unsigned int flags) +{ + return create_mrt(xsz, ysz, 1, fmt, flags); +} + +bool RenderTarget::create_mrt(int xsz, int ysz, int num, unsigned int fmt, unsigned int flags) +{ + glGenFramebuffers(1, &fbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + width = xsz; + height = ysz; + tex_width = next_pow2(xsz); + tex_height = next_pow2(ysz); + rtcount = num; + + texmat.scaling((float)width / (float)tex_width, (float)height / (float)tex_height, 1); + + if(flags & RT_COLOR) { + for(int i=0; icreate(tex_width, tex_height, TEX_2D, fmt); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, + tex[i]->get_id(), 0); + own_texture[i] = true; + } + } else { + // in case set_texture was called before create + for(int i=0; iget_id(), 0); + } + } + } + + if(flags & (RT_DEPTH | RT_STENCIL)) { + unsigned int fmt, attachment; + + glGenRenderbuffers(1, &rbdepth); + glBindRenderbuffer(GL_RENDERBUFFER, rbdepth); + + switch(flags & (RT_DEPTH | RT_STENCIL)) { + case RT_DEPTH: + fmt = GL_DEPTH_COMPONENT24; + attachment = GL_DEPTH_ATTACHMENT; + break; + + case RT_STENCIL: + fmt = GL_STENCIL_INDEX8; + attachment = GL_STENCIL_ATTACHMENT; + break; + + case RT_DEPTH | RT_STENCIL: + fmt = GL_DEPTH24_STENCIL8; + attachment = GL_DEPTH_STENCIL_ATTACHMENT; + break; + } + + glRenderbufferStorage(GL_RENDERBUFFER, fmt, width, height); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, rbdepth); + + } else if(depth) { + attach_depth_texture(depth); + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + return true; +} + +void RenderTarget::destroy() +{ + for(int i=0; i<4; i++) { + if(tex[i] && own_texture[i]) { + delete tex[i]; + tex[i] = 0; + } + } + if(rbdepth) { + glDeleteRenderbuffers(1, &rbdepth); + rbdepth = 0; + } + if(fbo) { + glDeleteFramebuffers(1, &fbo); + fbo = 0; + } +} + +int RenderTarget::get_width() const +{ + return width; +} + +int RenderTarget::get_height() const +{ + return height; +} + +void RenderTarget::set_texture(Texture *tex, int idx) +{ + if(this->tex[idx] && own_texture[idx]) { + delete this->tex[idx]; + } + this->tex[idx] = tex; + own_texture[idx] = false; + + if(fbo) { + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + idx, GL_TEXTURE_2D, + tex->get_id(), 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } +} + +void RenderTarget::set_depth_texture(Texture *tex) +{ + depth = tex; + + if(fbo) { + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + attach_depth_texture(tex); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } +} + +Texture *RenderTarget::texture(int idx) const +{ + return tex[idx]; +} + +Texture *RenderTarget::depth_texture() const +{ + return depth; +} + +void RenderTarget::set_auto_viewport(bool enable) +{ + auto_vport = enable; +} + +bool RenderTarget::auto_viewport() const +{ + return auto_vport; +} + +void RenderTarget::bind() const +{ + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + if(auto_vport) { + glViewport(0, 0, width, height); + } +} + +const Mat4 &RenderTarget::texture_matrix() const +{ + return texmat; +} + +static void attach_depth_texture(Texture *tex) +{ + unsigned int fmt = tex->get_format(); + unsigned int attachment = 0; + + switch(fmt) { + case GL_DEPTH_COMPONENT: + case GL_DEPTH_COMPONENT16: + case GL_DEPTH_COMPONENT24: + case GL_DEPTH_COMPONENT32: + attachment = GL_DEPTH_ATTACHMENT; + break; + + case GL_STENCIL_INDEX: + case GL_STENCIL_INDEX1: + case GL_STENCIL_INDEX4: + case GL_STENCIL_INDEX8: + attachment = GL_STENCIL_ATTACHMENT; + break; + + case GL_DEPTH_STENCIL: + case GL_DEPTH24_STENCIL8: + case GL_DEPTH32F_STENCIL8: + case GL_UNSIGNED_INT_24_8: + attachment = GL_DEPTH_STENCIL_ATTACHMENT; + break; + + default: + error_log("failed to attach depth/stencil texture: unexpected texture format: %x\n", fmt); + break; + } + + glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, tex->get_id(), 0); +} diff --git a/src/rtarg.h b/src/rtarg.h new file mode 100644 index 0000000..f77682d --- /dev/null +++ b/src/rtarg.h @@ -0,0 +1,63 @@ +#ifndef RTARG_H_ +#define RTARG_H_ + +#include +#include "opengl.h" +#include "texture.h" + +// flags for RenderTarget::create +enum { + RT_COLOR = 1, + RT_DEPTH = 2, + RT_STENCIL = 4 +}; + +class RenderTarget { +private: + int width, height; + int tex_width, tex_height; + + unsigned int fbo; + Texture *tex[4]; + int rtcount; + // either the depth texture or a dummy depth render target is used + Texture *depth; + unsigned int rbdepth; + Mat4 texmat; // texture matrix to map tex coords from [0,1] to the useful area + + bool own_texture[4]; + bool auto_vport; + +public: + RenderTarget(); + ~RenderTarget(); + + bool create(int xsz, int ysz, unsigned int fmt = GL_SRGB_ALPHA, + unsigned int flags = RT_COLOR | RT_DEPTH); + bool create_mrt(int xsz, int ysz, int num, unsigned int fmt = GL_RGB16F, + unsigned int flags = RT_COLOR | RT_DEPTH); + void destroy(); + + int get_width() const; + int get_height() const; + + void set_texture(Texture *tex, int idx = 0); + void set_depth_texture(Texture *tex); + + Texture *texture(int idx = 0) const; + Texture *depth_texture() const; + + void set_auto_viewport(bool enable); + bool auto_viewport() const; + + void bind() const; + const Mat4 &texture_matrix() const; +}; + +void set_render_target(RenderTarget *rt); +RenderTarget *current_render_target(); + +bool push_render_target(RenderTarget *rt); +bool pop_render_target(); + +#endif // RTARG_H_ diff --git a/src/texture.cc b/src/texture.cc index f4f7cfe..ed34798 100644 --- a/src/texture.cc +++ b/src/texture.cc @@ -43,6 +43,17 @@ void bind_texture(Texture *tex, int tunit) } } +int next_pow2(int x) +{ + x--; + x = (x >> 1) | x; + x = (x >> 2) | x; + x = (x >> 4) | x; + x = (x >> 8) | x; + x = (x >> 16) | x; + return x + 1; +} + Image *Texture::default_img; diff --git a/src/texture.h b/src/texture.h index 5c4bf3c..41749f8 100644 --- a/src/texture.h +++ b/src/texture.h @@ -60,6 +60,7 @@ public: }; void bind_texture(Texture *tex, int tunit = 0); +int next_pow2(int x); class TextureSet : public DataSet { private: diff --git a/src/ui_exhibit.cc b/src/ui_exhibit.cc index 355d078..eda39c6 100644 --- a/src/ui_exhibit.cc +++ b/src/ui_exhibit.cc @@ -1,26 +1,43 @@ +#include #include "ui_exhibit.h" #include "ui.h" #include "app.h" -#include +#include "snode.h" +#include "shader.h" -static void draw_titlebar(); -static void draw_tabs(); +struct Rect { + float x, y, w, h; +}; + +static void draw_frame(const Rect &rect); +static void draw_titlebar(const Rect &rect); +static void draw_tabs(const Rect &rect); +static void draw_text(const Rect &rect); static void layout_text(const char *text); +static const SceneNode *parent; static Vec3 pos; static Vec2 size; static float text_padding; +static float text_scale = 0.05f; static Exhibit *ex; static int vis_tab; static float scroll; static std::vector text_lines; static AudioStream *voice; +enum {COL_BG, COL_FG, COL_FRM}; +static float color[][3] = { + {0.09, 0.14, 0.2}, // COL_BG + {0.31, 0.58, 0.9}, // COL_FG + {0.19, 0.23, 0.46} // COL_FRM +}; + bool exui_init() { - size.x = 150; - size.y = 180; + size.x = 15; + size.y = 18; text_padding = size.x * 0.01; return true; @@ -30,6 +47,11 @@ void exui_shutdown() { } +void exui_setnode(const SceneNode *node) +{ + parent = node; +} + void exui_change_tab(int dir) { } @@ -73,16 +95,91 @@ void exui_update(float dt) void exui_draw() { if(!exsel_active) return; + if(!ui_font) return; - draw_titlebar(); - draw_tabs(); + dtx_use_font(ui_font, ui_font_size); + float rowspc = dtx_line_height() * text_scale; + + Rect rect = {-size.x / 2.0f, -size.y / 2.0f, size.x, size.y}; + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + if(parent) { + glMultMatrixf(parent->get_matrix()[0]); + } + glTranslatef(pos.x, pos.y, pos.z); + glScalef(1, -1, 1); + + bind_shader(0); + + glPushAttrib(GL_ENABLE_BIT); + glDisable(GL_TEXTURE_2D); + glDisable(GL_LIGHTING); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + glDepthMask(0); + + draw_frame(rect); + Rect tbar_rect = {rect.x, rect.y, rect.w, rowspc}; + draw_titlebar(tbar_rect); + Rect tabs_rect = {tbar_rect.x, tbar_rect.y + rowspc, tbar_rect.w, tbar_rect.h}; + draw_tabs(tabs_rect); + Rect text_rect = {rect.x, tabs_rect.y + rowspc, rect.w, rect.h - tabs_rect.y - rowspc}; + draw_text(text_rect); + + glDepthMask(1); + glPopAttrib(); + + glPopMatrix(); } -static void draw_titlebar() +static inline float *vrect(const Rect &rect, int i) { + static float v[2]; + v[0] = ((i + 1) & 2) ? rect.x + rect.w : rect.x; + v[1] = (i & 2) ? rect.y : rect.y + rect.h; + return v; } -static void draw_tabs() +static inline void draw_rect(const Rect &rect, int col) +{ + glBegin(GL_QUADS); + glColor3fv(color[col]); + for(int i=0; i<4; i++) + glVertex2fv(vrect(rect, i)); + glEnd(); +} + +static void draw_frame(const Rect &rect) +{ + draw_rect(rect, COL_BG); + glLineWidth(2.0); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + draw_rect(rect, COL_FRM); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); +} + +static void draw_titlebar(const Rect &rect) +{ + draw_rect(rect, COL_FRM); + + const char *title = ex->get_name(); + if(title) { + glPushMatrix(); + glTranslatef(rect.x + text_padding, rect.y + rect.h - text_padding, 0); + glScalef(text_scale, -text_scale, text_scale); + + glColor3fv(color[COL_BG]); + dtx_string(ex->get_name()); + glPopMatrix(); + } +} + +static void draw_tabs(const Rect &rect) +{ +} + +static void draw_text(const Rect &rect) { } @@ -109,7 +206,11 @@ static void layout_text(const char *text) int code = dtx_utf8_char_code(text); const char *next = dtx_utf8_next_char((char*)text); - pos += dtx_glyph_width(code); + pos += dtx_glyph_width(code) * text_scale; + + if(code < 256 && isspace(code)) { + last_break = text; + } if(pos >= size.x - text_padding) { if(text == text_lines.back()) { @@ -129,4 +230,18 @@ static void layout_text(const char *text) } text = next; } + text_lines.push_back(0); + + /* + debug_log("text layout:\n"); + for(size_t i=0; i +class SceneNode; + bool exui_init(); void exui_shutdown(); +void exui_setnode(const SceneNode *node); void exui_change_tab(int dir); void exui_scroll(float delta); -- 1.7.10.4