1: /*
2: * Guitar-ZyX(tm)::MasterControlProgram - portable guitar F/X controller
3: * Copyright (C) 2009 Douglas McClendon
4: *
5: * This program is free software: you can redistribute it and/or modify
6: * it under the terms of the GNU General Public License as published by
7: * the Free Software Foundation, version 3 of the License.
8: *
9: * This program is distributed in the hope that it will be useful,
10: * but WITHOUT ANY WARRANTY; without even the implied warranty of
11: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12: * GNU General Public License for more details.
13: *
14: * You should have received a copy of the GNU General Public License
15: * along with this program. If not, see <http://www.gnu.org/licenses/>.
16: */
17: /*
18: #############################################################################
19: #############################################################################
20: ##
21: ## gzmcpc::graphics: visual output rendering
22: ##
23: #############################################################################
24: ##
25: ## Copyright 2008-2009 Douglas McClendon <dmc AT filteredperception DOT org>
26: ##
27: #############################################################################
28: #############################################################################
29: #
30: */
31:
32:
33:
34:
35: #include <nds.h>
36:
37: #include <stdio.h>
38:
39: #include <stdarg.h>
40:
41: #include <math.h>
42:
43:
44: #include "graphics.h"
45:
46: #include "debug.h"
47:
48: #include "input.h"
49:
50: #include "mcp.h"
51:
52: #include "modes.h"
53:
54: #include "mode__tpw__jam.h"
55:
56: #include "network.h"
57:
58: #include "sound.h"
59:
60: #include "time.h"
61:
62:
63: #include "sounds.h"
64:
65: #include "sounds_bin.h"
66:
67:
68:
69:
70: char text_output_line[TLT_NUM_TEXT_OUTPUT_LINE_TYPES][MAX_TEXT_OUTPUT_LINE_SIZE];
71:
72: float cursor_rotation = 0.0f;
73:
74: float cursor_intensity = DEFAULT_CURSOR_INTESITY;
75:
76: v16 touch_pos_x = 128;
77: v16 touch_pos_y = 96;
78:
79: int cursor_rotating = 1;
80:
81: unsigned char font_intensity = DEFAULT_FONT_INTENSITY;
82:
83: PrintConsole top_screen;
84: PrintConsole bottom_screen;
85: PrintConsole top_screen_x;
86: PrintConsole bottom_screen_x;
87:
88: Keyboard keyboard;
89:
90: float sqrt_three;
91:
92: unsigned char top_render_rate = DEFAULT_TOP_RENDER_RATE;
93: unsigned char bottom_render_rate = DEFAULT_BOTTOM_RENDER_RATE;
94: time_val next_top_render = {0, 0};
95: time_val next_bottom_render = {0, 0};
96:
97: int bg0, bg1, bg2, bg3;
98: int bgs0, bgs1, bgs2, bgs3;
99:
100:
101:
102:
103:
104:
105: void gzmcpc_init_3d(void) {
106:
107: int i, j;
108:
109: // initialize openGL-ish 3d engine
110: glInit();
111:
112: // initialize comb charge matrix
113: for (i = 0; i < COMB_WIDTH ; i++) {
114: for (j = 0; j < COMB_WIDTH ; j++) {
115: comb_charge[i][j] = 0;
116: }
117: }
118:
119: // single value lut
120: sqrt_three=sqrtf(3.0f);
121:
122: // enable antialiasing
123: glEnable(GL_ANTIALIAS);
124:
125: // background must be opaque for antialiasing
126: glClearColor(0,0,0,31);
127:
128: // background must have a unique polyID for antialiasing
129: glClearPolyID(63);
130:
131: glClearDepth(0x7FFF);
132:
133: // enable translucency
134: // XXX: haven't actually figured out nds opengl-ish alpha yet
135: // glEnable(GL_BLEND);
136:
137: // real OGL
138: //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
139: // nds OGL: use as a state, thus polys (and by that we mean tris or quads),
140: // will have this 0..15 alpha value
141: // POLY_CULL_NONE for now
142: glPolyFmt(POLY_ALPHA(15) | POLY_CULL_NONE);
143: //glPolyFmt(POLY_CULL_NONE);
144:
145: // use threshold below ?? need to read up...
146: // XXX: haven't actually figured out nds opengl-ish alpha yet
147: // glEnable(GL_ALPHA_TEST);
148: // 0..15 from nds, not sure ... 4 bits alpha?
149: // "sets minimum alpha value that will be used" ???
150: // glAlphaFunc(7);
151:
152: ////
153: //// begin effective window reshape/init function
154: ////
155:
156: // set viewport to the screen resolution
157: glViewport(0,0,255,191);
158:
159: glMatrixMode(GL_PROJECTION);
160:
161: glLoadIdentity();
162:
163: // set 3d perspective 'camera' type/parameters:
164: // fov(?), aspect-ratio, nearz, farz
165: // gluPerspective(70, 256.0 / 192.0, 0.1, 100);
166:
167: // set 2d orthographic 'camera' type/parameters:
168: glOrthof32(MODEL_XMIN,
169: MODEL_XMAX,
170: MODEL_YMIN,
171: MODEL_YMAX,
172: MODEL_ZMIN,
173: MODEL_ZMAX);
174:
175: glMatrixMode(GL_MODELVIEW);
176:
177: ////
178: //// end effective window reshape/init function
179: ////
180:
181: // nds openGL-ish: several nds specific attributes can be set
182: glPolyFmt(POLY_ALPHA(31) | POLY_CULL_NONE);
183:
184:
185: }
186:
187:
188:
189: void gzmcpc_display_main_splash_text(void) {
190:
191: // printf("\x1b[04;0H |----------------------------| ");
192: printf("\x1b[02;0H Guitar-ZyX::M.C.P. ");
193:
194: printf("\x1b[04;0H Master Control Program ");
195:
196: }
197:
198:
199:
200: void textout(text_output_line_type linetype, const char *fmt, ...) {
201:
202: //
203: // XXX would making these vars static help at all?
204: //
205:
206: va_list arg;
207:
208: char message_prefix[MAX_TEXT_OUTPUT_LINE_SIZE];
209: char temp_buffer[MAX_TEXT_OUTPUT_LINE_SIZE];
210:
211: // XXX is there a cleaner way to do this with an array (effective hash) initialization?
212: switch (linetype) {
213:
214: case TLT_DEBUG:
215: snprintf(message_prefix,
216: MAX_TEXT_OUTPUT_LINE_SIZE,
217: "DBG::");
218: break;
219: case TLT_ERROR:
220: snprintf(message_prefix,
221: MAX_TEXT_OUTPUT_LINE_SIZE,
222: "ERROR: ");
223: break;
224: case TLT_FX_MODE:
225: snprintf(message_prefix,
226: MAX_TEXT_OUTPUT_LINE_SIZE,
227: " F/X Mode /\\/ ");
228: break;
229: case TLT_MODE:
230: snprintf(message_prefix,
231: MAX_TEXT_OUTPUT_LINE_SIZE,
232: " MCP /\\/ ");
233: break;
234: case TLT_NETWORK_STATUS:
235: snprintf(message_prefix,
236: MAX_TEXT_OUTPUT_LINE_SIZE,
237: " network /\\/ ");
238: break;
239: case TLT_STATUS:
240: snprintf(message_prefix,
241: MAX_TEXT_OUTPUT_LINE_SIZE,
242: " status /\\/ ");
243: break;
244: case TLT_TOUCH_VAL:
245: snprintf(message_prefix,
246: MAX_TEXT_OUTPUT_LINE_SIZE,
247: " WhAmMyPaD /\\/ ");
248: break;
249: case TLT_TOUCH_VALXP:
250: snprintf(message_prefix,
251: MAX_TEXT_OUTPUT_LINE_SIZE,
252: " WPX// ");
253: break;
254: case TLT_TOUCH_VALYP:
255: snprintf(message_prefix,
256: MAX_TEXT_OUTPUT_LINE_SIZE,
257: " WPY// ");
258: break;
259: case TLT_NODEBUG:
260: //snprintf(message_prefix, MAX_TEXT_OUTPUT_LINE_SIZE"");
261: // nicer way to avoid warning??
262: message_prefix[0]='\0';
263: break;
264: case TLT_AESTHETIC:
265: //snprintf(message_prefix, MAX_TEXT_OUTPUT_LINE_SIZE"");
266: message_prefix[0]='\0';
267: break;
268: case TLT_NUM_TEXT_OUTPUT_LINE_TYPES:
269: // handling all enum values, even the utility one avoids compiler warning
270: break;
271: }
272:
273: va_start(arg, fmt);
274: vsnprintf(temp_buffer,
275: MAX_TEXT_OUTPUT_LINE_SIZE,
276: fmt,
277: arg);
278: va_end(arg);
279:
280: snprintf(text_output_line[linetype],
281: MAX_TEXT_OUTPUT_LINE_SIZE,
282: "%s%s",
283: message_prefix,
284: temp_buffer);
285:
286: debug_log_more("textout::%s\n",
287: text_output_line[linetype]);
288: }
289:
290:
291:
292: void render_textout(void) {
293:
294: int i;
295:
296: // 3 should work too (max 99 would be fine)
297: char rownum[4];
298:
299:
300: // clear the screen
301: printf("\x1b[2J");
302:
303: // the banner
304: gzmcpc_display_main_splash_text();
305:
306: for (i = 0 ; i < TLT_NUM_TEXT_OUTPUT_LINE_TYPES ; i++) {
307:
308: // XXX as mentioned elsewhere, this screams for a hash, but don't care now
309: switch (i) {
310: case TLT_DEBUG:
311: snprintf(rownum, 4, "%d", TS_DEBUG_ROW);
312: break;
313: case TLT_ERROR:
314: snprintf(rownum, 4, "%d", TS_ERROR_ROW);
315: break;
316: case TLT_FX_MODE:
317: snprintf(rownum, 4, "%d", TS_FX_MODE_ROW);
318: break;
319: case TLT_MODE:
320: snprintf(rownum, 4, "%d", TS_MODE_ROW);
321: break;
322: case TLT_NETWORK_STATUS:
323: snprintf(rownum, 4, "%d", TS_NETWORK_STATUS_ROW);
324: break;
325: case TLT_STATUS:
326: snprintf(rownum, 4, "%d", TS_STATUS_ROW);
327: break;
328: case TLT_TOUCH_VAL:
329: // XXX uh, yeah, inconsistent
330: snprintf(rownum, 4, "%d", TS_TOUCH_VAL_ROW);
331: break;
332: case TLT_TOUCH_VALXP:
333: snprintf(rownum, 4, "%d", TS_TOUCH_VALXP_ROW);
334: break;
335: case TLT_TOUCH_VALYP:
336: snprintf(rownum, 4, "%d", TS_TOUCH_VALYP_ROW);
337: break;
338: case TLT_NODEBUG:
339: snprintf(rownum, 4, "%d", TS_DEBUG_ROW);
340: break;
341: case TLT_AESTHETIC:
342: snprintf(rownum, 4, "%d", TS_AESTHETIC_ROW);
343: break;
344: }
345:
346: // XXX obsolete due to screen clear?
347: // note: with this, 345 wraps
348: // printf("\x1b[%s;0H12345678901234567890123456789012345", rownum);
349: printf("\x1b[%s;0H ", rownum);
350:
351: printf("\x1b[%s;0H%s", rownum, text_output_line[i]);
352:
353: } // end for iteration over linetypes
354:
355: }
356:
357:
358:
359: void init_modelview_matrix(void) {
360:
361: glMatrixMode(GL_MODELVIEW);
362:
363: glLoadIdentity();
364:
365: // for non-ortho 3d
366: // void gluLookAtf32(int32 eyex, int32 eyey, int32 eyez,
367: // int32 lookAtx, int32 lookAty, int32 lookAtz,
368: // int32 upx, int32 upy, int32 upz)
369:
370: // global (animation?) xforms if desired...
371:
372: // move the camera, currently at the default origin looking
373: // down the negative Z axis, backwards half the depth, thus
374: // will use z=0 for all z's in this 2d ortho world. Much of
375: // this is for explicit educational value (though don't believe
376: // any of it till initial code reviews are done)
377: //
378: // note: you wouldn't use divf32 instead of '/' unless you also
379: // used inttof32(-4). Fixed point sucks. Especially because
380: // f32(1.19.12) and v16(1.3.12) are both used in what seems
381: // ugly- i.e. f32 only for glOrtho, and v16 only for glVertex,
382: // thus confusing.
383: glTranslate3f32(0,
384: 0,
385: model_depth / -4);
386:
387:
388: // example simple time based animation camera xform
389: // - side to side motion, distance
390: //
391: // if (num_ticks.s % 2) {
392: // glTranslate3f32((model_width / 8) *
393: // (num_ticks.ms / 1000),
394: // 0,
395: // 0);
396: // } else {
397: // glTranslate3f32((model_width / 8) *
398: // (1 - (num_ticks.ms / 1000)),
399: // 0,
400: // 0);
401: // }
402:
403: }
404:
405: void
406: draw_hex(v16 centerx, v16 centery,
407: v16 z,
408: v16 radius,
409: float rotation,
410: uint8 centerr, uint8 centerg, uint8 centerb, uint8 centera,
411: uint8 outterr, uint8 outterg, uint8 outterb, uint8 outtera,
412: uint8 rimr, uint8 rimg, uint8 rimb, uint8 rima,
413: u16 intensity)
414: {
415:
416: uint8 act_center_r;
417: uint8 act_center_g;
418: uint8 act_center_b;
419: uint8 act_outter_r;
420: uint8 act_outter_g;
421: uint8 act_outter_b;
422:
423:
424: act_center_r = (uint8)((u16)centerr * (u16)(RGB15_TO_R5(intensity)) / 31);
425: act_center_g = (uint8)((u16)centerg * (u16)(RGB15_TO_G5(intensity)) / 31);
426: act_center_b = (uint8)((u16)centerb * (u16)(RGB15_TO_B5(intensity)) / 31);
427: act_outter_r = (uint8)((u16)outterr * (u16)(RGB15_TO_R5(intensity)) / 31);
428: act_outter_g = (uint8)((u16)outterg * (u16)(RGB15_TO_G5(intensity)) / 31);
429: act_outter_b = (uint8)((u16)outterb * (u16)(RGB15_TO_B5(intensity)) / 31);
430:
431: // save current modelview matrix
432: // note: I think I am following the global method of thinking
433: // rather than the local method, as described in the red
434: // book (p106 2nd ed).
435: glPushMatrix();
436:
437: // move to the desired center/location
438: glTranslate3f32(centerx,
439: centery,
440: z);
441:
442: // rotate around the z axis
443: glRotatef(rotation + 30.0f,0.0f,0.0f,1.0f);
444:
445:
446: #define P_A_X (0)
447: #define P_A_Y (0)
448: #define P_B_X (radius * -1)
449: #define P_B_Y (0)
450: #define P_C_X ((radius >> 1) * -1)
451: // XXX? single value lut?
452: #define P_C_Y (mulf32(P_C_X, sqrtf32(inttof32(3))))
453: #define P_D_X (P_C_X * -1)
454: #define P_D_Y (P_C_Y)
455: #define P_E_X (radius)
456: #define P_E_Y (0)
457: #define P_F_X P_D_X
458: #define P_F_Y (P_D_Y * -1)
459: #define P_G_X ((radius >> 1) * -1)
460: #define P_G_Y P_F_Y
461:
462: glBegin(GL_TRIANGLE_STRIP);
463:
464: glColor3b(act_outter_r, act_outter_g, act_outter_b);
465:
466: // f-g-a-b-c
467: glColor3b(act_outter_r, act_outter_g, act_outter_b);
468: glVertex3v16(P_F_X, P_F_Y, z);
469: glVertex3v16(P_G_X, P_G_Y, z);
470: glColor3b(act_center_r, act_center_g, act_center_b);
471: glVertex3v16(P_A_X, P_A_Y, z);
472: glColor3b(act_outter_r, act_outter_g, act_outter_b);
473: glVertex3v16(P_B_X, P_B_Y, z);
474: glVertex3v16(P_C_X, P_C_Y, z);
475:
476: // c-d-a-e-f
477: glColor3b(act_outter_r, act_outter_g, act_outter_b);
478: glVertex3v16(P_C_X, P_C_Y, z);
479: glVertex3v16(P_D_X, P_D_Y, z);
480: glColor3b(act_center_r, act_center_g, act_center_b);
481: glVertex3v16(P_A_X, P_A_Y, z);
482: glColor3b(act_outter_r, act_outter_g, act_outter_b);
483: glVertex3v16(P_E_X, P_E_Y, z);
484: glVertex3v16(P_F_X, P_F_Y, z);
485:
486: glEnd();
487: glPopMatrix(1);
488:
489: }
490:
491:
492: void
493: draw_simple_hex_grid(int width,
494: int height,
495: u16 intensity) {
496:
497: uint32 myx, myy;
498: int i, j;
499: v16 myz;
500: uint8 comb_center_r;
501: uint8 comb_center_g;
502: uint8 comb_center_b;
503: float anim_rotate;
504: float anim_fraction;
505:
506: //
507: // simple intro animation
508: //
509:
510: // crude method to stop z-fighting (no glDisable(GL_DEPTH_BUFFERING)?)
511: // i.e. below myz++ happens for each hex rendered
512: myz = floattov16(0.25f);
513:
514: // XXX: this is clumsy for now (i don't know or care what I'm doing)
515: glPushMatrix();
516: glTranslate3f32(model_width >> 1,
517: model_height >> 1,
518: 0);
519:
520: //
521: // intro animation
522: //
523: if (mode_ms < INTRO_ANIM_3DSPINZOOM_START) {
524: glScalef(INTRO_ANIM_3DSPINZOOM_SCALE_FACTOR * 1.0f,
525: INTRO_ANIM_3DSPINZOOM_SCALE_FACTOR * 1.0f,
526: 1.0f);
527: } else if (mode_ms < (INTRO_ANIM_3DSPINZOOM_START + INTRO_ANIM_3DSPINZOOM_DURATION)) {
528:
529: // a float from 0.0 to 1.0 representing how far along during the 3DSPINZOOM animation
530: anim_fraction = ((float)(mode_ms - INTRO_ANIM_3DSPINZOOM_START)) /
531: (INTRO_ANIM_3DSPINZOOM_DURATION * 1.0f);
532:
533: glScalef(((INTRO_ANIM_3DSPINZOOM_SCALE_FACTOR * 1.0f) -
534: anim_fraction *
535: (INTRO_ANIM_3DSPINZOOM_SCALE_FACTOR - 1.0f)),
536: ((INTRO_ANIM_3DSPINZOOM_SCALE_FACTOR * 1.0f) -
537: anim_fraction *
538: (INTRO_ANIM_3DSPINZOOM_SCALE_FACTOR - 1.0f)),
539: 1.0f);
540:
541: anim_rotate = anim_fraction * -360.0f;
542:
543: glRotatef(anim_rotate,
544: 0.0f,0.0f,1.0f);
545: }
546:
547:
548: comb_center_r = 13;
549: comb_center_g = 13;
550: comb_center_b = 13;
551:
552: comb_center_r = cstate_wpy << 1;
553: comb_center_g = 0;
554: comb_center_b = cstate_wpx << 1;
555:
556: // iterate over the 2D hex grid, prepping and drawing cells
557: for (j = 0 ; j < height ; j++) {
558: for (i = 0 ; i < width ; i++) {
559:
560: comb_center_g = comb_charge[i][j];
561:
562: // offset odd rows a bit to the right
563: if (j % 2) {
564: myx = MODEL_XMIN + ((MODEL_XMAX - MODEL_XMIN) * COMB_XBORDER / 100) +
565: (i * (((MODEL_XMAX - MODEL_XMIN) * (100 - 2 * COMB_XBORDER) / 100) / (width - 1)));
566: } else {
567: myx = MODEL_XMIN + ((MODEL_XMAX - MODEL_XMIN) * COMB_XBORDER / 100) +
568: ((2 * i + 1) * (((MODEL_XMAX - MODEL_XMIN) * (100 - 2 * COMB_XBORDER) / 100) / 2 / (width - 1)));
569: }
570:
571: // this formula is a hack, but achieves reasonable packing (for me, now)
572: // AHH, now I remember the old code. The reason for the border may
573: // have been part of this hack, i.e. with a different value
574: // than 10 / 6 below, and maybe better looking result (if
575: // my tired rememory is correct)
576: myy = MODEL_YMIN + ((MODEL_YMAX - MODEL_YMIN) * COMB_YBORDER / 100) +
577: (j * (((MODEL_YMAX - MODEL_YMIN) * (100 - 2 * COMB_YBORDER) / 100) / (height - 1)));
578:
579: // note: in the trenches pitfall of fixed point hazard
580: // myx = MODEL_XMIN + ((4 * i + 2) / 4) * (model_width / width);
581: // myy = MODEL_YMIN + ((4 * j + 2) / 4) * (model_height / height);
582: // note: this works
583: // myx = MODEL_XMIN + (model_width / width * (4 * i + 2)) / 4;
584: // myy = MODEL_YMIN + (model_height / height * (4 * j + 2)) / 4;
585:
586: // draw the comb cell
587: // note: ... / 2 * 2, when was / 2 * 4, looked pretty overlappn cool
588: draw_hex(myx - (model_width >> 1),
589: myy - (model_height >> 1),
590: myz++,
591: ((((MODEL_XMAX - MODEL_XMIN) +
592: (MODEL_YMAX - MODEL_YMIN)) / 2) /
593: (width + height) * debug_tweak_var / 6),
594: 0,
595: comb_center_r,
596: comb_center_g,
597: comb_center_b,
598: 13,
599: 13, 197, 13, 231,
600: 228, 228, 114, 253,
601: intensity);
602: }
603: }
604:
605: glPopMatrix(1);
606: }
607:
608:
609: void gzmcpc_flush_frame(void) {
610:
611: // XXX: maybe this should be made per mode
612: glFlush(0);
613:
614: // wait for vblank
615: swiWaitForVBlank();
616:
617: }
618: