Mercurial > hg > forks > gldragon
comparison glxdragon.cpp @ 6:4d6fec8f0c64
Implement optional support for vertex/fragment shaders. Cleanups.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Sun, 27 Oct 2019 22:09:38 +0200 |
parents | 5dcae4dddcd9 |
children | 95dd5417e7de |
comparison
equal
deleted
inserted
replaced
5:5dcae4dddcd9 | 6:4d6fec8f0c64 |
---|---|
29 // | 29 // |
30 | 30 |
31 #include <SDL.h> | 31 #include <SDL.h> |
32 #include <SDL_opengl.h> | 32 #include <SDL_opengl.h> |
33 #include <GL/glu.h> | 33 #include <GL/glu.h> |
34 #include <GL/glext.h> | |
34 | 35 |
35 #include <iostream> | 36 #include <iostream> |
36 #include <sstream> | |
37 #include <fstream> | 37 #include <fstream> |
38 #include <stdexcept> | |
39 #include <string> | 38 #include <string> |
40 #include <vector> | 39 #include <vector> |
41 #include <algorithm> | |
42 #include <cstdio> | 40 #include <cstdio> |
43 #include <ctime> | 41 #include <ctime> |
44 | 42 |
45 | 43 |
46 #define SET_FRAMES (180 * 2) | 44 #define SET_FRAMES (180 * 2) |
47 | 45 |
48 | 46 |
49 SDL_Window *s_window = NULL; | 47 bool opt_shaders = false; |
50 SDL_GLContext s_context; | 48 SDL_Window *sdl_window = NULL; |
49 SDL_GLContext sdl_glctx = NULL; | |
51 | 50 |
52 | 51 |
53 struct Mesh | 52 struct Mesh |
54 { | 53 { |
55 int nvertices, nfaces; | 54 int nvertices, nfaces; |
56 std::vector<float> vertices; | 55 std::vector<float> vertices; |
57 std::vector<unsigned> faces; | 56 std::vector<unsigned> faces; |
57 | |
58 GLuint id_prog, id_ps, id_vs; | |
58 }; | 59 }; |
59 | 60 |
60 | 61 |
61 | 62 |
62 bool dmInitSDLGL(const int width, const int height, const char *title) | 63 bool dmInitSDLGL(const int width, const int height, const char *title) |
63 { | 64 { |
64 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); | 65 // Set GL attributes |
65 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); | 66 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); |
66 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); | 67 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); |
68 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); | |
69 | |
67 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); | 70 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); |
68 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); | 71 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); |
69 | 72 |
73 // Attempt to initialize libSDL | |
70 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_EVENTS) != 0) | 74 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_EVENTS) != 0) |
71 { | 75 { |
72 printf("ERROR: Unable to initialize SDL: %s\n", | 76 printf("ERROR: Unable to initialize SDL: %s\n", |
73 SDL_GetError()); | 77 SDL_GetError()); |
74 return false; | 78 return false; |
75 } | 79 } |
76 | 80 |
77 if ((s_window = SDL_CreateWindow(title, | 81 // Attempt to create a window |
82 if ((sdl_window = SDL_CreateWindow(title, | |
78 SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, | 83 SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, |
79 width, height, | 84 width, height, |
80 SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE)) == NULL) | 85 SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE)) == NULL) |
81 { | 86 { |
82 printf("ERROR: Could not create SDL window: %s", | 87 printf("ERROR: Could not create SDL window: %s", |
83 SDL_GetError()); | 88 SDL_GetError()); |
84 return false; | 89 return false; |
85 } | 90 } |
86 | 91 |
87 if ((s_context = SDL_GL_CreateContext(s_window)) == NULL) | 92 if ((sdl_glctx = SDL_GL_CreateContext(sdl_window)) == NULL) |
88 { | 93 { |
89 printf("ERROR: Unable to create SDL OpenGL context: %s\n", | 94 printf("ERROR: Unable to create SDL OpenGL context: %s\n", |
90 SDL_GetError()); | 95 SDL_GetError()); |
91 return false; | 96 return false; |
92 } | 97 } |
93 | 98 |
99 // Dump some information | |
94 printf( | 100 printf( |
95 "GL_VENDOR : %s\n" | 101 "GL_VENDOR : %s\n" |
96 "GL_RENDERER : %s\n" | 102 "GL_RENDERER : %s\n" |
97 "GL_VERSION : %s\n", | 103 "GL_VERSION : %s\n", |
98 glGetString(GL_VENDOR), | 104 glGetString(GL_VENDOR), |
135 | 141 |
136 return true; | 142 return true; |
137 } | 143 } |
138 | 144 |
139 | 145 |
140 void dmInitScene(void) | |
141 { | |
142 glEnable(GL_COLOR_MATERIAL); | |
143 | |
144 glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE); | |
145 glMateriali(GL_FRONT, GL_SHININESS, 96); | |
146 | |
147 float specReflection[] = { 0.8f, 0.8f, 0.8f, 1.0f }; | |
148 glMaterialfv(GL_FRONT, GL_SPECULAR, specReflection); | |
149 | |
150 | |
151 glEnable(GL_LIGHT0); | |
152 | |
153 // Define the light components and position | |
154 GLfloat ambient[] = { 0.2f, 0.2f, 0.2f, 1.0f }; | |
155 GLfloat diffuse[] = { 0.8f, 0.8f, 0.8f, 1.0f }; | |
156 GLfloat specular[] = { 0.5f, 0.5f, 0.5f, 1.0f }; | |
157 GLfloat position[] = { 10.0f, 10.0f, 0.0f, 0.0f }; | |
158 | |
159 // Define the light components and position | |
160 glLightfv(GL_LIGHT0, GL_AMBIENT, ambient); | |
161 glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse); | |
162 glLightfv(GL_LIGHT0, GL_SPECULAR, specular); | |
163 glLightfv(GL_LIGHT0, GL_POSITION, position); | |
164 | |
165 // Define the camera | |
166 gluLookAt(0, 0.12, 0.24, 0, 0.12, 0, 0, 1, 0); | |
167 } | |
168 | |
169 | |
170 void dmFinish() | |
171 { | |
172 SDL_GL_DeleteContext(s_context); | |
173 SDL_DestroyWindow(s_window); | |
174 s_window = NULL; | |
175 SDL_Quit(); | |
176 } | |
177 | |
178 | |
179 void dmDrawModelVA(const Mesh &mesh) | 146 void dmDrawModelVA(const Mesh &mesh) |
180 { | 147 { |
181 int maxIndices; | 148 int maxIndices; |
182 | 149 |
183 glGetIntegerv(GL_MAX_ELEMENTS_INDICES, &maxIndices); | 150 glGetIntegerv(GL_MAX_ELEMENTS_INDICES, &maxIndices); |
231 | 198 |
232 glMatrixMode(GL_MODELVIEW); | 199 glMatrixMode(GL_MODELVIEW); |
233 glPopMatrix(); | 200 glPopMatrix(); |
234 | 201 |
235 glEnable(GL_DEPTH_TEST); | 202 glEnable(GL_DEPTH_TEST); |
236 glEnable(GL_LIGHTING); | 203 |
237 | 204 // Render the model |
238 // Set the color of the model | 205 if (opt_shaders) |
239 glColor3ub(0x90, 0x80, 0x90); | 206 { |
240 | 207 // Enable shader program |
241 // Draw the model using vertex arrays | 208 glUseProgram(mesh.id_prog); |
242 dmDrawModelVA(mesh); | 209 dmDrawModelVA(mesh); |
243 } | 210 glUseProgram(0); |
244 | 211 } |
245 | 212 else |
246 void dmLoadMesh(const std::string &filename, Mesh &mesh, int nvertices, int nfaces) | 213 { |
214 // Set the color of the model | |
215 glEnable(GL_LIGHTING); | |
216 glColor3ub(0x90, 0x80, 0x90); | |
217 dmDrawModelVA(mesh); | |
218 } | |
219 } | |
220 | |
221 | |
222 bool dmReadText(const std::string &filename, std::string &tstr) | |
223 { | |
224 std::ifstream in(filename.c_str()); | |
225 | |
226 if (!in.is_open()) | |
227 { | |
228 printf("ERROR: Unable to open file '%s'.\n", | |
229 filename.c_str()); | |
230 return false; | |
231 } | |
232 | |
233 in.seekg(0, std::ios::end); | |
234 tstr.reserve(in.tellg()); | |
235 in.seekg(0, std::ios::beg); | |
236 | |
237 tstr.assign((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>()); | |
238 | |
239 return true; | |
240 } | |
241 | |
242 | |
243 bool dmLoadMesh(const std::string &filename, Mesh &mesh, int nvertices, int nfaces) | |
247 { | 244 { |
248 std::ifstream in(filename.c_str(), std::ios::binary); | 245 std::ifstream in(filename.c_str(), std::ios::binary); |
249 | 246 |
250 if (!in.is_open()) | 247 if (!in.is_open()) |
251 { | 248 { |
252 std::stringstream ss; | 249 printf("ERROR: Unable to open file '%s'.\n", |
253 ss << "Unable to open file: " << filename << '\n'; | 250 filename.c_str()); |
254 throw std::runtime_error(ss.str()); | 251 return false; |
255 } | 252 } |
256 | 253 |
257 mesh.nvertices = nvertices; | 254 mesh.nvertices = nvertices; |
258 mesh.vertices.resize(mesh.nvertices * 6); | 255 mesh.vertices.resize(mesh.nvertices * 6); |
259 in.read(reinterpret_cast<char*>(&mesh.vertices[0]), mesh.nvertices * 6 * 4); | 256 in.read(reinterpret_cast<char*>(&mesh.vertices[0]), mesh.nvertices * 6 * 4); |
264 for (int i = 0; i < nfaces; i++) | 261 for (int i = 0; i < nfaces; i++) |
265 { | 262 { |
266 in.seekg(1, std::ios::cur); | 263 in.seekg(1, std::ios::cur); |
267 in.read(reinterpret_cast<char*>(&mesh.faces[i * 3]), 3 * 4); | 264 in.read(reinterpret_cast<char*>(&mesh.faces[i * 3]), 3 * 4); |
268 } | 265 } |
269 } | 266 |
270 | 267 return true; |
271 | 268 } |
272 int main() | 269 |
273 { | 270 |
274 try | 271 GLuint dmSetCompileShader(const GLenum stype, const std::string &src) |
275 { | 272 { |
276 struct Mesh dragonMesh; | 273 GLuint shader = glCreateShader(stype); |
277 dmLoadMesh("dragon.mesh", dragonMesh, 100139, 200198); | 274 const char *tmp = src.c_str(); |
278 | 275 |
279 //if (!dmInitSDLGL(640, 480, "glxdragon")) | 276 glShaderSource(shader, 1, &tmp, 0); |
280 if (!dmInitSDLGL(1280, 960, "glxdragon")) | 277 glCompileShader(shader); |
281 throw std::runtime_error("Fatal error."); | 278 |
282 | 279 return shader; |
283 dmInitScene(); | 280 } |
284 | 281 |
285 bool exitFlag = false; | 282 |
286 int steps = 0; | 283 void dmLinkMeshShaders(Mesh &mesh) |
287 std::clock_t startTime = std::clock(); | 284 { |
288 | 285 mesh.id_prog = glCreateProgram(); |
289 while (!exitFlag) | 286 glAttachShader(mesh.id_prog, mesh.id_ps); |
287 glAttachShader(mesh.id_prog, mesh.id_vs); | |
288 glLinkProgram(mesh.id_prog); | |
289 } | |
290 | |
291 | |
292 int main(int argc, char *argv[]) | |
293 { | |
294 struct Mesh dragonMesh; | |
295 std::string dragonVS, dragonFS; | |
296 std::clock_t startTime; | |
297 bool exitFlag = false; | |
298 int steps = 0; | |
299 | |
300 // Check commandline argument for enabling shaders | |
301 if (argc > 1 && | |
302 (strstr(argv[1], "glsl") != NULL || | |
303 strstr(argv[1], "sha") != NULL)) | |
304 opt_shaders = true; | |
305 | |
306 | |
307 if (!dmLoadMesh("dragon.mesh", dragonMesh, 100139, 200198)) | |
308 goto exit; | |
309 | |
310 if (opt_shaders) | |
311 { | |
312 // Read shader files | |
313 if (!dmReadText("dragon.frag", dragonFS) || | |
314 !dmReadText("dragon.vert", dragonVS)) | |
315 goto exit; | |
316 } | |
317 | |
318 // Initialize SDL + OpenGL | |
319 if (!dmInitSDLGL(1280, 960, "GLXDragon2")) | |
320 goto exit; | |
321 | |
322 // According to our mode .. | |
323 if (opt_shaders) | |
324 { | |
325 dragonMesh.id_ps = dmSetCompileShader(GL_FRAGMENT_SHADER, dragonFS); | |
326 dragonMesh.id_vs = dmSetCompileShader(GL_VERTEX_SHADER, dragonVS); | |
327 dmLinkMeshShaders(dragonMesh); | |
328 } | |
329 else | |
330 { | |
331 float specReflection[] = { 0.8f, 0.8f, 0.8f, 1.0f }; | |
332 | |
333 glEnable(GL_COLOR_MATERIAL); | |
334 | |
335 glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE); | |
336 glMateriali(GL_FRONT, GL_SHININESS, 96); | |
337 | |
338 glMaterialfv(GL_FRONT, GL_SPECULAR, specReflection); | |
339 | |
340 glEnable(GL_LIGHT0); | |
341 | |
342 // Define the light components and position | |
343 GLfloat ambient[] = { 0.2f, 0.2f, 0.2f, 1.0f }; | |
344 GLfloat diffuse[] = { 0.8f, 0.8f, 0.8f, 1.0f }; | |
345 GLfloat specular[] = { 0.5f, 0.5f, 0.5f, 1.0f }; | |
346 GLfloat position[] = { 10.0f, 10.0f, 0.0f, 0.0f }; | |
347 | |
348 // Define the light components and position | |
349 glLightfv(GL_LIGHT0, GL_AMBIENT, ambient); | |
350 glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse); | |
351 glLightfv(GL_LIGHT0, GL_SPECULAR, specular); | |
352 glLightfv(GL_LIGHT0, GL_POSITION, position); | |
353 } | |
354 | |
355 // Define the camera | |
356 gluLookAt(0, 0.12, 0.24, 0, 0.12, 0, 0, 1, 0); | |
357 | |
358 | |
359 // Main loop starts | |
360 startTime = std::clock(); | |
361 | |
362 while (!exitFlag) | |
363 { | |
364 SDL_Event event; | |
365 | |
366 // Check for quit events | |
367 while (SDL_PollEvent(&event)) | |
368 switch (event.type) | |
290 { | 369 { |
291 SDL_Event event; | 370 case SDL_QUIT: |
292 | 371 exitFlag = true; |
293 // Check for quit events | 372 break; |
294 while (SDL_PollEvent(&event)) | 373 |
295 switch (event.type) | 374 case SDL_KEYDOWN: |
296 { | 375 switch (event.key.keysym.sym) |
297 case SDL_QUIT: | 376 { |
298 exitFlag = true; | 377 case SDLK_ESCAPE: |
299 break; | 378 case SDLK_q: |
300 | 379 exitFlag = true; |
301 case SDL_KEYDOWN: | 380 break; |
302 switch (event.key.keysym.sym) | 381 } |
303 { | |
304 case SDLK_ESCAPE: | |
305 case SDLK_q: | |
306 exitFlag = true; | |
307 break; | |
308 } | |
309 } | |
310 | |
311 // Render the next frame | |
312 dmPaintGL(dragonMesh); | |
313 | |
314 // Draw the current frame | |
315 SDL_GL_SwapWindow(s_window); | |
316 | |
317 // Rotate for 2 degrees | |
318 glRotatef(2.0f, 0, 1, 0); | |
319 | |
320 // Return true if a full rotation was done | |
321 if (steps++ == SET_FRAMES) | |
322 { | |
323 // Reset steps | |
324 steps = 0; | |
325 | |
326 // Get the time it took to render a full turn | |
327 double time = (double(std::clock() - startTime) * 1000.0f) / CLOCKS_PER_SEC; | |
328 | |
329 // Print the current frames per second | |
330 printf("%.1lf ms for %d frames = %.1lf FPS\n", | |
331 time, SET_FRAMES, (SET_FRAMES * 1000.0f) / time); | |
332 | |
333 // Restart the timer | |
334 startTime = std::clock(); | |
335 } | |
336 } | 382 } |
337 } | 383 |
338 catch(std::runtime_error & e) | 384 // Render the next frame |
339 { | 385 dmPaintGL(dragonMesh); |
340 std::cerr << e.what(); | 386 |
341 } | 387 // Draw the current frame |
342 | 388 SDL_GL_SwapWindow(sdl_window); |
343 dmFinish(); | 389 |
344 } | 390 // Rotate for 2 degrees |
391 glRotatef(2.0f, 0, 1, 0); | |
392 | |
393 // Return true if a full rotation was done | |
394 if (steps++ == SET_FRAMES) | |
395 { | |
396 // Reset steps | |
397 steps = 0; | |
398 | |
399 // Get the time it took to render a full turn | |
400 double time = (double(std::clock() - startTime) * 1000.0f) / CLOCKS_PER_SEC; | |
401 | |
402 // Print the current frames per second | |
403 printf("%.1lf ms for %d frames = %.1lf FPS\n", | |
404 time, SET_FRAMES, (SET_FRAMES * 1000.0f) / time); | |
405 | |
406 // Restart the timer | |
407 startTime = std::clock(); | |
408 } | |
409 } | |
410 | |
411 exit: | |
412 if (sdl_glctx != NULL) | |
413 SDL_GL_DeleteContext(sdl_glctx); | |
414 | |
415 if (sdl_window != NULL) | |
416 SDL_DestroyWindow(sdl_window); | |
417 | |
418 SDL_Quit(); | |
419 | |
420 return 0; | |
421 } |