9b42c453aa9a02378132523b8e9603cc2ddac374
[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 #include "rtarg.h"
8
9 struct Rect {
10         float x, y, w, h;
11 };
12
13 static void draw_frame(const Rect &rect);
14 static void draw_titlebar(const Rect &rect);
15 static void draw_tabs(const Rect &rect);
16 static void draw_text(const Rect &rect);
17 static void layout_text(const char *text);
18
19 static float aspect;
20 static int ui_width, ui_height;
21
22 static RenderTarget *rtarg;
23 static const SceneNode *parent;
24 static Vec3 pos;
25 static Vec2 size;
26 static int text_padding;
27 static float text_scale = 1.0f;//0.05f;
28 static Exhibit *ex;
29 static int vis_tab;
30 static float scroll;
31 static std::vector<const char*> text_lines;
32 static AudioStream *voice;
33
34 enum {COL_BG, COL_FG, COL_FRM};
35 static float color[][3] = {
36         {0.09, 0.14, 0.2},      // COL_BG
37         {0.31, 0.58, 0.9},      // COL_FG
38         {0.19, 0.23, 0.46}      // COL_FRM
39 };
40
41
42 bool exui_init()
43 {
44         size.x = 15;
45         size.y = 18;
46         text_padding = ui_width / 100;
47
48         aspect = size.x / size.y;
49         ui_height = 512;
50         ui_width = ui_height * aspect;
51
52         rtarg = new RenderTarget;
53         if(!rtarg->create(ui_width, ui_height, GL_RGBA)) {
54                 error_log("failed to create exui render target\n");
55                 return false;
56         }
57
58         return true;
59 }
60
61 void exui_shutdown()
62 {
63         delete rtarg;
64 }
65
66 void exui_setnode(const SceneNode *node)
67 {
68         parent = node;
69 }
70
71 void exui_change_tab(int dir)
72 {
73 }
74
75 void exui_scroll(float delta)
76 {
77 }
78
79 bool exui_raycast(const Ray &ray)
80 {
81         return false;
82 }
83
84 void exui_update(float dt)
85 {
86         if(exsel_active.ex != ex) {
87                 ex = exsel_active.ex;
88                 scroll = 0.0f;
89                 vis_tab = 0;
90                 text_lines.clear();
91                 if(voice) voice->stop();
92
93                 if(ex) {
94                         int num_data = ex->data.size();
95                         for(int i=0; i<num_data; i++) {
96                                 if(ex->data[i].type == EXDATA_INFO) {
97                                         layout_text(ex->data[i].text.c_str());
98                                         voice = ex->data[i].voice;
99                                 }
100                         }
101
102                         if(voice) {
103                                 voice->play(AUDIO_PLAYMODE_ONCE);
104                         }
105                 } else {
106                         voice = 0;
107                 }
108         }
109 }
110
111 static void draw_2d_ui()
112 {
113         dtx_use_font(ui_font, ui_font_size);
114         float rowspc = dtx_line_height() * text_scale;
115
116         glMatrixMode(GL_PROJECTION);
117         glPushMatrix();
118         glLoadIdentity();
119         glTranslatef(-1, 1, 0);
120         glScalef(2.0 / ui_width, -2.0 / ui_height, 1);
121
122         glMatrixMode(GL_MODELVIEW);
123         glPushMatrix();
124         glLoadIdentity();
125
126         bind_shader(0);
127
128         glPushAttrib(GL_ENABLE_BIT);
129         glDisable(GL_TEXTURE_2D);
130         glDisable(GL_LIGHTING);
131         glDisable(GL_DEPTH_TEST);
132
133         Rect rect = {0, 0, (float)ui_width, (float)ui_height};
134         draw_frame(rect);
135         Rect tbar_rect = {rect.x, rect.y, rect.w, rowspc};
136         draw_titlebar(tbar_rect);
137         Rect tabs_rect = {tbar_rect.x, tbar_rect.y + rowspc, tbar_rect.w, tbar_rect.h};
138         draw_tabs(tabs_rect);
139         Rect text_rect = {rect.x, tabs_rect.y + rowspc, rect.w, rect.h - tabs_rect.y - rowspc};
140         draw_text(text_rect);
141
142         glPopAttrib();
143
144         glMatrixMode(GL_PROJECTION);
145         glPopMatrix();
146         glMatrixMode(GL_MODELVIEW);
147         glPopMatrix();
148 }
149
150 void exui_draw()
151 {
152         if(!exsel_active) return;
153         if(!ui_font) return;
154
155         // render the 2D UI in a texture
156         push_render_target(rtarg);
157         glClearColor(0, 1, 0, 0);
158         glClear(GL_COLOR_BUFFER_BIT);
159         draw_2d_ui();
160         pop_render_target();
161
162         // place UI image into the scene
163         glMatrixMode(GL_MODELVIEW);
164         glPushMatrix();
165
166         Mat4 mvmat;
167         glGetFloatv(GL_MODELVIEW_MATRIX, mvmat[0]);
168
169         /*
170         if(parent) {
171                 glMultMatrixf(parent->get_matrix()[0]);
172         }
173         glTranslatef(pos.x, pos.y, pos.z);
174         */
175         if(parent) {
176                 mvmat = parent->get_matrix() * mvmat;
177         }
178         mvmat.translate(pos.x, pos.y, pos.z);
179
180         mvmat[0][0] = mvmat[1][1] = mvmat[2][2] = 1.0f;
181         mvmat[0][1] = mvmat[0][2] = mvmat[1][0] = mvmat[2][0] = mvmat[1][2] = mvmat[2][1] = 0.0f;
182
183         glLoadMatrixf(mvmat[0]);
184
185         glPushAttrib(GL_ENABLE_BIT);
186         glEnable(GL_BLEND);
187         glBlendFunc(GL_SRC_ALPHA, GL_ONE);
188         glEnable(GL_TEXTURE_2D);
189         glDepthMask(0);
190
191         bind_shader(0);
192         bind_texture(rtarg->texture());
193
194         glMatrixMode(GL_TEXTURE);
195         glLoadMatrixf(rtarg->texture_matrix()[0]);
196
197         glBegin(GL_QUADS);
198         glColor3f(1, 1, 1);
199         glTexCoord2f(0, 0); glVertex2f(-size.x / 2, -size.y / 2);
200         glTexCoord2f(1, 0); glVertex2f(size.x / 2, -size.y / 2);
201         glTexCoord2f(1, 1); glVertex2f(size.x / 2, size.y / 2);
202         glTexCoord2f(0, 1); glVertex2f(-size.x / 2, size.y / 2);
203         glEnd();
204
205         glLoadIdentity();
206
207         glDepthMask(1);
208         glPopAttrib();
209
210         glMatrixMode(GL_MODELVIEW);
211         glPopMatrix();
212 }
213
214 static inline float *vrect(const Rect &rect, int i)
215 {
216         static float v[2];
217         v[0] = ((i + 1) & 2) ? rect.x + rect.w : rect.x;
218         v[1] = (i & 2) ? rect.y : rect.y + rect.h;
219         return v;
220 }
221
222 static inline void draw_rect(const Rect &rect, int col)
223 {
224         glBegin(GL_QUADS);
225         glColor3fv(color[col]);
226         for(int i=0; i<4; i++)
227                 glVertex2fv(vrect(rect, i));
228         glEnd();
229 }
230
231 static void draw_frame(const Rect &rect)
232 {
233         draw_rect(rect, COL_BG);
234         glLineWidth(2.0);
235         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
236         draw_rect(rect, COL_FRM);
237         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
238 }
239
240 static void draw_titlebar(const Rect &rect)
241 {
242         draw_rect(rect, COL_FRM);
243
244         const char *title = ex->get_name();
245         if(title) {
246                 glPushMatrix();
247                 glTranslatef(rect.x + text_padding, rect.y + rect.h - text_padding, 0);
248                 glScalef(text_scale, -text_scale, text_scale);
249
250                 glColor3fv(color[COL_BG]);
251                 dtx_string(ex->get_name());
252                 glPopMatrix();
253         }
254 }
255
256 static void draw_tabs(const Rect &rect)
257 {
258 }
259
260 static void draw_text(const Rect &rect)
261 {
262 }
263
264 static void layout_text(const char *text)
265 {
266         text_lines.clear();
267         if(!text) return;
268         if(!ui_font) return;
269
270         dtx_use_font(ui_font, ui_font_size);
271
272         float pos = text_padding;
273         text_lines.push_back(text);
274         const char *last_break = 0;
275
276         while(*text) {
277                 if(*text == '\n') {     // paragraph break
278                         text_lines.push_back(text);
279                         text_lines.push_back(++text);
280                         pos = text_padding;
281                         last_break = 0;
282                         continue;
283                 }
284
285                 int code = dtx_utf8_char_code(text);
286                 const char *next = dtx_utf8_next_char((char*)text);
287                 pos += dtx_glyph_width(code) * text_scale;
288
289                 if(code < 256 && isspace(code)) {
290                         last_break = text;
291                 }
292
293                 if(pos >= ui_width - text_padding) {
294                         if(text == text_lines.back()) {
295                                 // not even a single character fits on a line... abort
296                                 warning_log("text layout failed. glyph %d doesn't fit in line (%d)\n", code, ui_width - 2 * text_padding);
297                                 text_lines.clear();
298                                 return;
299                         }
300                         if(last_break) {
301                                 text_lines.push_back(last_break + 1);
302                                 last_break = 0;
303                         } else {
304                                 // no good point to break, just break here
305                                 text_lines.push_back(text);
306                         }
307                         pos = text_padding;
308                 }
309                 text = next;
310         }
311         text_lines.push_back(0);
312
313         /*
314         debug_log("text layout:\n");
315         for(size_t i=0; i<text_lines.size() - 1; i++) {
316                 const char *p = text_lines[i];
317                 while(*p && p != text_lines[i + 1]) {
318                         putchar(*p);
319                         ++p;
320                 }
321                 putchar('\n');
322         }
323         debug_log("---\n");
324         */
325 }