1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
|
/*********************************************************************************************
*
* raylib.core
*
* Basic functions to manage Windows, OpenGL context and Input
*
* Uses external lib:
* GLFW3 - Window, context and Input management (static lib version)
*
* Copyright (c) 2013 Ramon Santamaria (Ray San - raysan@raysanweb.com)
*
* This software is provided "as-is", without any express or implied warranty. In no event
* will the authors be held liable for any damages arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose, including commercial
* applications, and to alter it and redistribute it freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not claim that you
* wrote the original software. If you use this software in a product, an acknowledgment
* in the product documentation would be appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
* as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
**********************************************************************************************/
#include "raylib.h"
#include <GLFW/glfw3.h> // GLFW3 lib: Windows, OpenGL context and Input management
//#include <GL/gl.h> // OpenGL functions (GLFW3 already includes gl.h)
#include <stdio.h> // Standard input / output lib
#include <stdlib.h> // Declares malloc() and free() for memory management, rand()
#include <time.h> // Useful to initialize random seed
#include <math.h> // Math related functions, tan() on SetPerspective
#include "vector3.h" // Basic Vector3 functions
#include "utils.h" // WritePNG() function
//#define GLFW_DLL // Using GLFW DLL on Windows -> No, we use static version!
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
// Nop...
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
typedef Color pixel;
//----------------------------------------------------------------------------------
// Global Variables Definition
//----------------------------------------------------------------------------------
static GLFWwindow* window; // Main window
static bool fullscreen; // Fullscreen mode track
static double currentTime, previousTime; // Used to track timmings
static double updateTime, drawTime; // Time measures for update and draw
static double frameTime; // Time measure for one frame
static double targetTime = 0; // Desired time for one frame, if 0 not applied
static int windowWidth, windowHeight; // Required to switch between windowed/fullscren mode (F11)
static const char *windowTitle; // Required to switch between windowed/fullscren mode (F11)
static int exitKey = GLFW_KEY_ESCAPE;
static bool customCursor = false; // Tracks if custom cursor has been set
static bool cursorOnScreen = false; // Tracks if cursor is inside client area
static Texture2D cursor; // Cursor texture
static char previousKeyState[512] = { 0 }; // Required to check if key pressed/released once
static char currentKeyState[512] = { 0 }; // Required to check if key pressed/released once
static char previousMouseState[3] = { 0 }; // Required to check if mouse btn pressed/released once
static char currentMouseState[3] = { 0 }; // Required to check if mouse btn pressed/released once
static char previousGamepadState[32] = {0}; // Required to check if gamepad btn pressed/released once
static char currentGamepadState[32] = {0}; // Required to check if gamepad btn pressed/released once
//----------------------------------------------------------------------------------
// Other Modules Functions Declaration (required by core)
//----------------------------------------------------------------------------------
extern void LoadDefaultFont(); // [Module: text] Loads default font on InitWindow()
extern void UnloadDefaultFont(); // [Module: text] Unloads default font from GPU memory
//----------------------------------------------------------------------------------
// Module specific Functions Declaration
//----------------------------------------------------------------------------------
static void InitGraphicsDevice(); // Initialize Graphics Device (OpenGL stuff)
static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error
static void KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); // GLFW3 Keyboard Callback, runs on key pressed
static void CursorEnterCallback(GLFWwindow* window, int enter); // GLFW3 Cursor Enter Callback, cursor enters client area
static void WindowSizeCallback(GLFWwindow* window, int width, int height); // GLFW3 WindowSize Callback, runs when window is resized
static void CameraLookAt(Vector3 position, Vector3 target, Vector3 up); // Setup camera view (updates MODELVIEW matrix)
static void SetPerspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar); // Setup view projection (updates PROJECTION matrix)
static void TakeScreenshot(); // Takes a bitmap (BMP) screenshot and saves it in the same folder as executable
//----------------------------------------------------------------------------------
// Module Functions Definition - Window and OpenGL Context Functions
//----------------------------------------------------------------------------------
// Initialize Window and Graphics Context (OpenGL)
void InitWindow(int width, int height, const char *title)
{
InitWindowEx(width, height, title, true, NULL);
}
// Initialize Window and Graphics Context (OpenGL) with extended parameters
void InitWindowEx(int width, int height, const char* title, bool resizable, const char *cursorImage)
{
glfwSetErrorCallback(ErrorCallback);
if (!glfwInit()) exit(1);
//glfwDefaultWindowHints() // Set default windows hints
//glfwWindowHint(GLFW_SAMPLES, 4); // If called before windows creation, enables multisampling x4 (MSAA), default is 0
if (!resizable) glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); // Avoid window being resizable
window = glfwCreateWindow(width, height, title, NULL, NULL);
windowWidth = width;
windowHeight = height;
windowTitle = title;
if (!window)
{
glfwTerminate();
exit(1);
}
glfwSetWindowSizeCallback(window, WindowSizeCallback);
glfwSetCursorEnterCallback(window, CursorEnterCallback);
glfwMakeContextCurrent(window);
glfwSetKeyCallback(window, KeyCallback);
glfwSwapInterval(0); // Disables GPU v-sync (if set), so frames are not limited to screen refresh rate (60Hz -> 60 FPS)
// If not set, swap interval uses GPU v-sync configuration
// Framerate can be setup using SetTargetFPS()
InitGraphicsDevice();
previousTime = glfwGetTime();
LoadDefaultFont();
if (cursorImage != NULL)
{
// Load image as texture
cursor = LoadTexture(cursorImage);
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
customCursor = true;
}
srand(time(NULL)); // Initialize random seed
}
// Close Window and Terminate Context
void CloseWindow()
{
UnloadDefaultFont();
glfwDestroyWindow(window);
glfwTerminate();
}
// Set a custom cursor icon/image
void SetCustomCursor(const char *cursorImage)
{
if (customCursor) UnloadTexture(cursor);
cursor = LoadTexture(cursorImage);
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
customCursor = true;
}
// Set a custom key to exit program
// NOTE: default exitKey is ESCAPE
void SetExitKey(int key)
{
exitKey = key;
}
// Detect if KEY_ESCAPE pressed or Close icon pressed
bool WindowShouldClose()
{
return (glfwWindowShouldClose(window));
}
// Fullscreen toggle (by default F11)
void ToggleFullscreen()
{
if (glfwGetKey(window, GLFW_KEY_F11))
{
fullscreen = !fullscreen; // Toggle fullscreen flag
glfwDestroyWindow(window); // Destroy the current window (we will recreate it!)
// NOTE: Window aspect ratio is always windowWidth / windowHeight
if (fullscreen) window = glfwCreateWindow(windowWidth, windowHeight, windowTitle, glfwGetPrimaryMonitor(), NULL); // Fullscreen mode
else window = glfwCreateWindow(windowWidth, windowHeight, windowTitle, NULL, NULL);
if (!window)
{
glfwTerminate();
exit(1);
}
glfwMakeContextCurrent(window);
glfwSetKeyCallback(window, KeyCallback);
InitGraphicsDevice();
}
}
// Sets Background Color
void ClearBackground(Color color)
{
// Color values clamp to 0.0f(0) and 1.0f(255)
float r = (float)color.r / 255;
float g = (float)color.g / 255;
float b = (float)color.b / 255;
float a = (float)color.a / 255;
glClearColor(r, g, b, a);
}
// Setup drawing canvas to start drawing
void BeginDrawing()
{
currentTime = glfwGetTime(); // glfwGetTime() returns a 'double' containing the number of elapsed seconds since glfwInit() was called
updateTime = currentTime - previousTime;
previousTime = currentTime;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear used buffers, Depth Buffer is used for 3D
glLoadIdentity(); // Reset current matrix (MODELVIEW)
glTranslatef(0.375, 0.375, 0); // HACK to have 2D pixel-perfect drawing on OpenGL
}
// End canvas drawing and Swap Buffers (Double Buffering)
void EndDrawing()
{
if (customCursor && cursorOnScreen) DrawTexture(cursor, GetMouseX(), GetMouseY(), WHITE);
glfwSwapBuffers(window); // Swap back and front buffers
glfwPollEvents(); // Register keyboard/mouse events
currentTime = glfwGetTime();
drawTime = currentTime - previousTime;
previousTime = currentTime;
frameTime = updateTime + drawTime;
double extraTime = 0;
while (frameTime < targetTime)
{
// Implement a delay
currentTime = glfwGetTime();
extraTime = currentTime - previousTime;
previousTime = currentTime;
frameTime += extraTime;
}
}
// Initializes 3D mode for drawing (Camera setup)
void Begin3dMode(Camera camera)
{
//glEnable(GL_LIGHTING); // TODO: Setup proper lighting system (raylib 1.x)
glMatrixMode(GL_PROJECTION); // Switch to projection matrix
glPushMatrix(); // Save previous matrix, which contains the settings for the 2d ortho projection
glLoadIdentity(); // Reset current matrix (PROJECTION)
SetPerspective(45.0f, (GLfloat)windowWidth/(GLfloat)windowHeight, 0.1f, 100.0f); // Setup perspective projection
glMatrixMode(GL_MODELVIEW); // Switch back to modelview matrix
glLoadIdentity(); // Reset current matrix (MODELVIEW)
CameraLookAt(camera.position, camera.target, camera.up); // Setup Camera view
}
// Ends 3D mode and returns to default 2D orthographic mode
void End3dMode()
{
glMatrixMode(GL_PROJECTION); // Switch to projection matrix
glPopMatrix(); // Restore previous matrix (PROJECTION) from matrix stack
glMatrixMode(GL_MODELVIEW); // Get back to modelview matrix
glLoadIdentity(); // Reset current matrix (MODELVIEW)
glTranslatef(0.375, 0.375, 0); // HACK to ensure pixel-perfect drawing on OpenGL (after exiting 3D mode)
//glDisable(GL_LIGHTING); // TODO: Setup proper lighting system (raylib 1.x)
}
// Set target FPS for the game
void SetTargetFPS(int fps)
{
targetTime = 1 / (float)fps;
printf("TargetTime per Frame: %f seconds\n", (float)targetTime);
}
// Returns current FPS
float GetFPS()
{
return (1/(float)frameTime);
}
// Returns time in seconds for one frame
float GetFrameTime()
{
// As we are operating quite a lot with frameTime, it could be no stable
// so we round it before before passing around to be used
// NOTE: There are still problems with high framerates (>500fps)
double roundedFrameTime = round(frameTime*10000) / 10000;
return (float)roundedFrameTime; // Time in seconds to run a frame
}
// Returns a Color struct from hexadecimal value
Color GetColor(int hexValue)
{
Color color;
color.r = (unsigned char)(hexValue >> 24) & 0xFF;
color.g = (unsigned char)(hexValue >> 16) & 0xFF;
color.b = (unsigned char)(hexValue >> 8) & 0xFF;
color.a = (unsigned char)hexValue & 0xFF;
return color;
}
// Returns hexadecimal value for a Color
int GetHexValue(Color color)
{
return ((color.a << 24) + (color.r << 16) + (color.g << 8) + color.b);
}
// Returns a random value between min and max (both included)
int GetRandomValue(int min, int max)
{
if (min > max)
{
int tmp = max;
max = min;
min = tmp;
}
return (rand()%(abs(max-min)+1) + min);
}
// Fades color by a percentadge
Color Fade(Color color, float alpha)
{
if (alpha < 0.0) alpha = 0.0;
else if (alpha > 1.0) alpha = 1.0;
return (Color){color.r, color.g, color.b, color.a*alpha};
}
//----------------------------------------------------------------------------------
// Module Functions Definition - Input (Keyboard, Mouse, Gamepad) Functions
//----------------------------------------------------------------------------------
// Detect if a key has been pressed once
bool IsKeyPressed(int key)
{
bool ret = false;
currentKeyState[key] = IsKeyDown(key);
if (currentKeyState[key] != previousKeyState[key])
{
if (currentKeyState[key]) ret = true;
previousKeyState[key] = currentKeyState[key];
}
else ret = false;
return ret;
}
// Detect if a key is being pressed (key held down)
bool IsKeyDown(int key)
{
if (glfwGetKey(window, key) == GLFW_PRESS) return true;
else return false;
}
// Detect if a key has been released once
bool IsKeyReleased(int key)
{
bool ret = false;
currentKeyState[key] = IsKeyUp(key);
if (currentKeyState[key] != previousKeyState[key])
{
if (currentKeyState[key]) ret = true;
previousKeyState[key] = currentKeyState[key];
}
else ret = false;
return ret;
}
// Detect if a key is NOT being pressed (key not held down)
bool IsKeyUp(int key)
{
if (glfwGetKey(window, key) == GLFW_RELEASE) return true;
else return false;
}
// Detect if a mouse button has been pressed once
bool IsMouseButtonPressed(int button)
{
bool ret = false;
currentMouseState[button] = IsMouseButtonDown(button);
if (currentMouseState[button] != previousMouseState[button])
{
if (currentMouseState[button]) ret = true;
previousMouseState[button] = currentMouseState[button];
}
else ret = false;
return ret;
}
// Detect if a mouse button is being pressed
bool IsMouseButtonDown(int button)
{
if (glfwGetMouseButton(window, button) == GLFW_PRESS) return true;
else return false;
}
// Detect if a mouse button has been released once
bool IsMouseButtonReleased(int button)
{
bool ret = false;
currentMouseState[button] = IsMouseButtonUp(button);
if (currentMouseState[button] != previousMouseState[button])
{
if (currentMouseState[button]) ret = true;
previousMouseState[button] = currentMouseState[button];
}
else ret = false;
return ret;
}
// Detect if a mouse button is NOT being pressed
bool IsMouseButtonUp(int button)
{
if (glfwGetMouseButton(window, button) == GLFW_RELEASE) return true;
else return false;
}
// Returns mouse position X
int GetMouseX()
{
double mouseX;
double mouseY;
glfwGetCursorPos(window, &mouseX, &mouseY);
return (int)mouseX;
}
// Returns mouse position Y
int GetMouseY()
{
double mouseX;
double mouseY;
glfwGetCursorPos(window, &mouseX, &mouseY);
return (int)mouseY;
}
// Returns mouse position XY
Vector2 GetMousePosition()
{
double mouseX;
double mouseY;
glfwGetCursorPos(window, &mouseX, &mouseY);
Vector2 position = { (float)mouseX, (float)mouseY };
return position;
}
// Detect if a gamepad is available
bool IsGamepadAvailable(int gamepad)
{
int result = glfwJoystickPresent(gamepad);
if (result == 1) return true;
else return false;
}
// Return axis movement vector for a gamepad
Vector2 GetGamepadMovement(int gamepad)
{
Vector2 vec = { 0, 0 };
const float *axes;
int axisCount;
axes = glfwGetJoystickAxes(gamepad, &axisCount);
if (axisCount >= 2)
{
vec.x = axes[0]; // Left joystick X
vec.y = axes[1]; // Left joystick Y
//vec.x = axes[2]; // Right joystick X
//vec.x = axes[3]; // Right joystick Y
}
return vec;
}
// Detect if a gamepad button is being pressed
bool IsGamepadButtonPressed(int gamepad, int button)
{
bool ret = false;
currentGamepadState[button] = IsGamepadButtonDown(gamepad, button);
if (currentGamepadState[button] != previousGamepadState[button])
{
if (currentGamepadState[button]) ret = true;
previousGamepadState[button] = currentGamepadState[button];
}
else ret = false;
return ret;
}
bool IsGamepadButtonDown(int gamepad, int button)
{
const unsigned char* buttons;
int buttonsCount;
buttons = glfwGetJoystickButtons(gamepad, &buttonsCount);
if (buttons[button] == GLFW_PRESS)
{
return true;
}
else return false;
}
// Detect if a gamepad button is NOT being pressed
bool IsGamepadButtonReleased(int gamepad, int button)
{
bool ret = false;
currentGamepadState[button] = IsGamepadButtonUp(gamepad, button);
if (currentGamepadState[button] != previousGamepadState[button])
{
if (currentGamepadState[button]) ret = true;
previousGamepadState[button] = currentGamepadState[button];
}
else ret = false;
return ret;
}
bool IsGamepadButtonUp(int gamepad, int button)
{
const unsigned char* buttons;
int buttonsCount;
buttons = glfwGetJoystickButtons(gamepad, &buttonsCount);
if (buttons[button] == GLFW_RELEASE)
{
return true;
}
else return false;
}
//----------------------------------------------------------------------------------
// Module specific Functions Definition
//----------------------------------------------------------------------------------
// GLFW3 Error Callback, runs on GLFW3 error
static void ErrorCallback(int error, const char *description)
{
printf(description);
//fprintf(stderr, description);
}
// GLFW3 Keyboard Callback, runs on key pressed
static void KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
if (key == exitKey && action == GLFW_PRESS)
{
glfwSetWindowShouldClose(window, GL_TRUE);
// NOTE: Before closing window, while loop must be left!
}
else if (key == GLFW_KEY_F11 && action == GLFW_PRESS)
{
ToggleFullscreen();
}
else if (key == GLFW_KEY_F12 && action == GLFW_PRESS)
{
TakeScreenshot();
}
}
static void CursorEnterCallback(GLFWwindow* window, int enter)
{
if (enter == GL_TRUE) cursorOnScreen = true;
else cursorOnScreen = false;
}
// GLFW3 WindowSize Callback, runs when window is resized
static void WindowSizeCallback(GLFWwindow* window, int width, int height)
{
InitGraphicsDevice(); // If window is resized, graphics device is re-initialized
// NOTE: Aspect ratio does not change, so, image can be deformed
}
// Initialize Graphics Device (OpenGL stuff)
static void InitGraphicsDevice()
{
int fbWidth, fbHeight;
glfwGetFramebufferSize(window, &fbWidth, &fbHeight); // Get framebuffer size of current window
glViewport(0, 0, fbWidth, fbHeight); // Set viewport width and height
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear used buffers, depth buffer is used for 3D
glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // Set background color (black)
glClearDepth(1.0f); // Clear depth buffer
glEnable(GL_DEPTH_TEST); // Enables depth testing (required for 3D)
glDepthFunc(GL_LEQUAL); // Type of depth testing to apply
glEnable(GL_BLEND); // Enable color blending (required to work with transparencies)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Color blending function (how colors are mixed)
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Improve quality of color and texture coordinate interpolation (Deprecated in OGL 3.0)
// Other options: GL_FASTEST, GL_DONT_CARE (default)
glMatrixMode(GL_PROJECTION); // Switch to PROJECTION matrix
glLoadIdentity(); // Reset current matrix (PROJECTION)
glOrtho(0, fbWidth, fbHeight, 0, 0, 1); // Config orthographic mode: top-left corner --> (0,0)
glMatrixMode(GL_MODELVIEW); // Switch back to MODELVIEW matrix
glLoadIdentity(); // Reset current matrix (MODELVIEW)
glDisable(GL_LIGHTING); // Lighting Disabled...
// TODO: Create an efficient Lighting System with proper functions (raylib 1.x)
/*
glEnable(GL_COLOR_MATERIAL); // Enable materials, causes some glMaterial atributes to track the current color (glColor)...
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE); // Material types and where to apply them
// NOTE: ONLY works with lighting; defines how light interacts with material
glLightfv(GL_LIGHT1, GL_AMBIENT, lightAmbient); // Define ambient light color property
glLightfv(GL_LIGHT1, GL_DIFFUSE, lightDiffuse); // Define diffuse light color property
glLightfv(GL_LIGHT1, GL_POSITION, lightPosition); // Define light position
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT1); // Enable light one (8 lights available at the same time)
*/
// TODO: Review all shapes/models are drawn CCW and enable backface culling
//glEnable(GL_CULL_FACE); // Enable backface culling (Disabled by default)
//glCullFace(GL_BACK); // Cull the Back face (default)
//glFrontFace(GL_CCW); // Front face are defined counter clockwise (default)
glShadeModel(GL_SMOOTH); // Smooth shading between vertex (vertex colors interpolation)
// Possible options: GL_SMOOTH (Color interpolation) or GL_FLAT (no interpolation)
}
// Setup camera view (updates MODELVIEW matrix)
static void CameraLookAt(Vector3 position, Vector3 target, Vector3 up)
{
float rotMatrix[16]; // Matrix to store camera rotation
Vector3 rotX, rotY, rotZ; // Vectors to calculate camera rotations X, Y, Z (Euler)
// Construct rotation matrix from vectors
rotZ = VectorSubtract(position, target);
VectorNormalize(&rotZ);
rotY = up; // Y rotation vector
rotX = VectorCrossProduct(rotY, rotZ); // X rotation vector = Y cross Z
rotY = VectorCrossProduct(rotZ, rotX); // Recompute Y rotation = Z cross X
VectorNormalize(&rotX); // X rotation vector normalization
VectorNormalize(&rotY); // Y rotation vector normalization
rotMatrix[0] = rotX.x;
rotMatrix[1] = rotY.x;
rotMatrix[2] = rotZ.x;
rotMatrix[3] = 0.0f;
rotMatrix[4] = rotX.y;
rotMatrix[5] = rotY.y;
rotMatrix[6] = rotZ.y;
rotMatrix[7] = 0.0f;
rotMatrix[8] = rotX.z;
rotMatrix[9] = rotY.z;
rotMatrix[10] = rotZ.z;
rotMatrix[11] = 0.0f;
rotMatrix[12] = 0.0f;
rotMatrix[13] = 0.0f;
rotMatrix[14] = 0.0f;
rotMatrix[15] = 1.0f;
glMultMatrixf(rotMatrix); // Multiply MODELVIEW matrix by rotation matrix
glTranslatef(-position.x, -position.y, -position.z); // Translate eye to position
}
// Setup view projection (updates PROJECTION matrix)
static void SetPerspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar)
{
double xmin, xmax, ymin, ymax;
ymax = zNear * tan(fovy * PI / 360.0);
ymin = -ymax;
xmin = ymin * aspect;
xmax = ymax * aspect;
glFrustum(xmin, xmax, ymin, ymax, zNear, zFar);
}
// Takes a bitmap (BMP) screenshot and saves it in the same folder as executable
static void TakeScreenshot()
{
static int shotNum = 0; // Screenshot number, increments every screenshot take during program execution
char buffer[20]; // Buffer to store file name
int fbWidth, fbHeight;
unsigned char *imgData; // Pixel image data array
glfwGetFramebufferSize(window, &fbWidth, &fbHeight); // Get framebuffer size of current window
imgData = (unsigned char *)malloc(fbWidth * fbHeight * sizeof(unsigned char) * 4);
// NOTE: glReadPixels returns image flipped vertically -> (0,0) is the bottom left corner of the framebuffer
glReadPixels(0, 0, fbWidth, fbHeight, GL_RGBA, GL_UNSIGNED_BYTE, imgData);
// TODO: Flip image vertically!
unsigned char *imgDataFlip = (unsigned char *)malloc(fbWidth * fbHeight * sizeof(unsigned char) * 4);
for (int y = fbHeight-1; y >= 0; y--)
{
for (int x = 0; x < (fbWidth*4); x++)
{
imgDataFlip[x + (fbHeight - y - 1)*fbWidth*4] = imgData[x + (y*fbWidth*4)];
}
}
free(imgData);
sprintf(buffer, "screenshot%03i.png", shotNum);
// NOTE: BMP directly stores data flipped vertically
//WriteBitmap(buffer, imgDataPixel, fbWidth, fbHeight); // Writes pixel data array into a bitmap (BMP) file
WritePNG(buffer, imgDataFlip, fbWidth, fbHeight);
free(imgDataFlip);
shotNum++;
}
|