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