new render target class while working on the exhibit UI
authorJohn Tsiombikas <nuclear@member.fsf.org>
Sat, 20 Jan 2018 11:40:41 +0000 (13:40 +0200)
committerJohn Tsiombikas <nuclear@member.fsf.org>
Sat, 20 Jan 2018 11:40:41 +0000 (13:40 +0200)
src/app.cc
src/post.cc
src/rtarg.cc [new file with mode: 0644]
src/rtarg.h [new file with mode: 0644]
src/texture.cc
src/texture.h
src/ui_exhibit.cc
src/ui_exhibit.h

index 620ec52..7fc5c13 100644 (file)
@@ -21,6 +21,7 @@
 #include "blob_exhibit.h"
 #include "dbg_gui.h"
 #include "geomdraw.h"
 #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
 
 #define NEAR_CLIP      5.0
 #define FAR_CLIP       10000.0
@@ -170,6 +171,11 @@ bool app_init(int argc, char **argv)
                //return false;
        }
        */
                //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;
 
        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;
 
 
        delete rend;
 
+       exui_shutdown();
+
        /* this must be destroyed before the scene graph to detach exhibit nodes
         * before the scene tries to delete them recursively
         */
        /* 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);
 
        mscn->update(dt);
        exman->update(dt);
+       exui_update(dt);
 
        float speed = walk_speed * dt;
        Vec3 dir;
 
        float speed = walk_speed * dt;
        Vec3 dir;
@@ -351,13 +360,18 @@ static void update(float dt)
                exsel_hover = exman->select(ray);
        }
 
                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);
        // 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()
 }
 
 void app_display()
@@ -498,6 +512,8 @@ static void draw_scene()
                glPopAttrib();
        }
 
                glPopAttrib();
        }
 
+       exui_draw();
+
        print_text(Vec2(9 * win_width / 10, 20), Vec3(1, 1, 0), "fps: %.1f", framerate);
        draw_ui();
 }
        print_text(Vec2(9 * win_width / 10, 20), Vec3(1, 1, 0), "fps: %.1f", framerate);
        draw_ui();
 }
index b675f5b..885a212 100644 (file)
@@ -1,5 +1,6 @@
 #include "opengl.h"
 #include "app.h"
 #include "opengl.h"
 #include "app.h"
+#include "texture.h"   // next_pow2
 
 static void update_fbtex();
 
 
 static void update_fbtex();
 
@@ -36,17 +37,6 @@ void slow_post(unsigned int sdr)
        glPopAttrib();
 }
 
        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) {
 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 (file)
index 0000000..f90c1d3
--- /dev/null
@@ -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; i<num; i++) {
+                       tex[i] = new Texture;
+                       tex[i]->create(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; i<num; i++) {
+                       if(tex[i]) {
+                               glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D,
+                                               tex[i]->get_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 (file)
index 0000000..f77682d
--- /dev/null
@@ -0,0 +1,63 @@
+#ifndef RTARG_H_
+#define RTARG_H_
+
+#include <gmath/gmath.h>
+#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_
index f4f7cfe..ed34798 100644 (file)
@@ -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;
 
 
 Image *Texture::default_img;
 
index 5c4bf3c..41749f8 100644 (file)
@@ -60,6 +60,7 @@ public:
 };
 
 void bind_texture(Texture *tex, int tunit = 0);
 };
 
 void bind_texture(Texture *tex, int tunit = 0);
+int next_pow2(int x);
 
 class TextureSet : public DataSet<Texture*> {
 private:
 
 class TextureSet : public DataSet<Texture*> {
 private:
index 355d078..eda39c6 100644 (file)
@@ -1,26 +1,43 @@
+#include <drawtext.h>
 #include "ui_exhibit.h"
 #include "ui.h"
 #include "app.h"
 #include "ui_exhibit.h"
 #include "ui.h"
 #include "app.h"
-#include <drawtext.h>
+#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 void layout_text(const char *text);
 
+static const SceneNode *parent;
 static Vec3 pos;
 static Vec2 size;
 static float text_padding;
 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<const char*> text_lines;
 static AudioStream *voice;
 
 static Exhibit *ex;
 static int vis_tab;
 static float scroll;
 static std::vector<const char*> 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()
 {
 
 bool exui_init()
 {
-       size.x = 150;
-       size.y = 180;
+       size.x = 15;
+       size.y = 18;
        text_padding = size.x * 0.01;
 
        return true;
        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)
 {
 }
 void exui_change_tab(int dir)
 {
 }
@@ -73,16 +95,91 @@ void exui_update(float dt)
 void exui_draw()
 {
        if(!exsel_active) return;
 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);
 
                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()) {
 
                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 = next;
        }
+       text_lines.push_back(0);
+
+       /*
+       debug_log("text layout:\n");
+       for(size_t i=0; i<text_lines.size() - 1; i++) {
+               const char *p = text_lines[i];
+               while(*p && p != text_lines[i + 1]) {
+                       putchar(*p);
+                       ++p;
+               }
+               putchar('\n');
+       }
+       debug_log("---\n");
+       */
 }
 }
index bf0d222..335e23e 100644 (file)
@@ -3,8 +3,11 @@
 
 #include <gmath/gmath.h>
 
 
 #include <gmath/gmath.h>
 
+class SceneNode;
+
 bool exui_init();
 void exui_shutdown();
 bool exui_init();
 void exui_shutdown();
+void exui_setnode(const SceneNode *node);
 
 void exui_change_tab(int dir);
 void exui_scroll(float delta);
 
 void exui_change_tab(int dir);
 void exui_scroll(float delta);