1 //  ---------------------------------------------------------------------------
2 //
3 //  @file       TwDualGLUT.c
4 //  @brief      This example illustrates the use of AntTweakBar with multiple
5 //              windows. Here we are using GLUT to create a main window having
6 //              two sub-windows, each sub-window has a tweak bar.
7 //              This example extends the TwSimpleGLUT example.
8 //
9 //              AntTweakBar: http://anttweakbar.sourceforge.net/doc
10 //              OpenGL:      http://www.opengl.org
11 //              GLUT:        http://opengl.org/resources/libraries/glut
12 //
13 //  ---------------------------------------------------------------------------
14 
15 
16 #include <AntTweakBar.h>
17 
18 #include <stdlib.h>
19 #include <stdio.h>
20 #include <math.h>
21 
22 #if defined(_WIN32) || defined(_WIN64)
23 //  MiniGLUT.h is provided to avoid the need of having GLUT installed to
24 //  recompile this example. Do not use it in your own programs, better
25 //  install and use the actual GLUT library SDK.
26 #   define USE_MINI_GLUT
27 #endif
28 
29 #if defined(USE_MINI_GLUT)
30 #   include "../src/MiniGLUT.h"
31 #elif defined(_MACOSX)
32 #   include <GLUT/glut.h>
33 #else
34 #   include <GL/glut.h>
35 #endif
36 
37 
38 // This example displays one of the following shapes in each sub-window
39 typedef enum { SHAPE_TEAPOT=1, SHAPE_TORUS, SHAPE_CONE } Shape;
40 #define NUM_SHAPES 3
41 
42 typedef struct
43 {
44 	int   WinID;
45 	TwBar *Bar;
46 	Shape ObjectShape;
47 	float Zoom;
48 	float Rotation[4];
49 	int   AutoRotate;
50 	int   RotateTime;
51 	float RotateStart[4];
52 	float MatAmbient[4];
53 	float MatDiffuse[4];
54 	float LightMultiplier;
55 	float LightDirection[3];
56 } SubWindowData;
57 
58 SubWindowData g_SubWindowData[2];
59 
60 
61 // Routine to set a quaternion from a rotation axis and angle
62 // ( input axis = float[3] angle = float  output: quat = float[4] )
SetQuaternionFromAxisAngle(const float * axis,float angle,float * quat)63 void SetQuaternionFromAxisAngle(const float *axis, float angle, float *quat)
64 {
65     float sina2, norm;
66     sina2 = (float)sin(0.5f * angle);
67     norm = (float)sqrt(axis[0]*axis[0] + axis[1]*axis[1] + axis[2]*axis[2]);
68     quat[0] = sina2 * axis[0] / norm;
69     quat[1] = sina2 * axis[1] / norm;
70     quat[2] = sina2 * axis[2] / norm;
71     quat[3] = (float)cos(0.5f * angle);
72 }
73 
74 
75 // Routine to convert a quaternion to a 4x4 matrix
76 // ( input: quat = float[4]  output: mat = float[4*4] )
ConvertQuaternionToMatrix(const float * quat,float * mat)77 void ConvertQuaternionToMatrix(const float *quat, float *mat)
78 {
79     float yy2 = 2.0f * quat[1] * quat[1];
80     float xy2 = 2.0f * quat[0] * quat[1];
81     float xz2 = 2.0f * quat[0] * quat[2];
82     float yz2 = 2.0f * quat[1] * quat[2];
83     float zz2 = 2.0f * quat[2] * quat[2];
84     float wz2 = 2.0f * quat[3] * quat[2];
85     float wy2 = 2.0f * quat[3] * quat[1];
86     float wx2 = 2.0f * quat[3] * quat[0];
87     float xx2 = 2.0f * quat[0] * quat[0];
88     mat[0*4+0] = - yy2 - zz2 + 1.0f;
89     mat[0*4+1] = xy2 + wz2;
90     mat[0*4+2] = xz2 - wy2;
91     mat[0*4+3] = 0;
92     mat[1*4+0] = xy2 - wz2;
93     mat[1*4+1] = - xx2 - zz2 + 1.0f;
94     mat[1*4+2] = yz2 + wx2;
95     mat[1*4+3] = 0;
96     mat[2*4+0] = xz2 + wy2;
97     mat[2*4+1] = yz2 - wx2;
98     mat[2*4+2] = - xx2 - yy2 + 1.0f;
99     mat[2*4+3] = 0;
100     mat[3*4+0] = mat[3*4+1] = mat[3*4+2] = 0;
101     mat[3*4+3] = 1;
102 }
103 
104 
105 // Routine to multiply 2 quaternions (ie, compose rotations)
106 // ( input q1 = float[4] q2 = float[4]  output: qout = float[4] )
MultiplyQuaternions(const float * q1,const float * q2,float * qout)107 void MultiplyQuaternions(const float *q1, const float *q2, float *qout)
108 {
109     float qr[4];
110 	qr[0] = q1[3]*q2[0] + q1[0]*q2[3] + q1[1]*q2[2] - q1[2]*q2[1];
111 	qr[1] = q1[3]*q2[1] + q1[1]*q2[3] + q1[2]*q2[0] - q1[0]*q2[2];
112 	qr[2] = q1[3]*q2[2] + q1[2]*q2[3] + q1[0]*q2[1] - q1[1]*q2[0];
113 	qr[3]  = q1[3]*q2[3] - (q1[0]*q2[0] + q1[1]*q2[1] + q1[2]*q2[2]);
114     qout[0] = qr[0]; qout[1] = qr[1]; qout[2] = qr[2]; qout[3] = qr[3];
115 }
116 
117 
118 // Return elapsed time in milliseconds
GetTimeMs()119 int GetTimeMs()
120 {
121 #if !defined(_WIN32)
122     return glutGet(GLUT_ELAPSED_TIME);
123 #else
124     // glutGet(GLUT_ELAPSED_TIME) seems buggy on Windows
125     return (int)GetTickCount();
126 #endif
127 }
128 
129 
130 // Find the current WindowData
GetCurrentSubWindowData()131 SubWindowData *GetCurrentSubWindowData()
132 {
133     int i, currWinID;
134     currWinID = glutGetWindow();
135     for (i = 0; i < 2; i++)
136         if (g_SubWindowData[i].WinID == currWinID)
137             return &g_SubWindowData[i];
138     return NULL;
139 }
140 
141 
142 // Callback function called by GLUT to render the main window content
DisplayMainWindow(void)143 void DisplayMainWindow(void)
144 {
145     // Clear frame buffer
146     glClearColor(1, 0.8f, 0.4f, 1);
147     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
148 
149     // Present frame buffer
150     glutSwapBuffers();
151 }
152 
153 
154 // Callback function called by GLUT when the main window size has changed
ReshapeMainWindow(int width,int height)155 void ReshapeMainWindow(int width, int height)
156 {
157     if (width > 32 && height > 32)
158     {
159 	    glutSetWindow(g_SubWindowData[0].WinID);
160 	    glutPositionWindow(8, 8);
161 	    glutReshapeWindow(width/2-16, height-16);
162 
163 	    glutSetWindow(g_SubWindowData[1].WinID);
164 	    glutPositionWindow(width/2+8, 8);
165 	    glutReshapeWindow(width/2-16, height-16);
166     }
167 }
168 
169 
170 // Callback function called by GLUT to render sub-window content
DisplaySubWindow(void)171 void DisplaySubWindow(void)
172 {
173     float v[4]; // will be used to set light parameters
174     float mat[4*4]; // rotation matrix
175     SubWindowData *win;
176 
177     win = GetCurrentSubWindowData();
178     if (win == NULL) return;
179 
180     // Tell AntTweakBar which is the current window
181     TwSetCurrentWindow(win->WinID);
182 
183     // Clear frame buffer
184     glClearColor(0, 0, 0, 1);
185     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
186 
187     glEnable(GL_DEPTH_TEST);
188     glDisable(GL_CULL_FACE);
189     glEnable(GL_NORMALIZE);
190 
191     // Set light
192     glEnable(GL_LIGHTING);
193     glEnable(GL_LIGHT0);
194     v[0] = v[1] = v[2] = win->LightMultiplier*0.4f; v[3] = 1.0f;
195     glLightfv(GL_LIGHT0, GL_AMBIENT, v);
196     v[0] = v[1] = v[2] = win->LightMultiplier*0.8f; v[3] = 1.0f;
197     glLightfv(GL_LIGHT0, GL_DIFFUSE, v);
198     v[0] = -win->LightDirection[0]; v[1] = -win->LightDirection[1]; v[2] = -win->LightDirection[2]; v[3] = 0.0f;
199     glLightfv(GL_LIGHT0, GL_POSITION, v);
200 
201     // Set material
202     glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, win->MatAmbient);
203     glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, win->MatDiffuse);
204 
205     // Rotate and draw shape
206     glPushMatrix();
207     glTranslatef(0.5f, -0.3f, 0.0f);
208     if( win->AutoRotate )
209     {
210         float axis[3] = { 0, 1, 0 };
211         float angle = (float)(GetTimeMs()-win->RotateTime)/1000.0f;
212         float quat[4];
213         SetQuaternionFromAxisAngle(axis, angle, quat);
214         MultiplyQuaternions(win->RotateStart, quat, win->Rotation);
215     }
216     ConvertQuaternionToMatrix(win->Rotation, mat);
217     glMultMatrixf(mat);
218     glScalef(win->Zoom, win->Zoom, win->Zoom);
219     glCallList(win->ObjectShape);
220     glPopMatrix();
221 
222     // Draw tweak bars
223     TwDraw();
224 
225     // Present frame buffer
226     glutSwapBuffers();
227 
228     // Recall Display at next frame
229     glutPostRedisplay();
230 }
231 
232 
233 // Callback function called by GLUT when sub-window size has changed
ReshapeSubWindow(int width,int height)234 void ReshapeSubWindow(int width, int height)
235 {
236 	SubWindowData *win;
237 
238     win = GetCurrentSubWindowData();
239     if (win == NULL) return;
240 
241     // Set OpenGL viewport and camera
242     glViewport(0, 0, width, height);
243     glMatrixMode(GL_PROJECTION);
244     glLoadIdentity();
245     gluPerspective(40, (double)width/height, 1, 10);
246     glMatrixMode(GL_MODELVIEW);
247     glLoadIdentity();
248     gluLookAt(0,0,5, 0,0,0, 0,1,0);
249     glTranslatef(0, 0.6f, -1);
250 
251     // Send the new window size to AntTweakBar
252     TwSetCurrentWindow(win->WinID);
253     TwWindowSize(width, height);
254 }
255 
256 
257 // Function called at exit
Terminate(void)258 void Terminate(void)
259 {
260     int i;
261 	for (i=0; i<2; i++) {
262 		glutSetWindow(g_SubWindowData[i].WinID);
263     	glDeleteLists(SHAPE_TEAPOT, NUM_SHAPES);
264 	}
265     TwTerminate();
266 }
267 
268 
269 //  Callback function called when the 'AutoRotate' variable value of the tweak bar has changed
SetAutoRotateCB(const void * value,void * clientData)270 void TW_CALL SetAutoRotateCB(const void *value, void *clientData)
271 {
272 	SubWindowData *win;
273 
274 	win = (SubWindowData *)clientData;
275     win->AutoRotate = *(const int *)value; // copy value to win->AutoRotate
276 
277     if (win->AutoRotate != 0)
278     {
279         // init rotation
280         win->RotateTime = GetTimeMs();
281         win->RotateStart[0] = win->Rotation[0];
282         win->RotateStart[1] = win->Rotation[1];
283         win->RotateStart[2] = win->Rotation[2];
284         win->RotateStart[3] = win->Rotation[3];
285     }
286 
287     // make Rotation variable read-only or read-write
288     TwSetCurrentWindow(win->WinID);
289     TwSetParam(win->Bar, "ObjRotation", "readonly", TW_PARAM_INT32, 1, &win->AutoRotate);
290 }
291 
292 
293 //  Callback function called by the tweak bar to get the 'AutoRotate' value
GetAutoRotateCB(void * value,void * clientData)294 void TW_CALL GetAutoRotateCB(void *value, void *clientData)
295 {
296 	SubWindowData *win;
297 
298 	win = (SubWindowData *)clientData;
299     *(int *)value = win->AutoRotate; // copy win->AutoRotate to value
300 }
301 
302 
303 // Mouse Button event callbacks
MouseButtonCB(int glutButton,int glutState,int mouseX,int mouseY)304 int MouseButtonCB(int glutButton, int glutState, int mouseX, int mouseY)
305 {
306 	TwSetCurrentWindow(glutGetWindow());
307 	return TwEventMouseButtonGLUT(glutButton,glutState,mouseX,mouseY);
308 }
309 
310 
311 // Mouse Motion event callbacks
MouseMotionCB(int mouseX,int mouseY)312 int MouseMotionCB(int mouseX, int mouseY)
313 {
314 	TwSetCurrentWindow(glutGetWindow());
315 	return TwEventMouseMotionGLUT(mouseX,mouseY);
316 }
317 
318 
319 // Keyboard event callbacks
KeyboardCB(unsigned char glutKey,int mouseX,int mouseY)320 int KeyboardCB(unsigned char glutKey, int mouseX, int mouseY)
321 {
322 	TwSetCurrentWindow(glutGetWindow());
323 	return TwEventKeyboardGLUT(glutKey,mouseX,mouseY);
324 }
325 
326 
327 // Special key event callbacks
SpecialKeyCB(int glutKey,int mouseX,int mouseY)328 int SpecialKeyCB(int glutKey, int mouseX, int mouseY)
329 {
330 	TwSetCurrentWindow(glutGetWindow());
331 	return TwEventSpecialGLUT(glutKey,mouseX,mouseY);
332 }
333 
334 
335 // Setup new sub-window
SetupSubWindow(int subWinIdx)336 void SetupSubWindow(int subWinIdx)
337 {
338     float axis[] = { 0.7f, 0.7f, 0.0f }; // initial model rotation
339     float angle = 0.8f;
340     SubWindowData *win;
341 
342     win = &g_SubWindowData[subWinIdx];
343     win->ObjectShape = (subWinIdx == 0) ? SHAPE_TEAPOT : SHAPE_TORUS;
344 	win->Zoom = 1;
345 	win->AutoRotate = (subWinIdx == 0);
346     win->MatAmbient[0] = (subWinIdx == 1) ? 0.0f : 0.5f;; win->MatAmbient[1] = win->MatAmbient[2] = 0.2f; win->MatAmbient[3] = 1;
347     win->MatDiffuse[0] = (subWinIdx == 1) ? 0.0f : 1.0f; win->MatDiffuse[1] = 1; win->MatDiffuse[2] = 0; win->MatDiffuse[3] = 1;
348 	win->LightMultiplier = 1;
349     win->LightDirection[0] = win->LightDirection[1] = win->LightDirection[2] = -0.57735f;
350     win->RotateTime = GetTimeMs();
351     SetQuaternionFromAxisAngle(axis, angle, win->Rotation);
352     SetQuaternionFromAxisAngle(axis, angle, win->RotateStart);
353 
354 	glutSetWindow(win->WinID);
355     // Set GLUT callbacks
356     glutDisplayFunc(DisplaySubWindow);
357     glutReshapeFunc(ReshapeSubWindow);
358     // Set GLUT event callbacks
359     // - Register mouse button events callback
360     glutMouseFunc((GLUTmousebuttonfun)MouseButtonCB);
361     // - Register mouse motion events callback
362     glutMotionFunc((GLUTmousemotionfun)MouseMotionCB);
363     // - Register mouse "passive" motion events (same as Motion)
364     glutPassiveMotionFunc((GLUTmousemotionfun)MouseMotionCB);
365     // - Register keyboard events callback
366     glutKeyboardFunc((GLUTkeyboardfun)KeyboardCB);
367     // - Register special key events callback
368     glutSpecialFunc((GLUTspecialfun)SpecialKeyCB);
369     // - Send 'glutGetModifers' function pointer to AntTweakBar;
370     //   required because the GLUT key event functions do not report key modifiers states.
371     TwGLUTModifiersFunc(glutGetModifiers);
372 
373     // Create some 3D objects (stored in display lists)
374     glNewList(SHAPE_TEAPOT, GL_COMPILE);
375     glutSolidTeapot(1.0);
376     glEndList();
377     glNewList(SHAPE_TORUS, GL_COMPILE);
378     glutSolidTorus(0.3, 1.0, 16, 32);
379     glEndList();
380     glNewList(SHAPE_CONE, GL_COMPILE);
381     glutSolidCone(1.0, 1.5, 64, 4);
382     glEndList();
383 
384     // Declare this window as current for AntTweakBar.
385     // Here, this will create a new AntTweakBar context for this window,
386     // which will be identified by the number WinID.
387     TwSetCurrentWindow(win->WinID);
388 
389     // Create a tweak bar
390     win->Bar = TwNewBar("TweakBar");
391     TwDefine(" GLOBAL help='This example shows how to use AntTweakBar with multiple windows.' "); // Message added to the help bar.
392     TwDefine(" TweakBar size='200 400' color='96 216 224' "); // change default tweak bar size and color
393 
394     // Add 'win->Zoom' to 'bar': this is a modifable (RW) variable of type TW_TYPE_FLOAT. Its key shortcuts are [z] and [Z].
395     TwAddVarRW(win->Bar, "Zoom", TW_TYPE_FLOAT, &win->Zoom,
396                " min=0.01 max=2.5 step=0.01 keyIncr=z keyDecr=Z help='Scale the object (1=original size).' ");
397 
398     // Add 'win->Rotation' to 'bar': this is a variable of type TW_TYPE_QUAT4F which defines the object's orientation
399     TwAddVarRW(win->Bar, "ObjRotation", TW_TYPE_QUAT4F, &win->Rotation,
400                " label='Object rotation' open help='Change the object orientation.' ");
401 
402     // Add callback to toggle auto-rotate mode (callback functions are defined above).
403     TwAddVarCB(win->Bar, "AutoRotate", TW_TYPE_BOOL32, SetAutoRotateCB, GetAutoRotateCB, win,
404                " label='Auto-rotate' key=space help='Toggle auto-rotate mode.' ");
405 
406     // Add 'win->LightMultiplier' to 'bar': this is a variable of type TW_TYPE_FLOAT. Its key shortcuts are [+] and [-].
407     TwAddVarRW(win->Bar, "Multiplier", TW_TYPE_FLOAT, &win->LightMultiplier,
408                " label='Light booster' min=0.1 max=4 step=0.02 keyIncr='+' keyDecr='-' help='Increase/decrease the light power.' ");
409 
410     // Add 'win->LightDirection' to 'bar': this is a variable of type TW_TYPE_DIR3F which defines the light direction
411     TwAddVarRW(win->Bar, "LightDir", TW_TYPE_DIR3F, &win->LightDirection,
412                " label='Light direction' open help='Change the light direction.' ");
413 
414     // Add 'win->MatAmbient' to 'bar': this is a variable of type TW_TYPE_COLOR3F (3 floats color, alpha is ignored)
415     // and is inserted into a group named 'Material'.
416     TwAddVarRW(win->Bar, "Ambient", TW_TYPE_COLOR3F, &win->MatAmbient, " group='Material' ");
417 
418     // Add 'win->MatDiffuse' to 'bar': this is a variable of type TW_TYPE_COLOR3F (3 floats color, alpha is ignored)
419     // and is inserted into group 'Material'.
420     TwAddVarRW(win->Bar, "Diffuse", TW_TYPE_COLOR3F, &win->MatDiffuse, " group='Material' ");
421 
422     // Add the enum variable 'win->ObjectShape' to 'bar'
423     // (before adding an enum variable, its enum type must be declared to AntTweakBar as follow)
424     {
425         // ShapeEV associates Shape enum values with labels that will be displayed instead of enum values
426         TwEnumVal shapeEV[NUM_SHAPES] = { {SHAPE_TEAPOT, "Teapot"}, {SHAPE_TORUS, "Torus"}, {SHAPE_CONE, "Cone"} };
427         // Create a type for the enum shapeEV
428         TwType shapeType = TwDefineEnum("ShapeType", shapeEV, NUM_SHAPES);
429         // add 'win->CurrentShape' to 'bar': this is a variable of type ShapeType. Its key shortcuts are [<] and [>].
430         TwAddVarRW(win->Bar, "Shape", shapeType, &win->ObjectShape, " keyIncr='<' keyDecr='>' help='Change object shape.' ");
431     }
432 }
433 
434 
435 // Main
main(int argc,char * argv[])436 int main(int argc, char *argv[])
437 {
438     int mainWinID;
439 
440     // Initialize GLUT
441     glutInit(&argc, argv);
442     glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
443     glutInitWindowSize(960, 480);
444     mainWinID = glutCreateWindow("AntTweakBar multi-window example");
445     glutCreateMenu(NULL);
446 	glutDisplayFunc(DisplayMainWindow);
447 	glutReshapeFunc(ReshapeMainWindow);
448 
449     // Initialize AntTweakBar
450     TwInit(TW_OPENGL, NULL);
451 
452 	// Create two sub-windows
453     g_SubWindowData[0].WinID = glutCreateSubWindow(mainWinID, 8, 8, 480, 464);
454 	SetupSubWindow(0);
455     g_SubWindowData[1].WinID = glutCreateSubWindow(mainWinID, 488, 8, 464, 464);
456     SetupSubWindow(1);
457 
458     atexit(Terminate);  // Called after glutMainLoop ends
459 
460     // Call the GLUT main loop
461     glutMainLoop();
462 
463     return 0;
464 }
465 
466