+#include <assert.h>
+#if defined(WIN32) || defined(__WIN32__)
+#include <malloc.h>
+#else
+#include <alloca.h>
+#endif
#include <drawtext.h>
#include "ui_exhibit.h"
#include "ui.h"
#include "app.h"
#include "snode.h"
-#include "shader.h"
+#include "sdr.h"
#include "rtarg.h"
struct Rect {
static void draw_text(const Rect &rect);
static void layout_text(const char *text);
+static struct dtx_font *font;
+static int font_size;
+static unsigned int fontsdr;
+
static float aspect;
static int ui_width, ui_height;
+static Mat4 tilt_matrix;
+
static RenderTarget *rtarg;
static const SceneNode *parent;
static Vec3 pos;
static Vec2 size;
static int text_padding;
-static float text_scale = 1.0f;//0.05f;
+static float text_scale = 0.65f;
+static float scale = 1.0f;
static Exhibit *ex;
-static int vis_tab;
+static int vis_tab, num_tabs;
+static std::vector<std::string> tab_names;
static float scroll;
static std::vector<const char*> text_lines;
+static int max_line_size;
static AudioStream *voice;
enum {COL_BG, COL_FG, COL_FRM};
static float color[][3] = {
- {0.09, 0.14, 0.2}, // COL_BG
+ {0.014, 0.016, 0.04}, // COL_BG
{0.31, 0.58, 0.9}, // COL_FG
{0.19, 0.23, 0.46} // COL_FRM
};
bool exui_init()
{
+ if(!(font = dtx_open_font_glyphmap("data/ui_en.glyphmap"))) {
+ error_log("failed to open exhibit ui font\n");
+ return false;
+ }
+ font_size = dtx_get_glyphmap_ptsize(dtx_get_glyphmap(font, 0));
+
+ if(!(fontsdr = create_program_load("sdr/dfont.v.glsl", "sdr/dfont.p.glsl"))) {
+ error_log("failed to load font shader\n");
+ return false;
+ }
+
+ tilt_matrix = Mat4::identity;
+
size.x = 15;
size.y = 18;
- text_padding = ui_width / 100;
+ text_padding = 6;
+
+ scale = 1.0f;
aspect = size.x / size.y;
ui_height = 512;
ui_width = ui_height * aspect;
rtarg = new RenderTarget;
- if(!rtarg->create(ui_width, ui_height, GL_RGB)) {
+ if(!rtarg->create(ui_width, ui_height, GL_RGBA)) {
error_log("failed to create exui render target\n");
return false;
}
void exui_shutdown()
{
+ dtx_close_font(font);
delete rtarg;
}
parent = node;
}
+void exui_rotation(const Vec3 &euler)
+{
+ tilt_matrix.rotation(euler);
+}
+
+void exui_scale(float s)
+{
+ scale = s;
+}
+
void exui_change_tab(int dir)
{
+ vis_tab = (vis_tab + dir) % num_tabs;
}
void exui_scroll(float delta)
ex = exsel_active.ex;
scroll = 0.0f;
vis_tab = 0;
+ num_tabs = 0;
+ tab_names.clear();
text_lines.clear();
if(voice) voice->stop();
if(ex->data[i].type == EXDATA_INFO) {
layout_text(ex->data[i].text.c_str());
voice = ex->data[i].voice;
+ ++num_tabs;
+ tab_names.push_back("info");
}
}
static void draw_2d_ui()
{
- dtx_use_font(ui_font, ui_font_size);
+ dtx_use_font(font, font_size);
float rowspc = dtx_line_height() * text_scale;
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
- glOrtho(0, ui_width, 0, ui_height, -1, 1);
+ glTranslatef(-1, 1, 0);
+ glScalef(2.0 / ui_width, -2.0 / ui_height, 1);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
- bind_shader(0);
+ glUseProgram(0);
glPushAttrib(GL_ENABLE_BIT);
glDisable(GL_TEXTURE_2D);
glDisable(GL_LIGHTING);
glDisable(GL_DEPTH_TEST);
+ glEnable(GL_SCISSOR_TEST);
Rect rect = {0, 0, (float)ui_width, (float)ui_height};
draw_frame(rect);
- Rect tbar_rect = {rect.x, rect.y, rect.w, rowspc};
+ Rect tbar_rect = {rect.x, rect.y, rect.w, rowspc + text_padding}; // half the padding
draw_titlebar(tbar_rect);
- Rect tabs_rect = {tbar_rect.x, tbar_rect.y + rowspc, tbar_rect.w, tbar_rect.h};
+ Rect tabs_rect = {tbar_rect.x, tbar_rect.y + tbar_rect.h, 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);
+
+ if(num_tabs) {
+ switch(ex->data[vis_tab].type) {
+ case EXDATA_INFO:
+ {
+ Rect text_rect = {rect.x, tabs_rect.y + tabs_rect.h, rect.w, rect.h - tabs_rect.y - tabs_rect.h};
+ draw_text(text_rect);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
glPopAttrib();
void exui_draw()
{
if(!exsel_active) return;
- if(!ui_font) return;
+ if(!font) return;
// render the 2D UI in a texture
push_render_target(rtarg);
- glClearColor(0, 1, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
draw_2d_ui();
pop_render_target();
// place UI image into the scene
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
+
+ /*
+ Mat4 mvmat;
+ glGetFloatv(GL_MODELVIEW_MATRIX, mvmat[0]);
+ if(parent) {
+ mvmat = parent->get_matrix() * mvmat;
+ }
+ mvmat.translate(pos.x, pos.y, pos.z);
+
+ mvmat[0][0] = mvmat[1][1] = mvmat[2][2] = 1.0f;
+ mvmat[0][1] = mvmat[0][2] = mvmat[1][0] = mvmat[2][0] = mvmat[1][2] = mvmat[2][1] = 0.0f;
+ glLoadMatrixf(mvmat[0]);
+ */
+ Mat4 xform;
if(parent) {
- glMultMatrixf(parent->get_matrix()[0]);
+ xform = parent->get_matrix();
}
- glTranslatef(pos.x, pos.y, pos.z);
+ xform = tilt_matrix * xform;
+ xform.pre_scale(scale, scale, scale);
+ glMultMatrixf(xform[0]);
glPushAttrib(GL_ENABLE_BIT);
glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+ glBlendColor(0, 0, 0, 0.35);
+ glBlendFunc(GL_SRC_ALPHA, GL_CONSTANT_ALPHA);
glEnable(GL_TEXTURE_2D);
+ glDisable(GL_CULL_FACE);
glDepthMask(0);
- bind_shader(0);
+ glUseProgram(0);
bind_texture(rtarg->texture());
glMatrixMode(GL_TEXTURE);
glEnd();
}
+static void clip_rect(const Rect &rect)
+{
+ glScissor(rect.x, ui_height - rect.y - rect.h, rect.w, rect.h);
+}
+
static void draw_frame(const Rect &rect)
{
+ clip_rect(rect);
+
draw_rect(rect, COL_BG);
- glLineWidth(2.0);
+ glLineWidth(3.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)
{
+ clip_rect(rect);
+
draw_rect(rect, COL_FRM);
const char *title = ex->get_name();
if(title) {
+ glUseProgram(fontsdr);
+
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();
+
+ glUseProgram(0);
}
}
static void draw_tabs(const Rect &rect)
{
+ if(!num_tabs) return;
+
+ clip_rect(rect);
+
+ glLineWidth(1);
+ if(num_tabs == 1) {
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ draw_rect(rect, COL_FRM);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ }
+
+ int max_tab_size = ui_width / 2;
+ int tab_size = std::min(max_tab_size, ui_width / num_tabs);
+
+ for(int i=0; i<num_tabs; i++) {
+ Rect tr = {rect.x + i * tab_size, rect.y, (float)tab_size, rect.h};
+
+ clip_rect(tr);
+
+ if(vis_tab == i) {
+ draw_rect(tr, COL_FRM);
+ } else {
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ draw_rect(tr, COL_FRM);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ }
+
+ glPushMatrix();
+ glTranslatef(tr.x + text_padding, tr.y + tr.h - text_padding, 0);
+ glScalef(text_scale, -text_scale, text_scale);
+
+ glUseProgram(fontsdr);
+ glColor3fv(color[vis_tab == i ? COL_BG : COL_FRM]);
+ dtx_string(tab_names[i].c_str());
+ glUseProgram(0);
+
+ glPopMatrix();
+ }
}
static void draw_text(const Rect &rect)
{
+ clip_rect(rect);
+
+ char *buf = (char*)alloca(max_line_size + 1);
+
+ float dy = dtx_line_height();
+
+ glUseProgram(fontsdr);
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glTranslatef(rect.x + text_padding, rect.y + dy + text_padding, 0);
+ glScalef(text_scale, -text_scale, text_scale);
+
+ glColor3fv(color[COL_FG]);
+
+ int nlines = text_lines.size() - 1;
+ for(int i=0; i<nlines; i++) {
+ if(i < nlines - 1) {
+ int sz = text_lines[i + 1] - text_lines[i];
+ assert(sz <= max_line_size);
+ memcpy(buf, text_lines[i], sz);
+ buf[sz] = 0;
+ } else {
+ buf = (char*)text_lines[i];
+ }
+
+ dtx_position(0, -dy * i);
+ dtx_string(buf);
+ }
+ dtx_position(0, 0);
+
+ glPopMatrix();
+ glUseProgram(0);
}
static void layout_text(const char *text)
{
text_lines.clear();
if(!text) return;
- if(!ui_font) return;
+ if(!font) return;
+
+ dtx_use_font(font, font_size);
- dtx_use_font(ui_font, ui_font_size);
+ int left_margin = text_padding;
+ int right_margin = ui_width - text_padding;
- float pos = text_padding;
text_lines.push_back(text);
const char *last_break = 0;
+ max_line_size = 1;
while(*text) {
if(*text == '\n') { // paragraph break
text_lines.push_back(text);
text_lines.push_back(++text);
- pos = text_padding;
last_break = 0;
continue;
}
int code = dtx_utf8_char_code(text);
const char *next = dtx_utf8_next_char((char*)text);
- pos += dtx_glyph_width(code) * text_scale;
+
+ struct dtx_box box;
+ dtx_substring_box(text_lines.back(), 0, text - text_lines.back(), &box);
+ float pos = left_margin + (box.width + box.x) * text_scale;
if(code < 256 && isspace(code)) {
last_break = text;
}
- if(pos >= ui_width - text_padding) {
+ if(pos > right_margin) {
if(text == text_lines.back()) {
// not even a single character fits on a line... abort
- warning_log("text layout failed. glyph %d doesn't fit in line (%d)\n", code, ui_width - 2 * text_padding);
+ warning_log("text layout failed. glyph %d doesn't fit in line (%d)\n", code, right_margin - left_margin);
text_lines.clear();
return;
}
// no good point to break, just break here
text_lines.push_back(text);
}
- pos = text_padding;
+
+ int d = text_lines.back() - (text_lines[text_lines.size() - 2]);
+ if(d > max_line_size) max_line_size = d;
}
text = next;
}