Updated README.md with build instructions
[hair] / src / main.cc
1 #include <GL/glew.h>
2 #include <GL/glut.h>
3
4 #include <assert.h>
5 #include <stdlib.h>
6 #include <stdio.h>
7 #include <string>
8
9 #include <gmath/gmath.h>
10
11 #include "mesh.h"
12 #include "hair.h"
13 #include "object.h"
14
15 #define MAX_NUM_SPAWNS 1600
16 #define THRESH 0.5
17
18 static bool init();
19 static void cleanup();
20 static void display();
21 static void reshape(int x, int y);
22 static void keydown(unsigned char key, int x, int y);
23 static void keyup(unsigned char key, int x, int y);
24 static void mouse(int bn, int st, int x, int y);
25 static void motion(int x, int y);
26 static void idle();
27 static void sball_motion(int x, int y, int z);
28 static void sball_rotate(int x, int y, int z);
29 static void sball_button(int bn, int st);
30
31 static unsigned int gen_grad_tex(int sz, const Vec3 &c0, const Vec3 &c1);
32 static void draw_text(const char *text, int x, int y, float sz, const Vec3 &color);
33 static void update_sball_matrix();
34
35 static std::vector<Mesh*> meshes;
36 static Mesh *mesh_head;
37 static Hair hair;
38
39 static unsigned int grad_tex;
40
41 static int win_width, win_height;
42 static float cam_theta, cam_phi = 25, cam_dist = 8;
43 static float head_rz, head_rx; /* rot angles x, z axis */
44 static Mat4 head_xform;
45 //static CollSphere coll_sphere; /* sphere used for collision detection */
46
47 // spaceball (6dof control) state
48 static Vec3 sball_pos;
49 static Quat sball_rot;
50 static Mat4 sball_xform;
51 static bool sball_update_pending;
52
53
54 int main(int argc, char **argv)
55 {
56         glutInit(&argc, argv);
57         glutInitWindowSize(800, 600);
58         glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
59         glutCreateWindow("hair test");
60
61         /* for the keydown, keyup functions to work */
62         glutSetKeyRepeat(GLUT_KEY_REPEAT_OFF);
63
64         glutDisplayFunc(display);
65         glutReshapeFunc(reshape);
66         glutKeyboardFunc(keydown);
67         glutKeyboardUpFunc(keyup);
68         glutMouseFunc(mouse);
69         glutMotionFunc(motion);
70         glutIdleFunc(idle);
71         glutSpaceballMotionFunc(sball_motion);
72         glutSpaceballRotateFunc(sball_rotate);
73         glutSpaceballButtonFunc(sball_button);
74
75         if(!init()) {
76                 return 1;
77         }
78         atexit(cleanup);
79
80         glutMainLoop();
81         return 0;
82 }
83
84 static bool init()
85 {
86         glewInit();
87
88         grad_tex = gen_grad_tex(32, Vec3(0, 0, 1), Vec3(0, 1, 0));
89
90         glEnable(GL_DEPTH_TEST);
91         glEnable(GL_CULL_FACE);
92 //      glEnable(GL_COLOR_MATERIAL);
93
94         glEnable(GL_LIGHTING);
95         glEnable(GL_LIGHT0);
96
97         glClearColor(0.5, 0.5, 0.5, 1);
98         meshes = load_meshes("data/head.fbx");
99         if (meshes.empty()) {
100                 fprintf(stderr, "Failed to load mesh.\n");
101                 return false;
102         }
103
104         for(size_t i=0; i<meshes.size(); i++) {
105                 meshes[i]->calc_bbox();
106 /*
107                 Vec3 v0 = meshes[i]->bbox.v0;
108                 Vec3 v1 = meshes[i]->bbox.v1;
109
110                 printf("mesh: %s\n", meshes[i]->name.c_str());
111                 printf("AABB mesh %d: v0: (%f, %f, %f) v1: (%f, %f, %f)\n",
112                                 (int)i, v0.x, v0.y, v0.z, v1.x, v1.y, v1.z);
113 */
114                 meshes[i]->update_vbo(MESH_ALL);
115 /*
116                 printf("num vertices: %d num triangles: %d\n",
117                                 (int)meshes[i]->vertices.size(),
118                                 (int)meshes[i]->indices.size() / 3);
119 */
120                 if(meshes[i]->name == "head") {
121                         mesh_head = meshes[i];
122                 }
123         }
124         if(!mesh_head) {
125                 fprintf(stderr, "Failed to find the head mesh.\n");
126                 return false;
127         }
128
129 //      coll_sphere.radius = 1.0;
130 //      coll_sphere.center = Vec3(0, 0.6, 0.53);
131
132         if(!hair.init(mesh_head, MAX_NUM_SPAWNS, THRESH)) {
133                 fprintf(stderr, "Failed to initialize hair\n");
134                 return false;
135         }
136
137 //      hair.add_collider(&coll_sphere);
138
139         return true;
140 }
141
142 static void cleanup()
143 {
144         for(size_t i=0; i<meshes.size(); i++) {
145                 delete meshes[i];
146         }
147         glDeleteTextures(1, &grad_tex);
148 }
149
150 static void display()
151 {
152         static unsigned long prev_time;
153         unsigned long msec = glutGet(GLUT_ELAPSED_TIME);
154         float dt = (float)(msec - prev_time) / 1000.0;
155         prev_time = msec;
156
157         if(sball_update_pending) {
158                 update_sball_matrix();
159                 sball_update_pending = false;
160         }
161
162         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
163
164         head_xform = Mat4::identity;
165         head_xform.rotate_x(gph::deg_to_rad(head_rx));
166         head_xform.rotate_z(-gph::deg_to_rad(head_rz));
167         head_xform *= sball_xform;
168
169         glMatrixMode(GL_MODELVIEW);
170         glLoadIdentity();
171         glTranslatef(0, 0, -cam_dist);
172         glRotatef(cam_phi, 1, 0, 0);
173         glRotatef(cam_theta, 0, 1, 0);
174         /* multiplying with the head rot matrix */
175         glPushMatrix();
176         glMultMatrixf(head_xform[0]);
177 /*
178         glPushAttrib(GL_LINE_BIT);
179         glLineWidth(1);
180         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
181 */
182         for(size_t i=0; i<meshes.size(); i++) {
183                 if(!meshes[i]->mtl.tex || meshes[i]->mtl.tex_opaque)
184                         meshes[i]->draw();
185         }
186         for(size_t i=0; i<meshes.size(); i++) {
187                 if(meshes[i]->mtl.tex && !meshes[i]->mtl.tex_opaque)
188                         meshes[i]->draw();
189         }
190 /*
191         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
192         glPopAttrib();
193 */
194
195         glPopMatrix();
196
197         hair.set_transform(head_xform);
198         hair.update(dt);
199         hair.draw();
200
201 /*
202         glPushAttrib(GL_ENABLE_BIT);
203         glDisable(GL_DEPTH_TEST);
204         glDisable(GL_LIGHTING);
205         glBegin(GL_POINTS);
206         for (int i=0; i<500; i++) {
207                 Vec3 p;
208                 p.x = (float)rand() / RAND_MAX * 8 - 4;
209                 p.y = (float)rand() / RAND_MAX * 4;
210                 p.z = 0;
211
212                 Vec3 tmp = inverse(head_xform) * p;
213                 if(coll_sphere.contains(tmp)) {
214                         glColor3f(1, 0, 0);
215                 }
216                 else glColor3f(0, 1, 0);
217
218                 glVertex3f(p.x, p.y, p.z);
219         }
220         glEnd();
221         glPopAttrib();
222 */
223         float plane[4] = {
224                 0, 0, 0.5 / 350, 0.5
225         };
226
227         glPushMatrix();
228         glRotatef(90, 1, 0, 0);
229
230         glPushAttrib(GL_ENABLE_BIT);
231         glDisable(GL_LIGHTING);
232         glEnable(GL_TEXTURE_1D);
233         glBindTexture(GL_TEXTURE_1D, grad_tex);
234         glFrontFace(GL_CW);
235         glEnable(GL_TEXTURE_GEN_S);
236         glTexGenfv(GL_S, GL_OBJECT_PLANE, plane);
237         glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
238         glColor3f(1, 1, 1);
239
240         glDepthMask(0);
241
242         glutSolidSphere(350, 16, 8);
243         glDisable(GL_TEXTURE_1D);
244
245         glColor3f(0.2, 0.2, 0.2);
246         glutWireSphere(350, 32, 16);
247
248         glDepthMask(1);
249         glFrontFace(GL_CCW);
250         glPopAttrib();
251
252         glPopMatrix();
253
254         draw_text("Hold h to move the head with the mouse!", 15, 15, 0.0015 * win_width, Vec3(0, 0, 0));
255         draw_text("Hold h to move the head with the mouse!", 12, 17, 0.0015 * win_width, Vec3(0.8, 0.5, 0.7));
256
257         glutSwapBuffers();
258         assert(glGetError() == GL_NO_ERROR);
259 }
260
261 static void reshape(int x, int y)
262 {
263         glViewport(0, 0, x, y);
264         win_width = x;
265         win_height = y;
266
267         glMatrixMode(GL_PROJECTION);
268         glLoadIdentity();
269         gluPerspective(50.0, (float)x / (float)y, 0.5, 500.0);
270 }
271
272 static bool hpressed;
273 static void keydown(unsigned char key, int /*x*/, int /*y*/)
274 {
275         switch(key) {
276         case 'h':
277         case 'H':
278                 hpressed = true;
279                 break;
280         case 27:
281                 exit(0);
282         default:
283                 break;
284         }
285 }
286
287 static void keyup(unsigned char key, int /*x*/, int /*y*/)
288 {
289         switch(key) {
290         case 'h':
291         case 'H':
292                 hpressed = false;
293                 break;
294         default:
295                 break;
296         }
297 }
298
299 bool bnstate[8];
300 int prev_x, prev_y;
301
302 static void mouse(int bn, int st, int x, int y)
303 {
304         bnstate[bn] = st == GLUT_DOWN;
305         prev_x = x;
306         prev_y = y;
307 }
308
309 static void motion(int x, int y)
310 {
311         int dx = x - prev_x;
312         int dy = y - prev_y;
313         prev_x = x;
314         prev_y = y;
315
316         if(!dx && !dy) return;
317
318         if(hpressed) {
319                 if(bnstate[0]) {
320                         head_rz += dx * 0.5;
321                         head_rx += dy * 0.5;
322
323                         if(head_rx < -45) head_rx = -45;
324                         if(head_rx > 45) head_rx = 45;
325
326                         if(head_rz < -90) head_rz = -90;
327                         if(head_rz > 90) head_rz = 30;
328                 }
329         }
330         else {
331                 if(bnstate[0]) {
332                         cam_theta += dx * 0.5;
333                         cam_phi += dy * 0.5;
334
335                         if(cam_phi < -90) cam_phi = -90;
336                         if(cam_phi > 90) cam_phi = 90;
337                 }
338                 if(bnstate[2]) {
339                         cam_dist += dy * 0.1;
340                         if(cam_dist < 0) cam_dist = 0;
341                 }
342         }
343 }
344
345 static void idle()
346 {
347         glutPostRedisplay();
348 }
349
350 static void sball_motion(int x, int y, int z)
351 {
352         sball_pos.x += (float)x * 0.001;
353         sball_pos.y += (float)y * 0.001;
354         sball_pos.z -= (float)z * 0.001;
355         sball_update_pending = true;
356 }
357
358 static void sball_rotate(int x, int y, int z)
359 {
360         Vec3 axis = Vec3(x, y, -z);
361         float axis_len = length(axis);
362         if(axis_len > 0.0f) {
363                 Quat q;
364                 q.set_rotation(axis / axis_len, axis_len * 0.001);
365                 sball_rot = q * sball_rot;
366                 sball_update_pending = true;
367         }
368 }
369
370 static void sball_button(int bn, int st)
371 {
372         if(st != GLUT_DOWN) return;
373
374         sball_pos = Vec3(0, 0, 0);
375         sball_rot = Quat::identity;
376         sball_xform = Mat4::identity;
377 }
378
379 static unsigned int gen_grad_tex(int sz, const Vec3 &c0, const Vec3 &c1)
380 {
381         unsigned char *pixels = new unsigned char[sz * 3];
382         for(int i=0; i<sz; i++) {
383                 float t = (float)i / (float)(sz - 1);
384                 Vec3 color = c0 + (c1 - c0) * t;
385                 pixels[i * 3] = color.x * 255;
386                 pixels[i * 3 + 1] = color.y * 255;
387                 pixels[i * 3 + 2] = color.z * 255;
388         }
389
390         unsigned int tex;
391         glGenTextures(1, &tex);
392         glBindTexture(GL_TEXTURE_1D, tex);
393
394         glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
395         glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
396         glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
397
398         glTexImage1D(GL_TEXTURE_1D, 0, GL_RGB, sz, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels);
399
400         delete [] pixels;
401
402         return tex;
403 }
404
405 static void draw_text(const char *text, int x, int y, float sz, const Vec3 &color)
406 {
407         glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT | GL_POINT_BIT);
408
409         glDisable(GL_DEPTH_TEST);
410         glDisable(GL_LIGHTING);
411         glLineWidth(3);
412         glPointSize(3);
413
414         glMatrixMode(GL_PROJECTION);
415         glPushMatrix();
416         glLoadIdentity();
417         glOrtho(0, win_width, 0, win_height, -1, 1);
418
419         glMatrixMode(GL_MODELVIEW);
420         glPushMatrix();
421         glLoadIdentity();
422         glTranslatef(x, y, 0);
423         glScalef(0.1 * sz, 0.1 * sz, 1);
424
425         glColor3f(color.x, color.y, color.z);
426         while(*text != '\0') {
427                 glutStrokeCharacter(GLUT_STROKE_MONO_ROMAN, *text);
428                 text++;
429         }
430
431         glMatrixMode(GL_PROJECTION);
432         glPopMatrix();
433
434         glMatrixMode(GL_MODELVIEW);
435         glPopMatrix();
436
437         glPopAttrib();
438 }
439
440 static void update_sball_matrix()
441 {
442         Mat4 rot = sball_rot.calc_matrix();
443         //rot.transpose();
444
445         Mat4 trans;
446         trans.translation(sball_pos);
447
448         sball_xform = rot * trans;
449 }