#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
//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;
delete rend;
+ exui_shutdown();
+
/* this must be destroyed before the scene graph to detach exhibit nodes
* before the scene tries to delete them recursively
*/
mscn->update(dt);
exman->update(dt);
+ exui_update(dt);
float speed = walk_speed * dt;
Vec3 dir;
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()
glPopAttrib();
}
+ exui_draw();
+
print_text(Vec2(9 * win_width / 10, 20), Vec3(1, 1, 0), "fps: %.1f", framerate);
draw_ui();
}
#include "opengl.h"
#include "app.h"
+#include "texture.h" // next_pow2
static void update_fbtex();
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) {
--- /dev/null
+#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);
+}
--- /dev/null
+#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_
}
}
+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;
};
void bind_texture(Texture *tex, int tunit = 0);
+int next_pow2(int x);
class TextureSet : public DataSet<Texture*> {
private:
+#include <drawtext.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 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<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()
{
- size.x = 150;
- size.y = 180;
+ size.x = 15;
+ size.y = 18;
text_padding = size.x * 0.01;
return true;
{
}
+void exui_setnode(const SceneNode *node)
+{
+ parent = node;
+}
+
void exui_change_tab(int dir)
{
}
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)
{
}
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()) {
}
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");
+ */
}
#include <gmath/gmath.h>
+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);