new render target class while working on the exhibit UI
[laserbrain_demo] / src / ui_exhibit.cc
1 #include <drawtext.h>
2 #include "ui_exhibit.h"
3 #include "ui.h"
4 #include "app.h"
5 #include "snode.h"
6 #include "shader.h"
7
8 struct Rect {
9         float x, y, w, h;
10 };
11
12 static void draw_frame(const Rect &rect);
13 static void draw_titlebar(const Rect &rect);
14 static void draw_tabs(const Rect &rect);
15 static void draw_text(const Rect &rect);
16 static void layout_text(const char *text);
17
18 static const SceneNode *parent;
19 static Vec3 pos;
20 static Vec2 size;
21 static float text_padding;
22 static float text_scale = 0.05f;
23 static Exhibit *ex;
24 static int vis_tab;
25 static float scroll;
26 static std::vector<const char*> text_lines;
27 static AudioStream *voice;
28
29 enum {COL_BG, COL_FG, COL_FRM};
30 static float color[][3] = {
31         {0.09, 0.14, 0.2},      // COL_BG
32         {0.31, 0.58, 0.9},      // COL_FG
33         {0.19, 0.23, 0.46}      // COL_FRM
34 };
35
36
37 bool exui_init()
38 {
39         size.x = 15;
40         size.y = 18;
41         text_padding = size.x * 0.01;
42
43         return true;
44 }
45
46 void exui_shutdown()
47 {
48 }
49
50 void exui_setnode(const SceneNode *node)
51 {
52         parent = node;
53 }
54
55 void exui_change_tab(int dir)
56 {
57 }
58
59 void exui_scroll(float delta)
60 {
61 }
62
63 bool exui_raycast(const Ray &ray)
64 {
65         return false;
66 }
67
68 void exui_update(float dt)
69 {
70         if(exsel_active.ex != ex) {
71                 ex = exsel_active.ex;
72                 scroll = 0.0f;
73                 vis_tab = 0;
74                 text_lines.clear();
75                 if(voice) voice->stop();
76
77                 if(ex) {
78                         int num_data = ex->data.size();
79                         for(int i=0; i<num_data; i++) {
80                                 if(ex->data[i].type == EXDATA_INFO) {
81                                         layout_text(ex->data[i].text.c_str());
82                                         voice = ex->data[i].voice;
83                                 }
84                         }
85
86                         if(voice) {
87                                 voice->play(AUDIO_PLAYMODE_ONCE);
88                         }
89                 } else {
90                         voice = 0;
91                 }
92         }
93 }
94
95 void exui_draw()
96 {
97         if(!exsel_active) return;
98         if(!ui_font) return;
99
100         dtx_use_font(ui_font, ui_font_size);
101         float rowspc = dtx_line_height() * text_scale;
102
103         Rect rect = {-size.x / 2.0f, -size.y / 2.0f, size.x, size.y};
104
105         glMatrixMode(GL_MODELVIEW);
106         glPushMatrix();
107         if(parent) {
108                 glMultMatrixf(parent->get_matrix()[0]);
109         }
110         glTranslatef(pos.x, pos.y, pos.z);
111         glScalef(1, -1, 1);
112
113         bind_shader(0);
114
115         glPushAttrib(GL_ENABLE_BIT);
116         glDisable(GL_TEXTURE_2D);
117         glDisable(GL_LIGHTING);
118         glEnable(GL_BLEND);
119         glBlendFunc(GL_SRC_ALPHA, GL_ONE);
120         glDepthMask(0);
121
122         draw_frame(rect);
123         Rect tbar_rect = {rect.x, rect.y, rect.w, rowspc};
124         draw_titlebar(tbar_rect);
125         Rect tabs_rect = {tbar_rect.x, tbar_rect.y + rowspc, tbar_rect.w, tbar_rect.h};
126         draw_tabs(tabs_rect);
127         Rect text_rect = {rect.x, tabs_rect.y + rowspc, rect.w, rect.h - tabs_rect.y - rowspc};
128         draw_text(text_rect);
129
130         glDepthMask(1);
131         glPopAttrib();
132
133         glPopMatrix();
134 }
135
136 static inline float *vrect(const Rect &rect, int i)
137 {
138         static float v[2];
139         v[0] = ((i + 1) & 2) ? rect.x + rect.w : rect.x;
140         v[1] = (i & 2) ? rect.y : rect.y + rect.h;
141         return v;
142 }
143
144 static inline void draw_rect(const Rect &rect, int col)
145 {
146         glBegin(GL_QUADS);
147         glColor3fv(color[col]);
148         for(int i=0; i<4; i++)
149                 glVertex2fv(vrect(rect, i));
150         glEnd();
151 }
152
153 static void draw_frame(const Rect &rect)
154 {
155         draw_rect(rect, COL_BG);
156         glLineWidth(2.0);
157         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
158         draw_rect(rect, COL_FRM);
159         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
160 }
161
162 static void draw_titlebar(const Rect &rect)
163 {
164         draw_rect(rect, COL_FRM);
165
166         const char *title = ex->get_name();
167         if(title) {
168                 glPushMatrix();
169                 glTranslatef(rect.x + text_padding, rect.y + rect.h - text_padding, 0);
170                 glScalef(text_scale, -text_scale, text_scale);
171
172                 glColor3fv(color[COL_BG]);
173                 dtx_string(ex->get_name());
174                 glPopMatrix();
175         }
176 }
177
178 static void draw_tabs(const Rect &rect)
179 {
180 }
181
182 static void draw_text(const Rect &rect)
183 {
184 }
185
186 static void layout_text(const char *text)
187 {
188         text_lines.clear();
189         if(!text) return;
190         if(!ui_font) return;
191
192         dtx_use_font(ui_font, ui_font_size);
193
194         float pos = text_padding;
195         text_lines.push_back(text);
196         const char *last_break = 0;
197
198         while(*text) {
199                 if(*text == '\n') {     // paragraph break
200                         text_lines.push_back(text);
201                         text_lines.push_back(++text);
202                         pos = text_padding;
203                         last_break = 0;
204                         continue;
205                 }
206
207                 int code = dtx_utf8_char_code(text);
208                 const char *next = dtx_utf8_next_char((char*)text);
209                 pos += dtx_glyph_width(code) * text_scale;
210
211                 if(code < 256 && isspace(code)) {
212                         last_break = text;
213                 }
214
215                 if(pos >= size.x - text_padding) {
216                         if(text == text_lines.back()) {
217                                 // not even a single character fits on a line... abort
218                                 warning_log("text layout failed. glyph %d doesn't fit in line (%g)\n", code, size.x - 2.0 * text_padding);
219                                 text_lines.clear();
220                                 return;
221                         }
222                         if(last_break) {
223                                 text_lines.push_back(last_break + 1);
224                                 last_break = 0;
225                         } else {
226                                 // no good point to break, just break here
227                                 text_lines.push_back(text);
228                         }
229                         pos = text_padding;
230                 }
231                 text = next;
232         }
233         text_lines.push_back(0);
234
235         /*
236         debug_log("text layout:\n");
237         for(size_t i=0; i<text_lines.size() - 1; i++) {
238                 const char *p = text_lines[i];
239                 while(*p && p != text_lines[i + 1]) {
240                         putchar(*p);
241                         ++p;
242                 }
243                 putchar('\n');
244         }
245         debug_log("---\n");
246         */
247 }