1 : |
jhr |
1880 |
/*! \file main.c
|
2 : |
|
|
*
|
3 : |
|
|
* \author John Reppy
|
4 : |
|
|
*
|
5 : |
|
|
* Main program for Diderot ISO2d animation.
|
6 : |
|
|
*/
|
7 : |
|
|
|
8 : |
|
|
/*
|
9 : |
|
|
* COPYRIGHT (c) 2012 The Diderot Project (http://diderot-language.cs.uchicago.edu)
|
10 : |
|
|
* All rights reserved.
|
11 : |
|
|
*/
|
12 : |
|
|
|
13 : |
|
|
#include <AntTweakBar.h>
|
14 : |
jhr |
1882 |
#include "GL/glfw.h"
|
15 : |
jhr |
1884 |
#include "util.h"
|
16 : |
|
|
#include "load-png.h"
|
17 : |
jhr |
1918 |
#include "iso2d.h"
|
18 : |
jhr |
1920 |
#include <unistd.h>
|
19 : |
jhr |
1880 |
|
20 : |
|
|
/* pixel dimensions of output */
|
21 : |
jhr |
1920 |
#define WIDTH 990
|
22 : |
|
|
#define HEIGHT 990
|
23 : |
jhr |
1880 |
|
24 : |
jhr |
1920 |
/* world bounds */
|
25 : |
|
|
#define WRLD_MIN_X 0.0
|
26 : |
|
|
#define WRLD_MIN_Y 0.0
|
27 : |
|
|
#define WRLD_MAX_X 1.0
|
28 : |
|
|
#define WRLD_MAX_Y 1.0
|
29 : |
|
|
#define WRLD_CENTER_X 0.5
|
30 : |
|
|
#define WRLD_CENTER_Y 0.5
|
31 : |
|
|
|
32 : |
jhr |
1918 |
#define STEP_SIZE 1
|
33 : |
jhr |
1920 |
#define SLEEP_MS 250
|
34 : |
jhr |
1918 |
|
35 : |
jhr |
1884 |
static inline void CheckError ()
|
36 : |
|
|
{
|
37 : |
|
|
GLenum errCode;
|
38 : |
|
|
if ((errCode = glGetError()) != GL_NO_ERROR) {
|
39 : |
|
|
fprintf(stderr, "OpenGL error: %s\n", gluErrorString(errCode));
|
40 : |
|
|
exit (1);
|
41 : |
|
|
}
|
42 : |
|
|
}
|
43 : |
|
|
|
44 : |
jhr |
1880 |
/***** Globals for viewing, etc. *****/
|
45 : |
|
|
|
46 : |
|
|
Nrrd *Diderot; // input image nrrd
|
47 : |
|
|
unsigned int Width; // view window width
|
48 : |
|
|
unsigned int Height; // view window height
|
49 : |
jhr |
1884 |
GLuint ImageTexture; // Texture ID of background image
|
50 : |
jhr |
1921 |
float WrldOrigin[2];
|
51 : |
|
|
float WrldDim[2];
|
52 : |
jhr |
1880 |
|
53 : |
jhr |
1882 |
typedef struct {
|
54 : |
|
|
TwBar *ctls;
|
55 : |
|
|
unsigned int running;
|
56 : |
|
|
} State_t;
|
57 : |
|
|
|
58 : |
jhr |
1880 |
// Callback function called by GLUT to render screen
|
59 : |
jhr |
1913 |
void Draw (Nrrd *positions)
|
60 : |
jhr |
1880 |
{
|
61 : |
|
|
// Clear frame buffer
|
62 : |
|
|
glClearColor (0, 0, 0, 1);
|
63 : |
|
|
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
64 : |
|
|
|
65 : |
jhr |
1920 |
glDisable(GL_DEPTH_TEST);
|
66 : |
jhr |
1880 |
glDisable(GL_CULL_FACE);
|
67 : |
|
|
|
68 : |
|
|
// Draw background
|
69 : |
jhr |
1884 |
glEnable (GL_TEXTURE_2D);
|
70 : |
jhr |
1920 |
glActiveTexture (GL_TEXTURE0);
|
71 : |
|
|
glBindTexture (GL_TEXTURE_2D, ImageTexture);
|
72 : |
jhr |
1884 |
glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
73 : |
jhr |
1882 |
glBegin (GL_QUADS);
|
74 : |
jhr |
1884 |
glColor3f (0.1, 0.1, 0.1);
|
75 : |
jhr |
1882 |
glTexCoord2f (0.0, 0.0);
|
76 : |
jhr |
1921 |
glVertex2f (WrldOrigin[0], WrldOrigin[1]);
|
77 : |
jhr |
1882 |
glTexCoord2f (1.0, 0.0);
|
78 : |
jhr |
1921 |
glVertex2f (WrldOrigin[0]+WrldDim[0], WrldOrigin[1]);
|
79 : |
jhr |
1882 |
glTexCoord2f (1.0, 1.0);
|
80 : |
jhr |
1921 |
glVertex2f (WrldOrigin[0]+WrldDim[0], WrldOrigin[1]+WrldDim[1]);
|
81 : |
jhr |
1882 |
glTexCoord2f (0.0, 1.0);
|
82 : |
jhr |
1921 |
glVertex2f (WrldOrigin[0], WrldOrigin[1]+WrldDim[1]);
|
83 : |
jhr |
1882 |
glEnd();
|
84 : |
jhr |
1884 |
glDisable (GL_TEXTURE_2D);
|
85 : |
|
|
CheckError ();
|
86 : |
jhr |
1880 |
|
87 : |
jhr |
1920 |
// Draw current particle state
|
88 : |
|
|
glEnable (GL_POINT_SMOOTH);
|
89 : |
|
|
// glPointSize (2.0);
|
90 : |
|
|
// it would be more efficient to do this as a single buffer, but it is fast enough
|
91 : |
|
|
float *p = (float *)(positions->data);
|
92 : |
|
|
unsigned int nPoints = positions->axis[1].size;
|
93 : |
|
|
glBegin (GL_POINTS);
|
94 : |
|
|
// first draw some points at known locations for testing purposes
|
95 : |
|
|
// glColor3f (1.0, 0.0, 0.0);
|
96 : |
|
|
// glVertex2f (WRLD_MIN_X, WRLD_MIN_Y);
|
97 : |
|
|
// glVertex2f (WRLD_MAX_X, WRLD_MIN_Y);
|
98 : |
|
|
// glVertex2f (WRLD_MAX_X, WRLD_MAX_Y);
|
99 : |
|
|
// glVertex2f (WRLD_MIN_X, WRLD_MAX_Y);
|
100 : |
|
|
glColor3f (0.2, 1.0, 0.2);
|
101 : |
|
|
for (int i = 0; i < nPoints; i++) {
|
102 : |
|
|
glVertex2fv(p);
|
103 : |
|
|
p += 2;
|
104 : |
|
|
}
|
105 : |
|
|
glEnd ();
|
106 : |
jhr |
1880 |
|
107 : |
|
|
// Draw tweak bars
|
108 : |
|
|
TwDraw();
|
109 : |
|
|
|
110 : |
|
|
// Present frame buffer
|
111 : |
jhr |
1882 |
glfwSwapBuffers();
|
112 : |
jhr |
1880 |
}
|
113 : |
|
|
|
114 : |
jhr |
1882 |
// Callback function called by GLFW when window size changes
|
115 : |
|
|
void GLFWCALL WindowSizeCB (int width, int height)
|
116 : |
jhr |
1880 |
{
|
117 : |
jhr |
1920 |
printf("window size = %d x %d\n", width, height);
|
118 : |
jhr |
1880 |
// Set OpenGL viewport and camera
|
119 : |
jhr |
1882 |
glViewport(0, 0, width, height);
|
120 : |
|
|
|
121 : |
jhr |
1920 |
glMatrixMode (GL_PROJECTION);
|
122 : |
|
|
glLoadIdentity ();
|
123 : |
|
|
double pad = 0.05*(WRLD_MAX_X - WRLD_MIN_X); // add 10% to world dimensions
|
124 : |
|
|
gluOrtho2D (
|
125 : |
|
|
WRLD_MIN_X - pad, WRLD_MAX_X + pad,
|
126 : |
|
|
WRLD_MAX_Y + pad, WRLD_MIN_Y - pad);
|
127 : |
|
|
|
128 : |
jhr |
1880 |
glMatrixMode (GL_MODELVIEW);
|
129 : |
|
|
glLoadIdentity ();
|
130 : |
jhr |
1882 |
gluLookAt (
|
131 : |
jhr |
1920 |
// WRLD_CENTER_X, WRLD_CENTER_Y, 1.0,
|
132 : |
|
|
0.0, 0.0, 1.0,
|
133 : |
|
|
// WRLD_CENTER_X, WRLD_CENTER_Y, 0.0,
|
134 : |
|
|
0.0, 0.0, 0.0,
|
135 : |
jhr |
1882 |
0.0, 1.0, 0.0);
|
136 : |
jhr |
1880 |
|
137 : |
jhr |
1882 |
// Send the new window size to AntTweakBar
|
138 : |
|
|
TwWindowSize(width, height);
|
139 : |
jhr |
1880 |
|
140 : |
|
|
// remember width and height
|
141 : |
|
|
Width = width;
|
142 : |
|
|
Height = height;
|
143 : |
|
|
|
144 : |
|
|
}
|
145 : |
|
|
|
146 : |
|
|
// initialize a texture from a grayscale Nrrd file
|
147 : |
jhr |
1884 |
void InitTexture ()
|
148 : |
jhr |
1880 |
{
|
149 : |
jhr |
1920 |
Image2D_t *img = LoadImage ("data/ddro-tx.png", true, GRAY_IMAGE);
|
150 : |
jhr |
1884 |
if (img == 0) {
|
151 : |
|
|
fprintf (stderr, "unable to load texture file\n");
|
152 : |
jhr |
1880 |
exit (1);
|
153 : |
|
|
}
|
154 : |
|
|
|
155 : |
|
|
// generate the TeXID
|
156 : |
|
|
GLuint texId;
|
157 : |
|
|
glGenTextures (1, &texId);
|
158 : |
|
|
|
159 : |
|
|
// load the texture data
|
160 : |
|
|
glBindTexture (GL_TEXTURE_2D, texId);
|
161 : |
jhr |
1884 |
int sts = TexImage (img);
|
162 : |
|
|
if (sts != GL_NO_ERROR) {
|
163 : |
|
|
fprintf(stderr, "error loading texture\n");
|
164 : |
|
|
exit (1);
|
165 : |
|
|
}
|
166 : |
jhr |
1920 |
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
167 : |
|
|
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
168 : |
jhr |
1880 |
|
169 : |
jhr |
1884 |
FreeImage (img);
|
170 : |
|
|
|
171 : |
jhr |
1880 |
ImageTexture = texId;
|
172 : |
|
|
}
|
173 : |
|
|
|
174 : |
jhr |
1882 |
void TW_CALL StartButtonCB (void *data)
|
175 : |
|
|
{
|
176 : |
|
|
State_t *st = (State_t *)data;
|
177 : |
|
|
|
178 : |
|
|
if (st->running == 0) {
|
179 : |
|
|
st->running = 1;
|
180 : |
|
|
TwSetParam (st->ctls, "StartButton", "label", TW_PARAM_CSTRING, 1, "Stop");
|
181 : |
|
|
}
|
182 : |
|
|
else {
|
183 : |
|
|
st->running = 0;
|
184 : |
|
|
TwSetParam (st->ctls, "StartButton", "label", TW_PARAM_CSTRING, 1, "Start");
|
185 : |
|
|
}
|
186 : |
|
|
}
|
187 : |
|
|
|
188 : |
jhr |
1918 |
void GetData (ISO_World_t *wrld, Nrrd *nData)
|
189 : |
|
|
{
|
190 : |
|
|
// get snapshot of state
|
191 : |
|
|
if (ISO_Snapshot_pos (wrld, nData)) {
|
192 : |
|
|
// error
|
193 : |
|
|
fprintf(stderr, "Error getting nrrd data\n");
|
194 : |
|
|
exit(1);
|
195 : |
|
|
}
|
196 : |
|
|
}
|
197 : |
|
|
|
198 : |
jhr |
1880 |
int main (int argc, const char **argv)
|
199 : |
|
|
{
|
200 : |
jhr |
1882 |
State_t state = { .running = 0 };
|
201 : |
jhr |
1880 |
|
202 : |
jhr |
1882 |
// Initialize GLFW
|
203 : |
|
|
if (0 == glfwInit()) {
|
204 : |
|
|
// An error occured
|
205 : |
|
|
fprintf(stderr, "GLFW initialization failed\n");
|
206 : |
|
|
return 1;
|
207 : |
|
|
}
|
208 : |
jhr |
1880 |
|
209 : |
jhr |
1882 |
GLFWvidmode mode;
|
210 : |
|
|
glfwGetDesktopMode (&mode);
|
211 : |
|
|
if (0 == glfwOpenWindow(WIDTH, HEIGHT, mode.RedBits, mode.GreenBits, mode.BlueBits, 0, 0, 0, GLFW_WINDOW) ) {
|
212 : |
|
|
// A fatal error occured
|
213 : |
|
|
fprintf(stderr, "Cannot open GLFW window\n");
|
214 : |
|
|
glfwTerminate();
|
215 : |
|
|
return 1;
|
216 : |
|
|
}
|
217 : |
|
|
glfwEnable(GLFW_MOUSE_CURSOR);
|
218 : |
|
|
glfwEnable(GLFW_KEY_REPEAT);
|
219 : |
|
|
glfwSetWindowTitle("Diderot Iso2D Animation");
|
220 : |
|
|
|
221 : |
jhr |
1880 |
// Initialize AntTweakBar
|
222 : |
|
|
TwInit (TW_OPENGL, 0);
|
223 : |
|
|
|
224 : |
jhr |
1882 |
// create a tweakbar
|
225 : |
|
|
TwBar *tweakBar = TwNewBar("Controls");
|
226 : |
|
|
state.ctls = tweakBar;
|
227 : |
|
|
unsigned int NumSteps = 0;
|
228 : |
|
|
TwAddVarRO (tweakBar, "NumSteps", TW_TYPE_UINT32, &NumSteps, " label='# of steps' ");
|
229 : |
|
|
TwAddButton (tweakBar, "StartButton", StartButtonCB, &state, " label='Start' ");
|
230 : |
jhr |
1880 |
|
231 : |
jhr |
1882 |
// Set GLFW event callbacks
|
232 : |
|
|
glfwSetWindowSizeCallback (WindowSizeCB);
|
233 : |
|
|
glfwSetMouseButtonCallback ((GLFWmousebuttonfun)TwEventMouseButtonGLFW); // redirect to AntTweakBar
|
234 : |
|
|
glfwSetMousePosCallback ((GLFWmouseposfun)TwEventMousePosGLFW); // redirect to AntTweakBar
|
235 : |
|
|
glfwSetMouseWheelCallback ((GLFWmousewheelfun)TwEventMouseWheelGLFW); // redirect to AntTweakBar
|
236 : |
|
|
glfwSetKeyCallback ((GLFWkeyfun)TwEventKeyGLFW); // redirect to AntTweakBar
|
237 : |
|
|
glfwSetCharCallback ((GLFWcharfun)TwEventCharGLFW); // redirect to AntTweakBar
|
238 : |
|
|
|
239 : |
jhr |
1884 |
// load Diderot image for background texture
|
240 : |
|
|
InitTexture ();
|
241 : |
jhr |
1880 |
|
242 : |
jhr |
1921 |
// setup the world coords.
|
243 : |
|
|
// FIXME: we should get these from the nrrd file, but for now I'm hard coding them
|
244 : |
|
|
WrldOrigin[0] = -0.0184375;
|
245 : |
|
|
WrldOrigin[1] = -0.0184375;
|
246 : |
|
|
WrldDim[0] = 0.013125 * 80.0;
|
247 : |
|
|
WrldDim[1] = 0.013125 * 80.0;
|
248 : |
|
|
|
249 : |
jhr |
1918 |
// initialize the Diderot program
|
250 : |
jhr |
1880 |
ISO_World_t *wrld = ISO_Init ();
|
251 : |
|
|
ISO_Initially (wrld);
|
252 : |
|
|
|
253 : |
jhr |
1918 |
// nrrd for getting computational state
|
254 : |
jhr |
1913 |
Nrrd *nData = nrrdNew();
|
255 : |
jhr |
1880 |
|
256 : |
jhr |
1918 |
// get and render initial state
|
257 : |
|
|
GetData (wrld, nData);
|
258 : |
jhr |
1913 |
Draw (nData);
|
259 : |
jhr |
1880 |
|
260 : |
jhr |
1918 |
// Main loop (repeated while window is not closed and [ESC] is not pressed)
|
261 : |
|
|
while (glfwGetWindowParam(GLFW_OPENED) && !glfwGetKey(GLFW_KEY_ESC)) {
|
262 : |
|
|
if (state.running != 0) {
|
263 : |
|
|
while ((state.running != 0) && (ISO_NumActive(wrld) > 0)) {
|
264 : |
|
|
// step the computation
|
265 : |
|
|
NumSteps += ISO_Run (wrld, STEP_SIZE);
|
266 : |
|
|
// get and render the state
|
267 : |
|
|
GetData (wrld, nData);
|
268 : |
|
|
Draw (nData);
|
269 : |
jhr |
1920 |
usleep (SLEEP_MS * 1000); // sleep for a bit
|
270 : |
jhr |
1918 |
}
|
271 : |
|
|
if (ISO_NumActive(wrld) == 0) {
|
272 : |
|
|
// get and render final state
|
273 : |
|
|
if (ISO_OutputGet_pos (wrld, nData)) {
|
274 : |
|
|
// error
|
275 : |
|
|
fprintf(stderr, "Error getting nrrd data\n");
|
276 : |
|
|
exit(1);
|
277 : |
|
|
}
|
278 : |
|
|
Draw (nData);
|
279 : |
|
|
}
|
280 : |
|
|
}
|
281 : |
|
|
else {
|
282 : |
|
|
glfwWaitEvents();
|
283 : |
|
|
Draw(nData);
|
284 : |
|
|
}
|
285 : |
|
|
}
|
286 : |
|
|
|
287 : |
|
|
// shutdown the world
|
288 : |
jhr |
1880 |
ISO_Shutdown (wrld);
|
289 : |
|
|
|
290 : |
jhr |
1882 |
// Terminate AntTweakBar and GLFW
|
291 : |
|
|
TwTerminate();
|
292 : |
|
|
glfwTerminate();
|
293 : |
|
|
|
294 : |
jhr |
1880 |
return 0;
|
295 : |
|
|
}
|