Basic 3D rendering
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
obj/*
|
||||
Game
|
||||
*.xcf
|
||||
|
||||
13
res/glsl/fragment.glsl
Normal file
13
res/glsl/fragment.glsl
Normal file
@@ -0,0 +1,13 @@
|
||||
#version 450 core
|
||||
|
||||
in vec3 textureCoord;
|
||||
|
||||
out vec4 color;
|
||||
|
||||
uniform sampler2DArray textureSampler;
|
||||
|
||||
void main()
|
||||
{
|
||||
color = texture(textureSampler, textureCoord);
|
||||
if (color.w < 0.1) discard;
|
||||
}
|
||||
17
res/glsl/vertex.glsl
Normal file
17
res/glsl/vertex.glsl
Normal file
@@ -0,0 +1,17 @@
|
||||
#version 450 core
|
||||
|
||||
layout (location = 0) in vec3 position;
|
||||
layout (location = 1) in vec2 texCoord;
|
||||
layout (location = 2) in float texIndex;
|
||||
layout (location = 3) in mat4 theModelMatrix;
|
||||
|
||||
out vec3 textureCoord;
|
||||
|
||||
uniform mat4 theViewMatrix;
|
||||
uniform mat4 theProjMatrix;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = theProjMatrix * theViewMatrix * theModelMatrix * vec4(position, 1.0);
|
||||
textureCoord = vec3(1.0 - texCoord.x, 1.0 - texCoord.y, texIndex);
|
||||
}
|
||||
BIN
res/tex/texture.png
Normal file
BIN
res/tex/texture.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.4 KiB |
53
src/camera.c
Normal file
53
src/camera.c
Normal file
@@ -0,0 +1,53 @@
|
||||
#include <math.h>
|
||||
#include "camera.h"
|
||||
|
||||
void Camera_Init(Camera* c)
|
||||
{
|
||||
c->position[0] = -50.0f;
|
||||
c->position[1] = 130.0f;
|
||||
c->position[2] = 0.0f;
|
||||
c->width[0] = 0.6f;
|
||||
c->width[1] = 5.8f;
|
||||
c->width[2] = 0.6f;
|
||||
glm_vec3_zero(c->velocity);
|
||||
glm_vec3_zero(c->look);
|
||||
glm_vec3_zero(c->up);
|
||||
glm_vec3_zero(c->front);
|
||||
glm_vec3_zero(c->right);
|
||||
glm_vec3_zero(c->rotation);
|
||||
}
|
||||
|
||||
void Camera_GetViewMatrix(Camera* c, mat4 m)
|
||||
{
|
||||
glm_lookat(c->position, c->look, c->up, m);
|
||||
}
|
||||
|
||||
void Camera_UpdateVectors(Camera* c)
|
||||
{
|
||||
if (c->rotation[0] > 180.0f) c->rotation[0] -= 360.0f;
|
||||
else if (c->rotation[0] <= -180.0f) c->rotation[0] += 360.0f;
|
||||
|
||||
c->rotation[1] = glm_clamp(c->rotation[1], -89.0f, 89.0f);
|
||||
c->rotation[2] = glm_clamp(c->rotation[2], -30.0f, 30.0f);
|
||||
|
||||
// calculate the front facing unit vector by yaw and pitch
|
||||
c->front[0] = cos(glm_rad(c->rotation[0])) * cos(glm_rad(c->rotation[1]));
|
||||
c->front[1] = sin(glm_rad(c->rotation[1]));
|
||||
c->front[2] = sin(glm_rad(c->rotation[0])) * cos(glm_rad(c->rotation[1]));
|
||||
|
||||
// the look vector is the position + front
|
||||
glm_vec3_add(c->position, c->front, c->look);
|
||||
|
||||
// reset the up vector
|
||||
c->up[0] = 0.0f;
|
||||
c->up[1] = 1.0f;
|
||||
c->up[2] = 0.0f;
|
||||
// cross product points to the right, perpendicular to both front and up
|
||||
glm_vec3_cross(c->front, c->up, c->right);
|
||||
// pitch up or down, around the axis of the cross product
|
||||
glm_vec3_rotate(c->up, glm_rad(c->rotation[1]), c->right);
|
||||
// roll around the axis of the front vector
|
||||
glm_vec3_rotate(c->up, glm_rad(c->rotation[2]), c->front);
|
||||
// recalculate right vector after rolling
|
||||
glm_vec3_cross(c->front, c->up, c->right);
|
||||
}
|
||||
19
src/camera.h
Normal file
19
src/camera.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "cglm/cglm.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
vec3 position;
|
||||
vec3 width;
|
||||
vec3 velocity;
|
||||
vec3 look;
|
||||
vec3 up;
|
||||
vec3 front;
|
||||
vec3 right;
|
||||
vec3 rotation;
|
||||
} Camera;
|
||||
|
||||
void Camera_Init(Camera* c);
|
||||
void Camera_GetViewMatrix(Camera* c, mat4 m);
|
||||
void Camera_UpdateVectors(Camera* c);
|
||||
37
src/game.c
37
src/game.c
@@ -1,32 +1,15 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include "SDL2/SDL.h"
|
||||
#include "SDL2/SDL_timer.h"
|
||||
#include "GL/glew.h"
|
||||
#include "SDL2/SDL_opengl.h"
|
||||
#include "game.h"
|
||||
#include "render.h"
|
||||
#include "camera.h"
|
||||
|
||||
GameState *Game_New()
|
||||
{
|
||||
GameState *gs = calloc(1, sizeof(GameState));
|
||||
|
||||
SDL_Init(SDL_INIT_VIDEO);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 5);
|
||||
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
|
||||
|
||||
gs->window = SDL_CreateWindow("Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1920, 1080, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);;
|
||||
gs->glContext = SDL_GL_CreateContext(gs->window);
|
||||
printf("Created OpenGL window.\n");
|
||||
|
||||
glewInit();
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glEnable(GL_CULL_FACE);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
printf("Initialized OpenGL.\n");
|
||||
if (!Render_Init(gs)) return NULL;
|
||||
|
||||
gs->running = true;
|
||||
return gs;
|
||||
@@ -34,12 +17,20 @@ GameState *Game_New()
|
||||
|
||||
void Game_Update(GameState *gs)
|
||||
{
|
||||
SDL_Delay(100);
|
||||
vec3 move;
|
||||
glm_vec3_zero(move);
|
||||
if (gs->input.w) move[0] = 0.3f;
|
||||
else if (gs->input.s) move[0] = -0.3f;
|
||||
if (gs->input.a) move[1] = 0.3f;
|
||||
else if (gs->input.d) move[1] = -0.3f;
|
||||
glm_vec3_add(gs->camera.position, move, gs->camera.position);
|
||||
|
||||
Camera_UpdateVectors(&gs->camera);
|
||||
SDL_Delay(5);
|
||||
}
|
||||
|
||||
void Game_Destroy(GameState *gs)
|
||||
{
|
||||
SDL_GL_DeleteContext(gs->glContext);
|
||||
SDL_DestroyWindow(gs->window);
|
||||
Render_Destroy(gs);
|
||||
free(gs);
|
||||
}
|
||||
|
||||
19
src/game.h
19
src/game.h
@@ -1,12 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "SDL2/SDL.h"
|
||||
#include "cglm/cglm.h"
|
||||
#include "camera.h"
|
||||
#include "shape.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
bool w;
|
||||
bool a;
|
||||
bool s;
|
||||
bool d;
|
||||
} InputState;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
bool running;
|
||||
InputState input;
|
||||
SDL_Window *window;
|
||||
SDL_GLContext *glContext;
|
||||
GLuint textureId;
|
||||
int shaderProgramId;
|
||||
Camera camera;
|
||||
Shape *testShape;
|
||||
mat4 projMatrix;
|
||||
mat4 viewMatrix;
|
||||
} GameState;
|
||||
|
||||
GameState *Game_New();
|
||||
|
||||
36
src/input.c
36
src/input.c
@@ -1,5 +1,6 @@
|
||||
#include "SDL2/SDL.h"
|
||||
#include "input.h"
|
||||
#include "camera.h"
|
||||
#include "game.h"
|
||||
|
||||
static void HandleKeyDown(GameState *gs, SDL_KeyCode sym)
|
||||
@@ -21,11 +22,40 @@ static void HandleKeyDown(GameState *gs, SDL_KeyCode sym)
|
||||
SDL_SetWindowFullscreen(gs->window, windowFlags);
|
||||
}
|
||||
break;
|
||||
|
||||
case SDLK_w: gs->input.w = true; break;
|
||||
case SDLK_a: gs->input.a = true; break;
|
||||
case SDLK_s: gs->input.s = true; break;
|
||||
case SDLK_d: gs->input.d = true; break;
|
||||
}
|
||||
}
|
||||
|
||||
static void HandleKeyUp(GameState *gs, SDL_KeyCode sym)
|
||||
{
|
||||
switch (sym)
|
||||
{
|
||||
case SDLK_w: gs->input.w = false; break;
|
||||
case SDLK_a: gs->input.a = false; break;
|
||||
case SDLK_s: gs->input.s = false; break;
|
||||
case SDLK_d: gs->input.d = false; break;
|
||||
}
|
||||
}
|
||||
|
||||
static void HandleMouseMotion(SDL_MouseMotionEvent e, GameState* gs)
|
||||
{
|
||||
const float sensitivity = 0.4f;
|
||||
Camera *camera = &gs->camera;
|
||||
camera->rotation[0] += e.xrel * sensitivity;
|
||||
camera->rotation[1] -= e.yrel * sensitivity;
|
||||
}
|
||||
|
||||
static void HandleMouseDown(SDL_MouseButtonEvent e, GameState* gs)
|
||||
{
|
||||
switch (e.button)
|
||||
{
|
||||
case 1:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Input_Poll(GameState *gs)
|
||||
@@ -49,6 +79,12 @@ void Input_Poll(GameState *gs)
|
||||
case SDL_KEYUP:
|
||||
HandleKeyUp(gs, sdlEvent.key.keysym.sym);
|
||||
break;
|
||||
case SDL_MOUSEMOTION:
|
||||
HandleMouseMotion(sdlEvent.motion, gs);
|
||||
break;
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
HandleMouseDown(sdlEvent.button, gs);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "SDL2/SDL.h"
|
||||
#include "game.h"
|
||||
#include "input.h"
|
||||
#include "render.h"
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
@@ -18,6 +19,7 @@ int main(int argc, char* argv[])
|
||||
{
|
||||
Input_Poll(gs);
|
||||
Game_Update(gs);
|
||||
Render_Draw(gs);
|
||||
}
|
||||
|
||||
printf("Shutting down...\n");
|
||||
|
||||
336
src/render.c
Normal file
336
src/render.c
Normal file
@@ -0,0 +1,336 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "SDL2/SDL.h"
|
||||
#include "GL/glew.h"
|
||||
#include "SDL2/SDL_opengl.h"
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "stb/stb_image.h"
|
||||
#include "cglm/cglm.h"
|
||||
#include "render.h"
|
||||
#include "camera.h"
|
||||
#include "game.h"
|
||||
#include "shape.h"
|
||||
|
||||
static void LoadTextureArray(GLuint texture, const char* filePath, int nCols, int nRows)
|
||||
{
|
||||
const int nMipmaps = 1;
|
||||
int nTiles = nCols * nRows;
|
||||
int tWidth, tHeight, tChannels;
|
||||
|
||||
unsigned char* textureBytes = stbi_load(filePath, &tWidth, &tHeight, &tChannels, 0);
|
||||
|
||||
tWidth /= nCols;
|
||||
tHeight /= nRows;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, texture);
|
||||
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, tWidth, tHeight, nTiles, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
||||
|
||||
int tBytes = 4 * tWidth * tHeight;
|
||||
unsigned char* buffer = malloc(tBytes * sizeof(unsigned char));
|
||||
|
||||
for (int z = 0; z < nTiles; z++)
|
||||
{
|
||||
for (int b = 0; b < tBytes; b++)
|
||||
{
|
||||
int bWidth = 4 * tWidth; // width of each tile in bytes
|
||||
int i = ((z / nCols) * nCols * bWidth * tHeight) // size (in bytes) of all tiles above
|
||||
+ ((b / bWidth) * bWidth * nCols) // size of pixel rows above, within the current tile row
|
||||
+ ((z % nCols) * bWidth) // width of tiles to the left
|
||||
+ (b % bWidth); // width from the left side of the current tile
|
||||
|
||||
buffer[b] = textureBytes[i];
|
||||
}
|
||||
|
||||
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, z, tWidth, tHeight, 1, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glGenerateMipmap(GL_TEXTURE_2D_ARRAY);
|
||||
|
||||
stbi_image_free(textureBytes);
|
||||
}
|
||||
|
||||
static void LoadTexture(GameState* gs)
|
||||
{
|
||||
glGenTextures(1, &gs->textureId);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glUniform1i(glGetUniformLocation(gs->shaderProgramId, "textureSampler"), 0);
|
||||
LoadTextureArray(gs->textureId, "res/tex/texture.png", 8, 4);
|
||||
printf("Loaded the texture array.\n");
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
static char **ReadFileLines(const char *path, int *n)
|
||||
{
|
||||
const int MAX_LINES = 128;
|
||||
const int MAX_LINE_LENGTH = 256;
|
||||
|
||||
FILE *file = fopen(path, "r");
|
||||
char line[MAX_LINE_LENGTH];
|
||||
char **lines = calloc(MAX_LINES, sizeof(char*));
|
||||
*n = 0;
|
||||
|
||||
if (lines == NULL) return NULL;
|
||||
|
||||
while (fgets(&(line[0]), MAX_LINE_LENGTH, file) != NULL && *n < MAX_LINES)
|
||||
{
|
||||
GLchar *l = strdup(&(line[0]));
|
||||
if (l == NULL) return NULL;
|
||||
lines[*n] = l;
|
||||
(*n)++;
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
int s = sizeof(char*) * (*n);
|
||||
lines = realloc(lines, s);
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
static void FreeLines(char **lines, int n)
|
||||
{
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
free(lines[i]);
|
||||
}
|
||||
|
||||
free(lines);
|
||||
}
|
||||
|
||||
static int CompileShader(GLchar *sourcePath, GLenum shaderType, char ***outCode, int *outNumLines)
|
||||
{
|
||||
GLint success;
|
||||
*outCode = ReadFileLines(sourcePath, outNumLines);
|
||||
GLuint shaderId = glCreateShader(shaderType);
|
||||
glShaderSource(shaderId, *outNumLines, (void*)(*outCode), NULL);
|
||||
glCompileShader(shaderId);
|
||||
glGetShaderiv(shaderId, GL_COMPILE_STATUS, &success);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
GLchar log[512];
|
||||
glGetShaderInfoLog(shaderId, 512, NULL, log);
|
||||
printf("%s\n", log);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return shaderId;
|
||||
}
|
||||
|
||||
static int LoadShaders()
|
||||
{
|
||||
const char *vertShaderFilePath = "./res/glsl/vertex.glsl";
|
||||
char **vertCode = NULL;
|
||||
int vertNumLines;
|
||||
int vertShaderId = CompileShader(vertShaderFilePath, GL_VERTEX_SHADER, &vertCode, &vertNumLines);
|
||||
if (vertShaderId < 0) return vertShaderId;
|
||||
|
||||
const char *fragShaderFilePath = "./res/glsl/fragment.glsl";
|
||||
char **fragCode = NULL;
|
||||
int fragNumLines;
|
||||
int fragShaderId = CompileShader(fragShaderFilePath, GL_FRAGMENT_SHADER, &fragCode, &fragNumLines);
|
||||
if (fragShaderId < 0 ) return fragShaderId;
|
||||
|
||||
GLint success;
|
||||
GLuint shaderProgramId = glCreateProgram();
|
||||
glAttachShader(shaderProgramId, vertShaderId);
|
||||
glAttachShader(shaderProgramId, fragShaderId);
|
||||
glLinkProgram(shaderProgramId);
|
||||
glGetProgramiv(shaderProgramId, GL_LINK_STATUS, &success);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
GLchar log[512];
|
||||
glGetProgramInfoLog(shaderProgramId, 512, NULL, log);
|
||||
printf("%s", log);
|
||||
return -1;
|
||||
}
|
||||
|
||||
glDeleteShader(vertShaderId);
|
||||
glDeleteShader(fragShaderId);
|
||||
FreeLines(vertCode, vertNumLines);
|
||||
FreeLines(fragCode, fragNumLines);
|
||||
|
||||
return shaderProgramId;
|
||||
}
|
||||
|
||||
static void InitShapeBuffers(Shape *shape)
|
||||
{
|
||||
glBindVertexArray(shape->VAO);
|
||||
|
||||
// vertex buffer: contains vertex and texture coords
|
||||
glBindBuffer(GL_ARRAY_BUFFER, shape->VBO);
|
||||
glBufferData(GL_ARRAY_BUFFER, shape->numVertices * sizeof(GLfloat), shape->vertices, GL_STATIC_DRAW);
|
||||
GLsizei stride = 5 * sizeof(GLfloat);
|
||||
|
||||
// vertex coordinates
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)0);
|
||||
|
||||
// texture coordinates
|
||||
glEnableVertexAttribArray(1);
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(3 * sizeof(GLfloat)));
|
||||
|
||||
// instance buffer: contains per-instance texture index and model transformation matrix
|
||||
// therefore each instance of the same shape, in the same draw call, can have a different texture and transformation
|
||||
glBindBuffer(GL_ARRAY_BUFFER, shape->IBO);
|
||||
stride = SHAPE_INSTANCE_SIZE;
|
||||
|
||||
// texture index: basically another tex coord in the third dimension because a texture array is used
|
||||
glEnableVertexAttribArray(2);
|
||||
glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, stride, (GLvoid*)0);
|
||||
glVertexAttribDivisor(2, 1);
|
||||
|
||||
// model matrix: contains 16 floats, each vert attrib has space for 4 floats, therefore it spans 4 attrib locations
|
||||
glEnableVertexAttribArray(3);
|
||||
glEnableVertexAttribArray(4);
|
||||
glEnableVertexAttribArray(5);
|
||||
glEnableVertexAttribArray(6);
|
||||
glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(1 * sizeof(GLfloat)));
|
||||
glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(5 * sizeof(GLfloat)));
|
||||
glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(9 * sizeof(GLfloat)));
|
||||
glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(13 * sizeof(GLfloat)));
|
||||
glVertexAttribDivisor(3, 1);
|
||||
glVertexAttribDivisor(4, 1);
|
||||
glVertexAttribDivisor(5, 1);
|
||||
glVertexAttribDivisor(6, 1);
|
||||
|
||||
// index buffer: contains vertex indices, reducing the data to be buffered on the GPU
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, shape->EBO);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, shape->numIndices * sizeof(GLushort), shape->indices, GL_STATIC_DRAW);
|
||||
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
bool Render_Init(GameState *gs)
|
||||
{
|
||||
SDL_Init(SDL_INIT_VIDEO);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 5);
|
||||
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
|
||||
|
||||
gs->window = SDL_CreateWindow("Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1920, 1080, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);;
|
||||
gs->glContext = SDL_GL_CreateContext(gs->window);
|
||||
printf("Created OpenGL window.\n");
|
||||
|
||||
glewInit();
|
||||
gs->shaderProgramId = LoadShaders();
|
||||
|
||||
if (gs->shaderProgramId < 0)
|
||||
{
|
||||
printf("Failed to load shaders.\n");
|
||||
}
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glEnable(GL_CULL_FACE);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
printf("Initialized OpenGL.\n");
|
||||
|
||||
Shape *shape = Shape_MakePyramid(3);
|
||||
gs->testShape = shape;
|
||||
glGenVertexArrays(1, &shape->VAO);
|
||||
glGenBuffers(1, &shape->VBO);
|
||||
glGenBuffers(1, &shape->IBO);
|
||||
glGenBuffers(1, &shape->EBO);
|
||||
|
||||
LoadTexture(gs);
|
||||
Camera_Init(&gs->camera);
|
||||
|
||||
InitShapeBuffers(gs->testShape);
|
||||
}
|
||||
|
||||
void Render_Destroy(GameState *gs)
|
||||
{
|
||||
SDL_GL_DeleteContext(gs->glContext);
|
||||
SDL_DestroyWindow(gs->window);
|
||||
}
|
||||
|
||||
// Sets the GL viewport while maintaining aspect ratio.
|
||||
// Calculates the projection and view matrices.
|
||||
static void SetViewport(GameState *gs)
|
||||
{
|
||||
const float ratio = 0.5625f; // 1080/1920
|
||||
int wWidth, wHeight, offsetX, offsetY;
|
||||
SDL_GL_GetDrawableSize(gs->window, &wWidth, &wHeight);
|
||||
|
||||
if ((float)wHeight / (float)wWidth > ratio)
|
||||
{
|
||||
int temp = (int)floorf(ratio * (float)wWidth);
|
||||
offsetX = 0;
|
||||
offsetY = (wHeight - temp) / 2;
|
||||
wHeight = temp;
|
||||
}
|
||||
else
|
||||
{
|
||||
int temp = (int)floorf((float)wHeight / ratio);
|
||||
offsetX = (wWidth - temp) / 2;
|
||||
offsetY = 0;
|
||||
wWidth = temp;
|
||||
}
|
||||
|
||||
glViewport(offsetX, offsetY, wWidth, wHeight);
|
||||
|
||||
glm_mat4_identity(gs->projMatrix);
|
||||
glm_perspective(45.0f, (GLfloat)wWidth / (GLfloat)wHeight, 0.1f, 2000.0f, gs->projMatrix);
|
||||
|
||||
glm_mat4_identity(gs->viewMatrix);
|
||||
Camera_GetViewMatrix(&gs->camera, gs->viewMatrix);
|
||||
}
|
||||
|
||||
void Render_Draw(GameState *gs)
|
||||
{
|
||||
glClearColor(0.4f, 0.6f, 0.8f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
glLoadIdentity();
|
||||
SetViewport(gs);
|
||||
|
||||
glUseProgram(gs->shaderProgramId);
|
||||
GLint projLoc = glGetUniformLocation(gs->shaderProgramId, "theProjMatrix");
|
||||
glUniformMatrix4fv(projLoc, 1, GL_FALSE, (void*)(gs->projMatrix));
|
||||
GLint viewLoc = glGetUniformLocation(gs->shaderProgramId, "theViewMatrix");
|
||||
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, (void*)(gs->viewMatrix));
|
||||
glBindTexture(GL_TEXTURE_2D, gs->textureId);
|
||||
vec3 scale = { 0, 0, 0 };
|
||||
|
||||
Shape *shape = gs->testShape;
|
||||
glBindVertexArray(shape->VAO);
|
||||
|
||||
// apply transformations to each instance
|
||||
for (int j = 0; j < shape->numInstances; j++)
|
||||
{
|
||||
ShapeInstance* instance = shape->instances + j;
|
||||
mat4* matrix = (void*)(shape->instanceData + (j * 17) + 1);
|
||||
mat4 tempMat;
|
||||
glm_mat4_identity(tempMat);
|
||||
|
||||
glm_translate(tempMat, instance->position);
|
||||
glm_rotate_x(tempMat, instance->rotation[0], tempMat);
|
||||
glm_rotate_y(tempMat, instance->rotation[1], tempMat);
|
||||
glm_rotate_z(tempMat, instance->rotation[2], tempMat);
|
||||
|
||||
scale[0] = instance->scale;
|
||||
scale[1] = instance->scale;
|
||||
scale[2] = instance->scale;
|
||||
glm_scale(tempMat, scale);
|
||||
|
||||
memcpy(matrix, tempMat, sizeof(mat4));
|
||||
}
|
||||
|
||||
// re-buffer the instance data because transformations may have changed
|
||||
glBindBuffer(GL_ARRAY_BUFFER, shape->IBO);
|
||||
glBufferData(GL_ARRAY_BUFFER, shape->numInstances * SHAPE_INSTANCE_SIZE, shape->instanceData, GL_DYNAMIC_DRAW);
|
||||
|
||||
glDrawElementsInstanced(GL_TRIANGLES, shape->numIndices, GL_UNSIGNED_SHORT, 0, shape->numInstances);
|
||||
|
||||
SDL_GL_SwapWindow(gs->window);
|
||||
}
|
||||
8
src/render.h
Normal file
8
src/render.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "game.h"
|
||||
|
||||
bool Render_Init(GameState *gs);
|
||||
void Render_Destroy(GameState *gs);
|
||||
void Render_Draw(GameState *gs);
|
||||
83
src/shape.c
Normal file
83
src/shape.c
Normal file
@@ -0,0 +1,83 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "GL/glew.h"
|
||||
#include "cglm/cglm.h"
|
||||
#include "shape.h"
|
||||
|
||||
Shape *Shape_MakePyramid(int numInstances)
|
||||
{
|
||||
GLfloat vertices[] =
|
||||
{
|
||||
-1.0f, -1.0f, -1.0f, 0.0f, 0.0f,
|
||||
1.0f, -1.0f, -1.0f, 1.0f, 0.0f,
|
||||
1.0f, -1.0f, 1.0f, 1.0f, 1.0f,
|
||||
-1.0f, -1.0f, -1.0f, 0.0f, 0.0f,
|
||||
1.0f, -1.0f, 1.0f, 1.0f, 1.0f,
|
||||
-1.0f, -1.0f, 1.0f, 0.0f, 1.0f,
|
||||
|
||||
1.0f, -1.0f, -1.0f, 1.0f, 0.0f,
|
||||
-1.0f, -1.0f, -1.0f, 0.0f, 0.0f,
|
||||
0.0f, 1.0f, 0.0f, 0.5f, 1.0f,
|
||||
|
||||
1.0f, -1.0f, 1.0f, 1.0f, 0.0f,
|
||||
1.0f, -1.0f, -1.0f, 0.0f, 0.0f,
|
||||
0.0f, 1.0f, 0.0f, 0.5f, 1.0f,
|
||||
|
||||
-1.0f, -1.0f, 1.0f, 1.0f, 0.0f,
|
||||
1.0f, -1.0f, 1.0f, 0.0f, 0.0f,
|
||||
0.0f, 1.0f, 0.0f, 0.5f, 1.0f,
|
||||
|
||||
-1.0f, -1.0f, -1.0f, 1.0f, 0.0f,
|
||||
-1.0f, -1.0f, 1.0f, 0.0f, 0.0f,
|
||||
0.0f, 1.0f, 0.0f, 0.5f, 1.0f,
|
||||
};
|
||||
|
||||
GLushort indices[] =
|
||||
{
|
||||
0, 1, 2,
|
||||
3, 4, 5,
|
||||
6, 7, 8,
|
||||
9, 10, 11,
|
||||
12, 13, 14,
|
||||
15, 16, 17
|
||||
};
|
||||
|
||||
size_t numBytesVertices = sizeof(vertices);
|
||||
size_t numBytesIndices = sizeof(indices);
|
||||
|
||||
Shape *shape = malloc(sizeof(Shape) + numBytesVertices + numBytesIndices);
|
||||
void *tempPtr = (void*)(shape + 1);
|
||||
shape->vertices = tempPtr;
|
||||
shape->numVertices = numBytesVertices / sizeof(vertices[0]);
|
||||
shape->indices = tempPtr + numBytesVertices;
|
||||
shape->numIndices = numBytesIndices / sizeof(indices[0]);
|
||||
|
||||
memcpy(shape->vertices, vertices, numBytesVertices);
|
||||
memcpy(shape->indices, indices, numBytesIndices);
|
||||
|
||||
shape->instances = calloc(numInstances, sizeof(ShapeInstance) + SHAPE_INSTANCE_SIZE);
|
||||
shape->instanceData = (void*)(shape->instances + numInstances);
|
||||
shape->numInstances = numInstances;
|
||||
|
||||
for (int i = 0; i < numInstances; i++)
|
||||
{
|
||||
ShapeInstance *instance = shape->instances + i;
|
||||
instance->position[0] = -4.0f;
|
||||
instance->position[0] = 0.0f;
|
||||
instance->position[0] = (5.0f * i) - 2.0f;
|
||||
instance->scale = 1.0f;
|
||||
instance->collisionRadius = 1.0f;
|
||||
glm_vec3_zero(instance->velocity);
|
||||
glm_vec3_zero(instance->rotation);
|
||||
float *textureId = shape->instanceData + (i * 17);
|
||||
*textureId = 0;
|
||||
mat4 *matrix = (void*)(textureId + 1);
|
||||
}
|
||||
|
||||
return shape;
|
||||
}
|
||||
|
||||
void *Shape_Destroy(Shape *shape)
|
||||
{
|
||||
free(shape);
|
||||
}
|
||||
37
src/shape.h
Normal file
37
src/shape.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include "GL/glew.h"
|
||||
#include "cglm/cglm.h"
|
||||
|
||||
enum
|
||||
{
|
||||
SHAPE_INSTANCE_SIZE = sizeof(float) + sizeof(mat4)
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
vec3 position;
|
||||
vec3 rotation;
|
||||
vec3 velocity;
|
||||
float scale;
|
||||
float collisionRadius;
|
||||
|
||||
} ShapeInstance;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GLfloat *vertices;
|
||||
int numVertices;
|
||||
GLushort *indices;
|
||||
int numIndices;
|
||||
ShapeInstance *instances;
|
||||
float *instanceData;
|
||||
int numInstances;
|
||||
|
||||
GLuint VAO;
|
||||
GLuint VBO;
|
||||
GLuint IBO;
|
||||
GLuint EBO;
|
||||
} Shape;
|
||||
|
||||
Shape *Shape_MakePyramid(int numInstances);
|
||||
Reference in New Issue
Block a user