#include <glad/glad.h>
#include <SDL.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/transform.hpp>
#include <vector>
#include <string>
#include <iostream>
enum class GameState { PLAY, EXIT };
class Color
{
public:
GLfloat r, g, b;
Color(GLfloat r, GLfloat g, GLfloat b) :
r(r), g(g), b(b)
{
}
};
class Point
{
public:
GLfloat x, y;
Point(GLfloat x, GLfloat y) :
x(x), y(y)
{
}
};
void initVertexBuffers();
GLuint createShaderProgram();
void drawSquare(const glm::vec3 &pos, const Color &color, GLfloat size = 1.0f, GLfloat angle = 0.0f);
GameState g_gameState = GameState::PLAY;
SDL_Window *g_window;
GLuint g_vao;
GLuint g_program;
float g_frameTime;
float g_maxFPS = 3.0f;
float g_fps;
const int N = 30;
const int M = 20;
const int SCALE = 25;
const int W = SCALE * N;
const int H = SCALE * M;
int dir, num = 4;
struct
{
int x, y;
} s[100];
class Fruits
{
public:
int x, y;
void createNew()
{
x = rand() % N;
y = rand() % M;
}
void drawFruit()
{
drawSquare(glm::vec3(x * SCALE, y * SCALE, 0.0f), Color(0.0f, 1.0f, 0.0f), SCALE);
}
} m[10];
const char *vertexShaderSource =
"#version 130 core\n"
"in vec2 a_position;"
"uniform mat4 u_mvp;"
"void main()"
"{"
" gl_Position= u_mvp * vec4(a_position, 0.0, 1.0);"
"}";
const char *fragmentShaderSource =
"#version 130 core\n"
"precision mediump float;"
"in vec3 v_Color;"
"out vec4 fragColor;"
"uniform vec3 u_color;"
"void main()"
"{"
" fragColor = vec4(u_color, 1.0);"
"}";
void fatalError(std::string errorString)
{
std::cout << errorString << std::endl;
std::cout << "Enter any key to quit..." << std::endl;
int tmp;
std::cin >> tmp;
SDL_Quit();
exit(1);
}
void processInput()
{
SDL_Event event;
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_QUIT:
g_gameState = GameState::EXIT;
break;
case SDL_KEYDOWN:
switch (event.key.keysym.sym)
{
case SDLK_w:
dir = 0;
break;
case SDLK_a:
dir = 1;
break;
case SDLK_s:
dir = 2;
break;
case SDLK_d:
dir = 3;
break;
}
break;
}
}
}
void setGeneralInfo(const glm::vec3 &pos, const Color &color, const glm::vec3 &scale, GLfloat angle)
{
// Rotate
GLfloat radian = glm::radians(angle);
glm::mat4 xformRotateMatrix = glm::rotate(radian, glm::vec3(0.0f, 0.0f, 1.0f));
// Translate
glm::mat4 xformTranslateMatrix = glm::translate(glm::mat4(), pos);
// Scale
glm::mat4 xformScaleMatrix = glm::scale(glm::vec3((scale.x, scale.y, scale.z)));
// Set Matrixes
glm::mat4 projMatrix = glm::ortho(0.0f, (float)W, 0.0f, (float)H, 100.0f, -100.0f);
glm::mat4 viewMatrix = glm::lookAt(
glm::vec3(0.0f, 0.0f, 3.0f), // Camera position
glm::vec3(0.0f, 0.0f, 0.0f), // Looks at the origin
glm::vec3(0.0f, 1.0f, 0.0f)); // Head is up
glm::mat4 modelMatrix = xformTranslateMatrix * xformRotateMatrix * xformScaleMatrix;
//glm::mat4 modelMatrix = glm::scale(xformTranslateMatrix, glm::vec3(50.0, 50.0f, 50.0f));
// Set MVP
glm::mat4 mvp = projMatrix * viewMatrix * modelMatrix;
GLint u_mvp = glGetUniformLocation(g_program, "u_mvp");
glUniformMatrix4fv(u_mvp, 1, false, &mvp[0][0]);
// Set Color
GLint u_color = glGetUniformLocation(g_program, "u_color");
glUniform3f(u_color, color.r, color.g, color.b);
}
void drawSquare(const glm::vec3 &pos, const Color &color, GLfloat size /*= 1.0f*/, GLfloat angle /*= 0.0f*/)
{
setGeneralInfo(pos, color, glm::vec3(size, size, size), angle);
// Draw
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
void drawLine(Point p1, Point p2, const Color &color)
{
Point center(p1.x + (p2.x - p1.x) / 2.0f, p1.y + (p2.y - p1.y) / 2.0f);
GLfloat a = p2.y - p1.y;
GLfloat b = p2.x - p1.x;
GLfloat tan = a / b;
GLfloat angle = glm::degrees(glm::atan(tan));
GLfloat length = glm::length(glm::vec2(b, a));
setGeneralInfo(
glm::vec3(center.x, center.y, 0.0f),
color,
glm::vec3(1.0f, 1.0f, length),
angle);
glDrawArrays(GL_LINES, 4, 2);
}
void drawField()
{
Color color(0.0f, 1.0f, 0.0f);
for (size_t i = 0; i < W; i += SCALE)
{
drawLine(Point(i, 0.0f), Point(i, H), color);
}
for (size_t j = 0; j < H; j += SCALE)
{
drawLine(Point(0.0f, j), Point(W, j), color);
}
}
void drawSnake()
{
for (size_t i = 0; i < num; i++)
{
drawSquare(glm::vec3(s[i].x * SCALE, s[i].y * SCALE, 0.0f), Color(0.0f, 0.0f, 1.0f), SCALE);
}
}
void display()
{
// Clear a screen
glClear(GL_COLOR_BUFFER_BIT);
// Set a viewport on whole screen
glViewport(0, 0, W, H);
for (size_t i = 0; i < 10; i++)
{
m[i].drawFruit();
}
drawField();
drawSnake();
SDL_GL_SwapWindow(g_window);
}
void calculateFPS()
{
static const int NUM_SAMPLES = 10;
static float frameTimes[NUM_SAMPLES];
static int currentFrame = 0;
static float prevTicks = SDL_GetTicks();
float currentTicks;
currentTicks = SDL_GetTicks();
g_frameTime = currentTicks - prevTicks;
frameTimes[currentFrame % NUM_SAMPLES] = g_frameTime;
prevTicks = currentTicks;
int count;
currentFrame++;
if (currentFrame < NUM_SAMPLES)
{
count = currentFrame;
}
else
{
count = NUM_SAMPLES;
}
float frameTimeAverage = 0;
for (int i = 0; i < count; i++)
{
frameTimeAverage += frameTimes[i];
}
frameTimeAverage /= count;
if (frameTimeAverage > 0)
{
g_fps = 1000.0f / frameTimeAverage;
}
else
{
g_fps = 60.0f;
}
}
void tick()
{
for (size_t i = num; i > 0; --i)
{
s[i].x = s[i - 1].x;
s[i].y = s[i - 1].y;
}
if (dir == 0)
{
s[0].y += 1;
}
if (dir == 1)
{
s[0].x -= 1;
}
if (dir == 2)
{
s[0].y -= 1;
}
if (dir == 3)
{
s[0].x += 1;
}
for (size_t i = 0; i < 10; i++)
{
if ((s[0].x == m[i].x) && (s[0].y == m[i].y))
{
num++;
m[i].createNew();
}
}
if (s[0].y > M)
{
dir = 2;
}
if (s[0].x < 0)
{
dir = 3;
}
if (s[0].y < 0)
{
dir = 0;
}
if (s[0].x > N)
{
dir = 1;
}
for (size_t i = 1; i < num; i++)
{
if (s[0].x == s[i].x && s[0].y == s[i].y)
{
num = i;
}
}
}
void gameLoop()
{
while (g_gameState != GameState::EXIT)
{
processInput();
float startTicks = SDL_GetTicks();
tick();
display();
calculateFPS();
// Print only once every 10 frames
static int frameCounter = 0;
frameCounter++;
if (frameCounter == 10)
{
std::cout << g_fps << std::endl;
frameCounter = 0;
}
float frameTicks = SDL_GetTicks() - startTicks;
// Limit the FPS to the max FPS
if (1000.0f / g_maxFPS > frameTicks)
{
SDL_Delay(1000.0f / g_maxFPS - frameTicks);
}
}
SDL_DestroyWindow(g_window);
SDL_Quit();
}
int main(int argc, char **argv)
{
SDL_Init(SDL_INIT_EVERYTHING);
g_window = SDL_CreateWindow(
"Snake FamTrinli To OpenGL 3.0",
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
W, H,
SDL_WINDOW_OPENGL);
if (g_window == nullptr)
{
fatalError("SDL Window could not be created");
}
SDL_GLContext glContext = SDL_GL_CreateContext(g_window);
if (glContext == nullptr)
{
fatalError("SDL_GL context could not be created");
}
if (!gladLoadGL())
{
fatalError("Could not initialize glad!");
}
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
g_program = createShaderProgram();
initVertexBuffers();
for (size_t i = 0; i < 10; i++)
{
m[i].createNew();
}
s[0].x = 10;
s[0].y = 0;
for (size_t i = 1; i < num; i++)
{
s[i].x = s[0].x;
s[i].y = s[i - 1].y - 1;
}
gameLoop();
return 0;
}
GLuint createShader(const char *shaderSource, int shaderType)
{
GLuint shader = glCreateShader(shaderType);
glShaderSource(shader, 1, &shaderSource, NULL);
glCompileShader(shader);
GLint status;
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE)
{
GLint maxLength = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);
std::vector<GLchar> errorLog(maxLength);
glGetShaderInfoLog(shader, maxLength, &maxLength, &errorLog[0]);
glDeleteShader(shader); // Don't leak the shader.
std::printf("%s\n", &(errorLog[0]));
fatalError("Shader failed to compile");
}
return shader;
}
GLuint createShaderProgram()
{
GLuint program = glCreateProgram();
GLuint vShader = createShader(vertexShaderSource, GL_VERTEX_SHADER);
GLuint fShader = createShader(fragmentShaderSource, GL_FRAGMENT_SHADER);
glAttachShader(program, vShader);
glAttachShader(program, fShader);
glLinkProgram(program);
glUseProgram(program);
return program;
}
void initVertexBuffers()
{
GLfloat vertices[] = {
0.0f, 1.0f, // Square
0.0f, 0.0f,
1.0f, 1.0f,
1.0f, 0.0f,
-0.5f, 0.0f, // Line
0.5f, 0.0f
};
GLuint vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
int numVertices = sizeof(vertices) / sizeof(vertices[0]);
glBufferData(GL_ARRAY_BUFFER, numVertices * sizeof(GLfloat), vertices, GL_STATIC_DRAW);
glGenVertexArrays(1, &g_vao);
glBindVertexArray(g_vao);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
GLint a_position = glGetAttribLocation(g_program, "a_position");
glVertexAttribPointer(a_position, 2, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(a_position);
}