1 /* Public domain */
2 /*
3 * This program demonstrates the use of the AG_GLView widget.
4 *
5 * AG_GLView provides a low-level GL context. Under OpenGL-based Agar
6 * display drivers, all AG_GLView does is to save and restore the relevant
7 * GL matrices and states around calls to Draw().
8 *
9 * For high-level scene graph functionality, check out the Agar-based
10 * FreeSG library at http://freesg.org/.
11 */
12
13 #include "agartest.h"
14
15 #ifdef HAVE_OPENGL
16
17 #include <agar/gui/opengl.h>
18
19 #include <stdio.h>
20 #include <string.h>
21 #include <stdlib.h>
22 #include <math.h>
23
24 const char *primitiveNames[] = { "Cube", "Sphere", NULL };
25 const char *shadingNames[] = { "Flat Shading", "Smooth Shading", NULL };
26
27 typedef struct {
28 AG_TestInstance _inherit;
29 enum { CUBE, SPHERE } primitive;
30 enum { FLATSHADING, SMOOTHSHADING } shading;
31 GLfloat spin;
32 GLdouble vz;
33 GLfloat ambient[4];
34 GLfloat diffuse[4];
35 GLfloat specular[4];
36 int wireframe;
37 } MyTestInstance;
38
39 static GLdouble isoVtx[12][3] = {
40 #define X .525731112119133606
41 #define Z .850650808352039932
42 {-X, 0.0, Z}, {X, 0.0, Z}, {-X, 0.0, -Z}, {X, 0.0, -Z},
43 {0.0, Z, X}, {0.0, Z, -X}, {0.0, -Z, X}, {0.0, -Z, -X},
44 {Z, X, 0.0}, {-Z, X, 0.0}, {Z, -X, 0.0}, {-Z, -X, 0.0}
45 };
46 static GLuint isoInd[20][3] = {
47 {0,4,1}, {0,9,4}, {9,5,4}, {4,5,8}, {4,8,1},
48 {8,10,1}, {8,3,10}, {5,3,8}, {5,2,3}, {2,7,3},
49 {7,10,3}, {7,6,10}, {7,11,6}, {11,0,6}, {0,1,6},
50 {6,1,10}, {9,0,11}, {9,11,2}, {9,2,5}, {7,2,11} };
51
52
53 /* Widget resize callback function. */
54 static void
MyScaleFunction(AG_Event * event)55 MyScaleFunction(AG_Event *event)
56 {
57 GLdouble xMin, xMax, yMin, yMax;
58
59 glLoadIdentity();
60
61 /* Set a 60 degrees field of view with 1.0 aspect ratio. */
62 yMax = 0.01*tan(0.523598f);
63 yMin = -yMax;
64 xMin = yMin;
65 xMax = yMax;
66 glFrustum(xMin, xMax, yMin, yMax, 0.01, 100.0);
67 }
68
69 static __inline__ void
Norm(GLdouble * a)70 Norm(GLdouble *a)
71 {
72 GLdouble d = sqrt(a[0]*a[0] + a[1]*a[1] + a[2]*a[2]);
73
74 a[0] /= d;
75 a[1] /= d;
76 a[2] /= d;
77 }
78
79 static void
DrawTriangle(GLdouble * a,GLdouble * b,GLdouble * c,int div,float r)80 DrawTriangle(GLdouble *a, GLdouble *b, GLdouble *c, int div, float r)
81 {
82 if (div <= 0) {
83 glNormal3dv(a); glVertex3d(a[0]*r, a[1]*r, a[2]*r);
84 glNormal3dv(b); glVertex3d(b[0]*r, b[1]*r, b[2]*r);
85 glNormal3dv(c); glVertex3d(c[0]*r, c[1]*r, c[2]*r);
86 } else {
87 GLdouble ab[3], ac[3], bc[3];
88 int i;
89
90 for (i = 0; i < 3; i++) {
91 ab[i] = (a[i]+b[i])/2.0;
92 ac[i] = (a[i]+c[i])/2.0;
93 bc[i] = (b[i]+c[i])/2.0;
94 }
95 Norm(ab);
96 Norm(ac);
97 Norm(bc);
98 DrawTriangle(a, ab, ac, div-1, r);
99 DrawTriangle(b, bc, ab, div-1, r);
100 DrawTriangle(c, ac, bc, div-1, r);
101 DrawTriangle(ab, bc, ac, div-1, r);
102 }
103 }
104
105 /* Render the test object. */
106 static void
MyDrawFunction(AG_Event * event)107 MyDrawFunction(AG_Event *event)
108 {
109 MyTestInstance *ti = AG_PTR(1);
110 GLfloat pos[4];
111 int i;
112
113 glLoadIdentity();
114 glPushAttrib(GL_POLYGON_BIT|GL_LIGHTING_BIT|GL_DEPTH_BUFFER_BIT);
115
116 glEnable(GL_LIGHTING);
117 glEnable(GL_LIGHT0);
118 glEnable(GL_LIGHT1);
119 glEnable(GL_DEPTH_TEST);
120
121 glPolygonMode(GL_FRONT_AND_BACK, ti->wireframe ? GL_LINE : GL_POLYGON);
122 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, ti->ambient);
123 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, ti->diffuse);
124 glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, ti->specular);
125 glShadeModel(ti->shading == FLATSHADING ? GL_FLAT : GL_SMOOTH);
126
127 pos[0] = 10.0f;
128 pos[1] = 10.0f;
129 pos[2] = 0.0f;
130 pos[3] = 1.0f;
131 glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION, 10.0f);
132 glLightfv(GL_LIGHT1, GL_POSITION, pos);
133
134 pos[1] = -10.0f;
135 pos[2] = 10.0f;
136 glLightfv(GL_LIGHT0, GL_POSITION, pos);
137 glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 10.0f);
138
139 glLightfv(GL_LIGHT1, GL_AMBIENT, ti->ambient);
140 glLightfv(GL_LIGHT1, GL_DIFFUSE, ti->diffuse);
141 glLightfv(GL_LIGHT1, GL_SPECULAR, ti->specular);
142
143 glPushMatrix();
144 glTranslated(0.0, 0.0, ti->vz);
145 glRotatef(ti->spin,0.0f,1.0f,0.0f);
146 glRotatef(ti->spin,1.0f,1.0f,1.0f);
147
148 switch (ti->primitive) {
149 case CUBE:
150 glBegin(GL_QUADS);
151 glColor3f(1.0f, 0.0f, 0.0f);
152 glVertex3f( 1.0f, 1.0f,-1.0f);
153 glVertex3f(-1.0f, 1.0f,-1.0f);
154 glVertex3f(-1.0f, 1.0f, 1.0f);
155 glVertex3f( 1.0f, 1.0f, 1.0f);
156 glVertex3f( 1.0f,-1.0f, 1.0f);
157 glVertex3f(-1.0f,-1.0f, 1.0f);
158 glVertex3f(-1.0f,-1.0f,-1.0f);
159 glVertex3f( 1.0f,-1.0f,-1.0f);
160 glVertex3f( 1.0f, 1.0f, 1.0f);
161 glVertex3f(-1.0f, 1.0f, 1.0f);
162 glVertex3f(-1.0f,-1.0f, 1.0f);
163 glVertex3f( 1.0f,-1.0f, 1.0f);
164 glVertex3f( 1.0f,-1.0f,-1.0f);
165 glVertex3f(-1.0f,-1.0f,-1.0f);
166 glVertex3f(-1.0f, 1.0f,-1.0f);
167 glVertex3f( 1.0f, 1.0f,-1.0f);
168 glVertex3f(-1.0f, 1.0f, 1.0f);
169 glVertex3f(-1.0f, 1.0f,-1.0f);
170 glVertex3f(-1.0f,-1.0f,-1.0f);
171 glVertex3f(-1.0f,-1.0f, 1.0f);
172 glVertex3f( 1.0f, 1.0f,-1.0f);
173 glVertex3f( 1.0f, 1.0f, 1.0f);
174 glVertex3f( 1.0f,-1.0f, 1.0f);
175 glVertex3f( 1.0f,-1.0f,-1.0f);
176 glEnd();
177 break;
178 case SPHERE:
179 glBegin(GL_TRIANGLES);
180 for (i = 0; i < 20; i++) {
181 DrawTriangle(isoVtx[isoInd[i][0]],
182 isoVtx[isoInd[i][1]],
183 isoVtx[isoInd[i][2]],
184 2, 1.0f);
185 }
186 glEnd();
187 }
188
189 glPopMatrix();
190 glPopAttrib();
191 }
192
193 static Uint32
UpdateRotation(AG_Timer * to,AG_Event * event)194 UpdateRotation(AG_Timer *to, AG_Event *event)
195 {
196 MyTestInstance *ti = AG_PTR(1);
197
198 if (++ti->spin > 360.0f) { ti->spin -= 360.0f; }
199 return (to->ival);
200 }
201
202 /*
203 * Overlay callback function. This type of callback is useful for rendering
204 * things such as status text on top of the OpenGL context.
205 */
206 static void
MyOverlayFunction(AG_Event * event)207 MyOverlayFunction(AG_Event *event)
208 {
209 AG_GLView *glv = AG_SELF();
210 MyTestInstance *ti = AG_PTR(1);
211 AG_Surface *myText;
212
213 /* Render a text string using the font engine. */
214 AG_PushTextState();
215 AG_TextColorRGB(255, 255, 125);
216 myText = AG_TextRenderf("Zoom using mouse wheel\n"
217 "Spin = %.0f degrees, z = %.02f",
218 ti->spin, ti->vz);
219 AG_PopTextState();
220
221 /*
222 * Blit the text at the lower left of the widget. Note that the
223 * AG_WidgetMapSurface() interface is much more efficient at this
224 * (hardware->hardware copy), unless the text changes frequently.
225 */
226 if (myText != NULL) {
227 AG_WidgetBlit(glv, myText,
228 0,
229 AGWIDGET(glv)->h - agTextFontHeight*2 - 5);
230 AG_SurfaceFree(myText);
231 }
232 }
233
234 /* Control the Z using the mouse wheel. */
235 static void
ButtonDown(AG_Event * event)236 ButtonDown(AG_Event *event)
237 {
238 MyTestInstance *ti = AG_PTR(1);
239 int button = AG_INT(2);
240
241 switch (button) {
242 case AG_MOUSE_WHEELUP:
243 ti->vz -= 0.1;
244 break;
245 case AG_MOUSE_WHEELDOWN:
246 ti->vz += 0.1;
247 break;
248 }
249 }
250
251 static int
Init(void * obj)252 Init(void *obj)
253 {
254 MyTestInstance *ti = obj;
255 GLfloat amb[4] = { 0.5f, 0.5f, 0.5f, 1.0f };
256 GLfloat dif[4] = { 0.0f, 1.0f, 0.5f, 1.0f };
257 GLfloat spe[4] = { 1.0f, 1.0f, 0.0f, 1.0f };
258 int i;
259
260 ti->primitive = SPHERE;
261 ti->shading = FLATSHADING;
262 ti->spin = 0.0f;
263 ti->vz = -5.0;
264 for (i = 0; i < 4; i++) {
265 ti->ambient[i] = amb[i];
266 ti->diffuse[i] = dif[i];
267 ti->specular[i] = spe[i];
268 }
269 ti->wireframe = 0;
270 return (0);
271 }
272
273 static int
TestGUI(void * obj,AG_Window * win)274 TestGUI(void *obj, AG_Window *win)
275 {
276 MyTestInstance *ti = obj;
277 AG_GLView *glv;
278 AG_Box *hb;
279 AG_HSVPal *pal;
280
281 hb = AG_BoxNewHoriz(win, AG_BOX_EXPAND);
282 {
283 AG_Pane *pa;
284 AG_Notebook *nb, *nbColor;
285 AG_NotebookTab *ntab;
286 int i;
287
288 pa = AG_PaneNewHoriz(hb, AG_PANE_EXPAND|AG_PANE_DIV1FILL);
289 AG_PaneResizeAction(pa, AG_PANE_EXPAND_DIV1);
290 nb = AG_NotebookNew(pa->div[0], AG_NOTEBOOK_EXPAND);
291
292 for (i = 0; i < 2; i++) {
293 ntab = AG_NotebookAdd(nb, "Test tab", AG_BOX_VERT);
294
295 /* Create the AG_GLView widget. */
296 glv = AG_GLViewNew(ntab, AG_GLVIEW_EXPAND);
297
298 /* Set a periodic redraw at 60fps. */
299 AG_RedrawOnTick(glv, 1000/60);
300
301 /* Set up our callback functions. */
302 AG_GLViewScaleFn(glv, MyScaleFunction, NULL);
303 AG_GLViewDrawFn(glv, MyDrawFunction, "%p", ti);
304 AG_GLViewOverlayFn(glv, MyOverlayFunction, "%p", ti);
305 AG_GLViewButtondownFn(glv, ButtonDown, "%p", ti);
306
307 /* Update the rotation 30 times per second. */
308 AG_AddTimerAuto(win, 1000/30,
309 UpdateRotation, "%p", ti);
310 }
311
312 /* Edit ambient and diffuse color components. */
313 nbColor = AG_NotebookNew(pa->div[1], AG_NOTEBOOK_EXPAND);
314 {
315 ntab = AG_NotebookAdd(nbColor, "Amb", AG_BOX_VERT);
316 pal = AG_HSVPalNew(ntab,
317 AG_HSVPAL_NOALPHA|AG_HSVPAL_EXPAND);
318 AG_BindFloat(pal, "RGBAv", ti->ambient);
319
320 ntab = AG_NotebookAdd(nbColor, "Dif", AG_BOX_VERT);
321 pal = AG_HSVPalNew(ntab,
322 AG_HSVPAL_NOALPHA|AG_HSVPAL_EXPAND);
323 AG_BindFloat(pal, "RGBAv", ti->diffuse);
324
325 ntab = AG_NotebookAdd(nbColor, "Spe", AG_BOX_VERT);
326 pal = AG_HSVPalNew(ntab,
327 AG_HSVPAL_NOALPHA|AG_HSVPAL_EXPAND);
328 AG_BindFloat(pal, "RGBAv", ti->specular);
329 }
330 }
331 hb = AG_BoxNewHoriz(win, AG_BOX_FRAME|AG_BOX_HFILL);
332 {
333 AG_RadioNewInt(hb, 0, primitiveNames, (void *)&ti->primitive);
334 AG_SeparatorNewVert(hb);
335 AG_RadioNewInt(hb, 0, shadingNames, (void *)&ti->shading);
336 AG_SeparatorNewVert(hb);
337 AG_ButtonNewInt(hb, AG_BUTTON_STICKY, "Wireframe Mode",
338 &ti->wireframe);
339 }
340 return (0);
341 }
342
343 #endif /* HAVE_OPENGL */
344
345 const AG_TestCase glviewTest = {
346 "glView",
347 "Test the AG_GLView(3) widget (OpenGL required)",
348 "1.4.2",
349 AG_TEST_OPENGL,
350 #ifdef HAVE_OPENGL
351 sizeof(MyTestInstance),
352 Init,
353 NULL,
354 NULL, /* test */
355 TestGUI,
356 #else
357 sizeof(AG_TestInstance),
358 NULL, /* init */
359 NULL, /* destroy */
360 NULL, /* test */
361 NULL, /* testGUI */
362 #endif
363 NULL /* bench */
364 };
365