-
-
Notifications
You must be signed in to change notification settings - Fork 23
/
Copy path05_maze_game_cubicmap.c
1657 lines (1368 loc) · 69.5 KB
/
05_maze_game_cubicmap.c
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
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*******************************************************************************************
*
* Challenge 03: MAZE GAME
* Lesson 05: cubicmap loading
* Description: Cubicmap generation and drawing
*
* Compile rglfw module using:
* gcc -c external/rglfw.c -Wall -std=c99 -DPLATFORM_DESKTOP -DGRAPHICS_API_OPENGL_33
*
* NOTE: This example also requires the following single-file header-only modules:
* glad.h - OpenGL extensions loader (stripped to only required extensions)
* raymath.h - Vector and matrix math functions
* stb_image.h - Multiple formats image loading (BMP, PNG, TGA, JPG...)
*
* Compile example using:
* gcc -o $(NAME_PART).exe $(FILE_NAME) -Iexternal -Iexternal/glfw/include \
* rglfw.o -lopengl32 -lgdi32 -Wall -std=c99
*
* Copyright (c) 2017-2018 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
#define GLAD_IMPLEMENTATION
#include "glad.h" // GLAD extensions loading library
// NOTE: Includes required OpenGL headers
#include <GLFW/glfw3.h> // Windows/Context and inputs management
#define RAYMATH_STANDALONE
#define RAYMATH_IMPLEMENTATION
#include "raymath.h" // Vector3 and Matrix math functions
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h" // Multiple image fileformats loading functions
#include <stdarg.h> // Required for TraceLog()
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
// LESSON 01: Basic data types
#ifndef __cplusplus
// Boolean type
typedef enum { false, true } bool;
#endif
// Color type, RGBA (32bit)
typedef struct Color {
unsigned char r;
unsigned char g;
unsigned char b;
unsigned char a;
} Color;
// Rectangle type
typedef struct Rectangle {
int x;
int y;
int width;
int height;
} Rectangle;
// Camera type, defines a camera position/orientation in 3d space
typedef struct Camera {
Vector3 position; // Camera position
Vector3 target; // Camera target it looks-at
Vector3 up; // Camera up vector (rotation over its axis)
float fovy; // Camera field-of-view apperture in Y (degrees)
} Camera;
typedef enum { LOG_INFO = 0, LOG_ERROR, LOG_WARNING, LOG_DEBUG, LOG_OTHER } TraceLogType;
// LESSON 03: Texture formats enum
typedef enum {
UNCOMPRESSED_GRAYSCALE = 1, // 8 bit per pixel (no alpha)
UNCOMPRESSED_GRAY_ALPHA, // 16 bpp (2 channels)
UNCOMPRESSED_R5G6B5, // 16 bpp
UNCOMPRESSED_R8G8B8, // 24 bpp
UNCOMPRESSED_R5G5B5A1, // 16 bpp (1 bit alpha)
UNCOMPRESSED_R4G4B4A4, // 16 bpp (4 bit alpha)
UNCOMPRESSED_R8G8B8A8, // 32 bpp
} TextureFormat;
// LESSON 03: Image struct (extended)
// NOTE: Image data is stored in CPU memory (RAM)
typedef struct Image {
unsigned int width; // Image base width
unsigned int height; // Image base height
unsigned int format; // Data format (TextureFormat type)
unsigned char *data; // Image raw data
} Image;
// LESSON 03: Texture2D type
// NOTE: Texture data is stored in GPU memory (VRAM)
typedef struct Texture2D {
unsigned int id; // OpenGL texture id
int width; // Texture base width
int height; // Texture base height
int mipmaps; // Mipmap levels, 1 by default
int format; // Data format (TextureFormat)
} Texture2D;
// LESSON 03: Shader type (default shader)
typedef struct Shader {
unsigned int id; // Shader program id
// Vertex attributes locations (default locations)
int vertexLoc; // Vertex attribute location point (default-location = 0)
int texcoordLoc; // Texcoord attribute location point (default-location = 1)
int normalLoc; // Normal attribute location point (default-location = 2)
// Uniform locations
int mvpLoc; // ModelView-Projection matrix uniform location point (vertex shader)
int colorLoc; // Diffuse color uniform location point (fragment shader)
int mapTextureLoc; // Map texture uniform location point (default-texture-unit = 0)
} Shader;
// LESSON 04: Vertex data defining a mesh
typedef struct Mesh {
int vertexCount; // number of vertices stored in arrays
float *vertices; // vertex position (XYZ - 3 components per vertex) (shader-location = 0)
float *texcoords; // vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1)
float *normals; // vertex normals (XYZ - 3 components per vertex) (shader-location = 2)
unsigned int vaoId; // OpenGL Vertex Array Object id
unsigned int vboId[3]; // OpenGL Vertex Buffer Objects id (3 types of vertex data supported)
} Mesh;
// LESSON 04: Material type
typedef struct Material {
Shader shader; // Default shader
Texture2D texDiffuse; // Diffuse texture
Color colDiffuse; // Diffuse color
} Material;
// LESSON 04: Model struct
// NOTE: Model is defined by its Mesh (vertex data), Material (shader) and transform matrix
typedef struct Model {
Mesh mesh; // Vertex data buffers (RAM and VRAM)
Matrix transform; // Local transform matrix
Material material; // Shader and textures data
} Model;
//----------------------------------------------------------------------------------
// Global Variables Declaration
//----------------------------------------------------------------------------------
GLFWwindow *window;
static Matrix matProjection; // Projection matrix to draw our world
static Matrix matModelview; // Modelview matrix to draw our world
static double currentTime, previousTime; // Used to track timmings
static double frameTime = 0.0; // Time measure for one frame
static double targetTime = 0.0; // Desired time for one frame, if 0 not applied
// LESSON 02: Keyboard/mouse input management
// Register keyboard/mouse states (current and previous)
static char previousKeyState[512] = { 0 }; // Registers previous frame key state
static char currentKeyState[512] = { 0 }; // Registers current frame key state
static char previousMouseState[3] = { 0 }; // Registers previous mouse button state
static char currentMouseState[3] = { 0 }; // Registers current mouse button state
// LESSON 03: Default texture (white) and shader
static Shader shdrDefault; // Default shader to draw (vertex and fragment processing)
static unsigned int quadId; // Quad VAO id to be used on texture drawing
//----------------------------------------------------------------------------------
// Module specific Functions Declaration
//----------------------------------------------------------------------------------
// GLFW3 callback functions to be registered: Error, Key, MouseButton, MouseCursor
static void ErrorCallback(int error, const char* description);
static void KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods);
static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods);
static void MouseCursorPosCallback(GLFWwindow *window, double x, double y);
void TraceLog(int msgType, const char *text, ...); // Show trace log messages (LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DEBUG)
// LESSON 01: Window and context creation, extensions loading
//----------------------------------------------------------------------------------
static void InitWindow(int width, int height); // Initialize window and context
static void InitGraphicsDevice(int width, int height); // Initialize graphic device
static void CloseWindow(void); // Close window and free resources
static void SetTargetFPS(int fps); // Set target FPS (maximum)
static void SyncFrame(void); // Synchronize to desired framerate
// LESSON 02: Inputs management (keyboard and mouse)
//----------------------------------------------------------------------------------
static bool IsKeyPressed(int key); // Detect if a key has been pressed once
static bool IsKeyDown(int key); // Detect if a key is being pressed (key held down)
static bool IsMouseButtonPressed(int button); // Detect if a mouse button has been pressed once
static bool IsMouseButtonDown(int button); // Detect if a mouse button is being pressed
static Vector2 GetMousePosition(void); // Returns mouse position XY
static void PollInputEvents(void); // Poll (store) all input events
// LESSON 03: Image data loading, texture creation and drawing
//----------------------------------------------------------------------------------
static unsigned int LoadQuad(float width, float height); // Load quad vertex data and return id
static Shader LoadShaderDefault(void); // Load default shader (basic shader)
static Image LoadImage(const char *fileName); // Load image data to CPU memory (RAM)
static void UnloadImage(Image image); // Unload image data from CPU memory (RAM)
static Color *GetImageData(Image image); // Get pixel data from image as Color array
static Texture2D LoadTexture(unsigned char *data, int width, int height, int format); // Load texture data in GPU memory (VRAM)
static void UnloadTexture(Texture2D texture); // Unload texture data from GPU memory (VRAM)
static void DrawTexture(Texture2D texture, Vector2 position, Color tint); // Draw texture in screen position coordinates
#define WHITE (Color){ 255, 255, 255, 255 }
// LESSON 04: Model loading, vertex buffer creation
//----------------------------------------------------------------------------------
static Mesh LoadOBJ(const char *fileName); // Load static mesh from OBJ file
static void UploadMeshData(Mesh *mesh); // Upload mesh data into VRAM
static Model LoadModel(Mesh mesh, Texture2D diffuse); // Load mesh data and texture into a 3d model
static void UnloadModel(Model model); // Unload model data from memory (RAM and VRAM)
static void DrawModel(Model model, Vector3 position, float scale, Color tint); // Draw model in screen
// LESSON 05: Cubicmap generation, loading and drawing
//----------------------------------------------------------------------------------
static Mesh GenMeshCubicmap(Image cubicmap, float cubeSize); // Generate cubicmap mesh from image data
//----------------------------------------------------------------------------------
// Main Entry point
//----------------------------------------------------------------------------------
int main(void)
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
// LESSON 02: Window and graphic device initialization and management
InitWindow(screenWidth, screenHeight); // Initialize Window using GLFW3
InitGraphicsDevice(screenWidth, screenHeight); // Initialize graphic device (OpenGL)
// LESSON 03: Init default Shader (customized for GL 3.3 and ES2)
shdrDefault = LoadShaderDefault();
// Define our camera
Camera camera;
camera.position = (Vector3){ 5.0f, 5.0f, 5.0f };
camera.target = Vector3Zero();
camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };
camera.fovy = 60.0f;
// LESSON 03: Calculate 2D projection matrix and model transform matrix
//matProjection = MatrixOrtho(0.0, screenWidth, screenHeight, 0.0, 0.0, 1.0);
//matModelview = MatrixIdentity();
// LESSON 04: Calculate 3D projection matrix (from perspective) and view matrix from camera look at
matProjection = MatrixPerspective(camera.fovy*DEG2RAD, (double)screenWidth/(double)screenHeight, 0.01, 1000.0);
matModelview = MatrixLookAt(camera.position, camera.target, camera.up);
// LESSON 04: Load 3d model
//Mesh mesh = LoadOBJ("resources/dwarf.obj"); // Load mesh data from OBJ file
//UploadMeshData(&mesh); // Upload mesh data to GPU memory (VRAM)
// LESSON 04: Load model diffuse texture
//Image image = LoadImage("resources/dwarf_diffuse.png");
//Texture2D texture = LoadTexture(image.data, image.width, image.height, image.format);
//UnloadImage(image);
// LESSON 05: Cubicmap generation
Image imMap = LoadImage("resources/map04.png");
Mesh mesh = GenMeshCubicmap(imMap, 1.0f);
UploadMeshData(&mesh);
UnloadImage(imMap);
// LESSON 05: Load cubicmap texture
Image image = LoadImage("resources/cubemap_atlas01.png");
Texture2D texture = LoadTexture(image.data, image.width, image.height, image.format);
UnloadImage(image);
Model model = LoadModel(mesh, texture);
Vector3 position = { -10, 0, 0}; // Model position on screen
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
// Main game loop
while (!glfwWindowShouldClose(window))
{
// Update
//----------------------------------------------------------------------------------
// ...
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear used buffers: Color and Depth (Depth is used for 3D)
// LESSON 03: Draw loaded texture on screen
//DrawTexture(texture, position, WHITE);
// LESSON 04: Draw loaded 3d model
DrawModel(model, position, 1.0f, WHITE);
glfwSwapBuffers(window); // Swap buffers: show back buffer into front
PollInputEvents(); // Register input events (keyboard, mouse)
SyncFrame(); // Wait required time to target framerate
//----------------------------------------------------------------------------------
}
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadModel(model); // Unload model data (includes texture unloading)
CloseWindow();
//--------------------------------------------------------------------------------------
return 0;
}
//----------------------------------------------------------------------------------
// Module specific Functions Definition
//----------------------------------------------------------------------------------
// GLFW3: Error callback function
static void ErrorCallback(int error, const char* description)
{
TraceLog(LOG_ERROR, description);
}
// GLFW3: Keyboard callback function
static void KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
{
glfwSetWindowShouldClose(window, GL_TRUE);
}
else currentKeyState[key] = action;
}
// GLFW3: Mouse buttons callback function
static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods)
{
currentMouseState[button] = action;
}
// GLFW3: Mouse cursor callback function
static void MouseCursorPosCallback(GLFWwindow *window, double x, double y)
{
//mousePosition.x = (float)x;
//mousePosition.y = (float)y;
}
// Show trace log messages (LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DEBUG)
void TraceLog(int msgType, const char *text, ...)
{
va_list args;
va_start(args, text);
switch (msgType)
{
case LOG_INFO: fprintf(stdout, "INFO: "); break;
case LOG_ERROR: fprintf(stdout, "ERROR: "); break;
case LOG_WARNING: fprintf(stdout, "WARNING: "); break;
case LOG_DEBUG: fprintf(stdout, "DEBUG: "); break;
default: break;
}
vfprintf(stdout, text, args);
fprintf(stdout, "\n");
va_end(args);
if (msgType == LOG_ERROR) exit(1);
}
// LESSON 01: Window and context creation, extensions loading
//----------------------------------------------------------------------------------
// Initialize window and context (OpenGL 3.3)
static void InitWindow(int width, int height)
{
// GLFW3 Initialization + OpenGL 3.3 Context + Extensions
glfwSetErrorCallback(ErrorCallback);
if (!glfwInit()) TraceLog(LOG_WARNING, "GLFW3: Can not initialize GLFW");
else TraceLog(LOG_INFO, "GLFW3: GLFW initialized successfully");
glfwWindowHint(GLFW_SAMPLES, 4);
glfwWindowHint(GLFW_DEPTH_BITS, 16);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE);
window = glfwCreateWindow(width, height, "CHALLENGE 03: 3D MAZE GAME", NULL, NULL);
if (!window) glfwTerminate();
else TraceLog(LOG_INFO, "GLFW3: Window created successfully");
glfwSetWindowPos(window, 200, 200);
glfwSetKeyCallback(window, KeyCallback); // Track keyboard events
glfwSetMouseButtonCallback(window, MouseButtonCallback); // Track mouse button events
glfwSetCursorPosCallback(window, MouseCursorPosCallback); // Track mouse position changes
//glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);// Disable cursor for first person camera
glfwMakeContextCurrent(window);
glfwSwapInterval(1);
}
// Initialize graphic device (OpenGL 3.3)
static void InitGraphicsDevice(int width, int height)
{
// Load OpenGL 3.3 supported extensions
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) TraceLog(LOG_WARNING, "GLAD: Cannot load OpenGL extensions");
else TraceLog(LOG_INFO, "GLAD: OpenGL extensions loaded successfully");
// Print current OpenGL and GLSL version
TraceLog(LOG_INFO, "GPU: Vendor: %s", glGetString(GL_VENDOR));
TraceLog(LOG_INFO, "GPU: Renderer: %s", glGetString(GL_RENDERER));
TraceLog(LOG_INFO, "GPU: Version: %s", glGetString(GL_VERSION));
TraceLog(LOG_INFO, "GPU: GLSL: %s", glGetString(GL_SHADING_LANGUAGE_VERSION));
// Initialize OpenGL context (states and resources)
//----------------------------------------------------------
// Init state: Depth test
glDepthFunc(GL_LEQUAL); // Type of depth testing to apply
glEnable(GL_DEPTH_TEST); // Enable depth testing for 3D
// Init state: Blending mode
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Color blending function (how colors are mixed)
glEnable(GL_BLEND); // Enable color blending (required to work with transparencies)
// Init state: Culling
// NOTE: All shapes/models triangles are drawn CCW
glCullFace(GL_BACK); // Cull the back face (default)
glFrontFace(GL_CCW); // Front face are defined counter clockwise (default)
glEnable(GL_CULL_FACE); // Enable backface culling
// Init state: Color/Depth buffers clear
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Set clear color (black)
glClearDepth(1.0f); // Set clear depth value (default)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear color and depth buffers (depth buffer required for 3D)
TraceLog(LOG_INFO, "OpenGL default states initialized successfully");
// Initialize viewport
glViewport(0, 0, width, height);
// Init internal matProjection and matModelview matrices
matProjection = MatrixIdentity();
matModelview = MatrixIdentity();
}
// Close window and free resources
static void CloseWindow(void)
{
// LESSON 03: Unload default shader
glUseProgram(0);
glDeleteProgram(shdrDefault.id);
glfwDestroyWindow(window); // Close window
glfwTerminate(); // Free GLFW3 resources
}
// Set target FPS (maximum)
static void SetTargetFPS(int fps)
{
if (fps < 1) targetTime = 0.0;
else targetTime = 1.0/(double)fps;
}
// Synchronize to desired framerate
static void SyncFrame(void)
{
// Frame time control system
currentTime = glfwGetTime();
frameTime = currentTime - previousTime;
previousTime = currentTime;
// Wait for some milliseconds...
if (frameTime < targetTime)
{
double prevTime = glfwGetTime();
double nextTime = 0.0;
// Busy wait loop
while ((nextTime - prevTime) < (targetTime - frameTime)) nextTime = glfwGetTime();
currentTime = glfwGetTime();
double extraTime = currentTime - previousTime;
previousTime = currentTime;
frameTime += extraTime;
}
}
// LESSON 02: Inputs management (keyboard and mouse)
//----------------------------------------------------------------------------------
// Detect if a key is being pressed (key held down)
static bool IsKeyDown(int key)
{
return glfwGetKey(window, key);
}
// Detect if a key has been pressed once
static bool IsKeyPressed(int key)
{
if ((currentKeyState[key] != previousKeyState[key]) && (currentKeyState[key] == 1)) return true;
else return false;
}
// Detect if a mouse button is being pressed
static bool IsMouseButtonDown(int button)
{
return glfwGetMouseButton(window, button);
}
// Detect if a mouse button has been pressed once
static bool IsMouseButtonPressed(int button)
{
if ((currentMouseState[button] != previousMouseState[button]) && (currentMouseState[button] == 1)) return true;
else return false;
}
// Returns mouse position XY
static Vector2 GetMousePosition(void)
{
Vector2 mousePosition;
double mouseX;
double mouseY;
glfwGetCursorPos(window, &mouseX, &mouseY);
mousePosition.x = (float)mouseX;
mousePosition.y = (float)mouseY;
return mousePosition;
}
// Poll (store) all input events
static void PollInputEvents(void)
{
// Register previous keys states and mouse button states
for (int i = 0; i < 512; i++) previousKeyState[i] = currentKeyState[i];
for (int i = 0; i < 3; i++) previousMouseState[i] = currentMouseState[i];
// Input events polling (managed by GLFW3 through callback)
glfwPollEvents();
}
// LESSON 03: Image data loading, texture creation and drawing
//----------------------------------------------------------------------------------
// Load default shader
static Shader LoadShaderDefault(void)
{
Shader shader = { 0 };
// STEP 01: Define shader code
// NOTE: It can be defined in external text file and just loaded
//-------------------------------------------------------------------------------
// Vertex shader directly defined, no external file required
char vDefaultShaderStr[] =
"#version 330 \n"
"in vec3 vertexPosition; \n"
"in vec2 vertexTexCoord; \n"
"in vec3 vertexNormal; \n"
"out vec2 fragTexCoord; \n"
"out vec3 fragNormal; \n"
"uniform mat4 mvp; \n"
"void main() \n"
"{ \n"
" fragTexCoord = vertexTexCoord; \n"
" fragNormal = vertexNormal; \n"
" gl_Position = mvp*vec4(vertexPosition, 1.0); \n"
"} \n";
// Fragment shader directly defined, no external file required
char fDefaultShaderStr[] =
"#version 330 \n"
"in vec2 fragTexCoord; \n"
"in vec3 fragNormal; \n"
"out vec4 finalColor; \n"
"uniform sampler2D texture0; \n"
"uniform vec4 colDiffuse; \n"
"void main() \n"
"{ \n"
" vec4 texelColor = texture(texture0, fragTexCoord); \n"
" finalColor = texelColor*colDiffuse; \n"
"} \n";
// STEP 02: Load shader program
// NOTE: Vertex shader and fragment shader are compiled at runtime
//-------------------------------------------------------------------------------
GLuint vertexShader;
GLuint fragmentShader;
vertexShader = glCreateShader(GL_VERTEX_SHADER);
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
const char *pvs = vDefaultShaderStr;
const char *pfs = fDefaultShaderStr;
glShaderSource(vertexShader, 1, &pvs, NULL);
glShaderSource(fragmentShader, 1, &pfs, NULL);
glCompileShader(vertexShader);
glCompileShader(fragmentShader);
shader.id = glCreateProgram();
glAttachShader(shader.id, vertexShader);
glAttachShader(shader.id, fragmentShader);
// Default attribute shader locations must be binded before linking
glBindAttribLocation(shader.id, 0, "vertexPosition");
glBindAttribLocation(shader.id, 1, "vertexTexCoord");
glBindAttribLocation(shader.id, 2, "vertexNormal");
// NOTE: If some attrib name is not found in the shader, it locations becomes -1
glLinkProgram(shader.id);
// NOTE: All uniform variables are intitialised to 0 when a program links
// Shaders already compiled into program, not required any more
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
if (shader.id != 0) TraceLog(LOG_INFO, "[SHDR ID %i] Default shader loaded successfully", shader.id);
else TraceLog(LOG_WARNING, "[SHDR ID %i] Default shader could not be loaded", shader.id);
// STEP 03: Load default shader locations
// NOTE: Connection points (locations) between shader and our code must be retrieved
//-----------------------------------------------------------------------------------
if (shader.id != 0)
{
// NOTE: Default shader attrib locations have been fixed before linking:
// vertex position location = 0
// vertex texcoord location = 1
// vertex normal location = 2
// Get handles to GLSL input attibute locations
shader.vertexLoc = glGetAttribLocation(shader.id, "vertexPosition");
shader.texcoordLoc = glGetAttribLocation(shader.id, "vertexTexCoord");
shader.normalLoc = glGetAttribLocation(shader.id, "vertexNormal");
// Get handles to GLSL uniform locations (vertex shader)
shader.mvpLoc = glGetUniformLocation(shader.id, "mvp");
// Get handles to GLSL uniform locations (fragment shader)
shader.colorLoc = glGetUniformLocation(shader.id, "colDiffuse");
shader.mapTextureLoc = glGetUniformLocation(shader.id, "texture0");
}
return shader;
}
// Load image data to CPU memory (RAM)
// NOTE: We use stb_image library to support multiple fileformats
static Image LoadImage(const char *fileName)
{
Image image = { 0 };
const char *fileExt;
if ((fileExt = strrchr(fileName, '.')) != NULL)
{
// Check if file extension is supported
if ((strcmp(fileExt, ".bmp") == 0) ||
(strcmp(fileExt, ".png") == 0) ||
(strcmp(fileExt, ".tga") == 0) ||
(strcmp(fileExt, ".jpg") == 0) ||
(strcmp(fileExt, ".gif") == 0) ||
(strcmp(fileExt, ".psd") == 0))
{
int imgWidth = 0;
int imgHeight = 0;
int imgBpp = 0;
FILE *imFile = fopen(fileName, "rb");
if (imFile != NULL)
{
// NOTE: Using stb_image to load images (Supports: BMP, TGA, PNG, JPG, ...)
image.data = stbi_load_from_file(imFile, &imgWidth, &imgHeight, &imgBpp, 4);
fclose(imFile);
image.width = imgWidth;
image.height = imgHeight;
image.format = UNCOMPRESSED_R8G8B8A8;
TraceLog(LOG_INFO, "Image loaded successfully (%ix%i)", image.width, image.height);
}
}
}
return image;
}
// Unload image data from CPU memory (RAM)
static void UnloadImage(Image image)
{
if (image.data != NULL) free(image.data);
}
// Get pixel data from image as Color array
static Color *GetImageData(Image image)
{
Color *pixels = (Color *)malloc(image.width*image.height*sizeof(Color));
int k = 0;
for (int i = 0; i < image.width*image.height; i++)
{
switch (image.format)
{
case UNCOMPRESSED_GRAYSCALE:
{
pixels[i].r = ((unsigned char *)image.data)[k];
pixels[i].g = ((unsigned char *)image.data)[k];
pixels[i].b = ((unsigned char *)image.data)[k];
pixels[i].a = 255;
k++;
} break;
case UNCOMPRESSED_GRAY_ALPHA:
{
pixels[i].r = ((unsigned char *)image.data)[k];
pixels[i].g = ((unsigned char *)image.data)[k];
pixels[i].b = ((unsigned char *)image.data)[k];
pixels[i].a = ((unsigned char *)image.data)[k + 1];
k += 2;
} break;
case UNCOMPRESSED_R5G5B5A1:
{
unsigned short pixel = ((unsigned short *)image.data)[k];
pixels[i].r = (unsigned char)((float)((pixel & 0b1111100000000000) >> 11)*(255/31));
pixels[i].g = (unsigned char)((float)((pixel & 0b0000011111000000) >> 6)*(255/31));
pixels[i].b = (unsigned char)((float)((pixel & 0b0000000000111110) >> 1)*(255/31));
pixels[i].a = (unsigned char)((pixel & 0b0000000000000001)*255);
k++;
} break;
case UNCOMPRESSED_R5G6B5:
{
unsigned short pixel = ((unsigned short *)image.data)[k];
pixels[i].r = (unsigned char)((float)((pixel & 0b1111100000000000) >> 11)*(255/31));
pixels[i].g = (unsigned char)((float)((pixel & 0b0000011111100000) >> 5)*(255/63));
pixels[i].b = (unsigned char)((float)(pixel & 0b0000000000011111)*(255/31));
pixels[i].a = 255;
k++;
} break;
case UNCOMPRESSED_R4G4B4A4:
{
unsigned short pixel = ((unsigned short *)image.data)[k];
pixels[i].r = (unsigned char)((float)((pixel & 0b1111000000000000) >> 12)*(255/15));
pixels[i].g = (unsigned char)((float)((pixel & 0b0000111100000000) >> 8)*(255/15));
pixels[i].b = (unsigned char)((float)((pixel & 0b0000000011110000) >> 4)*(255/15));
pixels[i].a = (unsigned char)((float)(pixel & 0b0000000000001111)*(255/15));
k++;
} break;
case UNCOMPRESSED_R8G8B8A8:
{
pixels[i].r = ((unsigned char *)image.data)[k];
pixels[i].g = ((unsigned char *)image.data)[k + 1];
pixels[i].b = ((unsigned char *)image.data)[k + 2];
pixels[i].a = ((unsigned char *)image.data)[k + 3];
k += 4;
} break;
case UNCOMPRESSED_R8G8B8:
{
pixels[i].r = (unsigned char)((unsigned char *)image.data)[k];
pixels[i].g = (unsigned char)((unsigned char *)image.data)[k + 1];
pixels[i].b = (unsigned char)((unsigned char *)image.data)[k + 2];
pixels[i].a = 255;
k += 3;
} break;
default: TraceLog(LOG_WARNING, "Format not supported for pixel data retrieval"); break;
}
}
return pixels;
}
// Load texture data in GPU memory (VRAM)
static Texture2D LoadTexture(unsigned char *data, int width, int height, int format)
{
Texture2D texture = { 0 };
// NOTE: Texture2D struct is defined inside rlgl
texture.width = width;
texture.height = height;
texture.format = UNCOMPRESSED_R8G8B8A8;
texture.mipmaps = 1;
glBindTexture(GL_TEXTURE_2D, 0); // Free any old binding
glGenTextures(1, &texture.id); // Generate Pointer to the texture
glBindTexture(GL_TEXTURE_2D, texture.id);
switch (format)
{
case UNCOMPRESSED_GRAYSCALE:
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, (unsigned char *)data);
// With swizzleMask we define how a one channel texture will be mapped to RGBA
// Required GL >= 3.3 or EXT_texture_swizzle/ARB_texture_swizzle
GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_ONE };
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask);
TraceLog(LOG_INFO, "[TEX ID %i] Grayscale texture loaded and swizzled", texture.id);
} break;
case UNCOMPRESSED_GRAY_ALPHA:
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RG8, width, height, 0, GL_RG, GL_UNSIGNED_BYTE, (unsigned char *)data);
GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_GREEN };
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask);
} break;
case UNCOMPRESSED_R5G6B5: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB565, width, height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, (unsigned short *)data); break;
case UNCOMPRESSED_R8G8B8: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, (unsigned char *)data); break;
case UNCOMPRESSED_R5G5B5A1: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB5_A1, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, (unsigned short *)data); break;
case UNCOMPRESSED_R4G4B4A4: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA4, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, (unsigned short *)data); break;
case UNCOMPRESSED_R8G8B8A8: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (unsigned char *)data); break;
default: TraceLog(LOG_WARNING, "Texture format not recognized"); break;
}
// Configure texture parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // Set texture to repeat on x-axis
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // Set texture to repeat on y-axis
// Magnification and minification filters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Alternative: GL_LINEAR
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // Alternative: GL_LINEAR
// Unbind current texture
glBindTexture(GL_TEXTURE_2D, 0);
if (texture.id > 0) TraceLog(LOG_INFO, "[TEX ID %i] Texture created successfully (%ix%i)", texture.id, width, height);
else TraceLog(LOG_WARNING, "Texture could not be created");
return texture;
}
// Unload texture data from GPU memory (VRAM)
static void UnloadTexture(Texture2D texture)
{
if (texture.id > 0) glDeleteTextures(1, &texture.id);
}
// Draw texture in screen position coordinates
static void DrawTexture(Texture2D texture, Vector2 position, Color tint)
{
glUseProgram(shdrDefault.id);
// Define translation matrix to translate quad to screen center
matModelview = MatrixTranslate(position.x, position.y, 0);
// Create modelview-projection matrix
Matrix matMVP = MatrixMultiply(matModelview, matProjection);
glUniformMatrix4fv(shdrDefault.mvpLoc, 1, false, MatrixToFloat(matMVP));
glUniform4f(shdrDefault.colorLoc, 1.0f, 1.0f, 1.0f, 1.0f);
glUniform1i(shdrDefault.mapTextureLoc, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture.id);
glBindVertexArray(quadId);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindTexture(GL_TEXTURE_2D, 0); // Unbind textures
glBindVertexArray(0); // Unbind VAO
glUseProgram(0); // Unbind shader program
}
// Load a quad to draw a texture
// NOTE: We need to define positions, coordinates and normals (optional)
static unsigned int LoadQuad(float width, float height)
{
unsigned int quadVAO = 0; // Quad VAO id
unsigned int quadVBO = 0; // Quad VBO id for individual buffer
float vertices[] = {
// Positions - Texture Coords
0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.0f, height, 0.0f, 0.0f, 1.0f,
width, 0.0f, 0.0f, 1.0f, 0.0f,
width, height, 0.0f, 1.0f, 1.0f,
};
// Generate quad VAO/VBO ids
glGenVertexArrays(1, &quadVAO);
glGenBuffers(1, &quadVBO);
glBindVertexArray(quadVAO);
// Fill VBO buffer
glBindBuffer(GL_ARRAY_BUFFER, quadVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), &vertices, GL_STATIC_DRAW);
// Link vertex attributes
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5*sizeof(float), (void *)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5*sizeof(float), (void *)(3*sizeof(float)));
return quadVAO;
}
// LESSON 04: Model loading, vertex buffer creation
//----------------------------------------------------------------------------------
// Load static mesh from OBJ file (RAM)
static Mesh LoadOBJ(const char *fileName)
{
Mesh mesh = { 0 };
char dataType;
char comments[200];
int vertexCount = 0;
int normalCount = 0;
int texcoordCount = 0;
int triangleCount = 0;
FILE *objFile = fopen(fileName, "rt");
if (objFile == NULL)
{
TraceLog(LOG_WARNING, "[%s] OBJ file could not be opened", fileName);
return mesh;
}
// First reading pass: Get vertexCount, normalCount, texcoordCount, triangleCount
// NOTE: vertex, texcoords and normals could be optimized (to be used indexed on faces definition)
// NOTE: faces MUST be defined as TRIANGLES (3 vertex per face)
while (!feof(objFile))
{
dataType = '\0';
fscanf(objFile, "%c", &dataType);
switch (dataType)
{
case '#': // Comments
case 'o': // Object name (One OBJ file can contain multible named meshes)
case 'g': // Group name
case 's': // Smoothing level
case 'm': // mtllib [external .mtl file name]
case 'u': // usemtl [material name]
{
fgets(comments, 200, objFile);
} break;
case 'v':
{
fscanf(objFile, "%c", &dataType);
if (dataType == 't') // Read texCoord
{
texcoordCount++;
fgets(comments, 200, objFile);
}
else if (dataType == 'n') // Read normals
{
normalCount++;
fgets(comments, 200, objFile);
}
else // Read vertex
{
vertexCount++;
fgets(comments, 200, objFile);
}
} break;
case 'f':
{
triangleCount++;
fgets(comments, 200, objFile);
} break;
default: break;
}
}
TraceLog(LOG_DEBUG, "[%s] Mesh vertices: %i", fileName, vertexCount);
TraceLog(LOG_DEBUG, "[%s] Mesh texcoords: %i", fileName, texcoordCount);
TraceLog(LOG_DEBUG, "[%s] Mesh normals: %i", fileName, normalCount);
TraceLog(LOG_DEBUG, "[%s] Mesh triangles: %i", fileName, triangleCount);
// Once we know the number of vertices to store, we create required arrays
Vector3 *midVertices = (Vector3 *)malloc(vertexCount*sizeof(Vector3));
Vector3 *midNormals = NULL;
if (normalCount > 0) midNormals = (Vector3 *)malloc(normalCount*sizeof(Vector3));
Vector2 *midTexCoords = NULL;
if (texcoordCount > 0) midTexCoords = (Vector2 *)malloc(texcoordCount*sizeof(Vector2));