1
0
forked from Redacted/Re3D

initial commit

This commit is contained in:
2023-11-18 09:04:45 -05:00
commit 4eedd52004
17 changed files with 704 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/cmake-build-debug
/.idea

21
CMakeLists.txt Normal file
View File

@@ -0,0 +1,21 @@
cmake_minimum_required(VERSION 3.18)
project(SDL3D)
set(CMAKE_CXX_STANDARD 20)
add_executable(SDL3D src/main.cpp
src/types/entity.h
src/types/camera.h
src/types/world.h
src/engine/engine.h
src/types/vector3.h
src/types/player.h
src/types/entityList.h
src/engine/render.h
src/engine/tick.h
src/types/vertex.h
src/types/skybox.h
src/types/geometry.h)
find_package(OpenGL)
target_link_libraries(SDL3D SDL2 freeimage ${OPENGL_LIBRARIES})

24
LICENSE Normal file
View File

@@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <https://unlicense.org>

2
run.sh Executable file
View File

@@ -0,0 +1,2 @@
sleep 0.5
vblank_mode=0 mangohud --dlsym /home/william/Documents/GitHub/SDL3D/cmake-build-debug/SDL3D

152
src/engine/engine.h Normal file
View File

@@ -0,0 +1,152 @@
#pragma once
#include <cstdint>
#include <cstring>
#include <iostream>
#include <SDL2/SDL.h>
#include <SDL2/SDL_opengl.h>
#include <GL/glu.h>
#include <FreeImage.h>
enum class gamestate: uint8_t {
NORMAL = 0, //Gameplay.
IN_MAIN_MENU = 1,
IN_PAUSE_MENU = 2,
IN_LEVEL_ANIMATION = 3, //A cutscene which moves entities in the world and cannot be interrupted.
IN_QUIT = 4, //The game should close.
NULL_ = 7
};
class Engine {
public:
gamestate gameState = gamestate::NULL_;
bool debug = true;
bool takingScreenshot = false; //Because
SDL_Window *window = nullptr;
uint16_t windowWidth = 1152;
uint16_t windowHeight = 864;
ulong tickCount = 0;
float tickDelta = NULL;
ulong frameCount = 0;
float frameDelta = NULL;
uint16_t minimumTickDelta = 15625;
GLenum glError = GL_NO_ERROR;
SDL_GLContext glContext = nullptr;
float nearPlane = 0.01f;
float farPlane = 100.0f;
float fov = 70.0f;
SDL_Event event;
[[nodiscard]] float framerate() const {
return 1.f / this->frameDelta;
}
void takeScreenshot() {
if (!this->takingScreenshot)
return;
FreeImage_Initialise();
auto* fbData = new GLubyte[this->windowWidth * this->windowHeight * 3];
glReadPixels(0, 0, this->windowWidth, this->windowHeight, GL_RGB, GL_UNSIGNED_BYTE, fbData);
FIBITMAP* image = FreeImage_ConvertFromRawBits(fbData, this->windowWidth, this->windowHeight, this->windowWidth * 3, 24, 0xFF0000, 0x00FF00, 0x0000FF, false);
FreeImage_Save(FIF_BMP, image, std::to_string(this->frameCount).c_str(), 0);
FreeImage_Unload(image);
delete[] fbData;
FreeImage_DeInitialise();
this->takingScreenshot = false;
}
void takeScreenshot(const char* filename) {
if (!this->takingScreenshot)
return;
FreeImage_Initialise();
auto* fbData = new GLubyte[this->windowWidth * this->windowHeight * 3];
glReadPixels(0, 0, this->windowWidth, this->windowHeight, GL_RGB, GL_UNSIGNED_BYTE, fbData);
FIBITMAP* image = FreeImage_ConvertFromRawBits(fbData, this->windowWidth, this->windowHeight, this->windowWidth * 3, 24, 0xFF0000, 0x00FF00, 0x0000FF, false);
FreeImage_Save(FIF_BMP, image, filename, 0);
FreeImage_Unload(image);
delete[] fbData;
FreeImage_DeInitialise();
this->takingScreenshot = false;
}
void quit() {
SDL_DestroyWindow(this->window);
SDL_Quit();
exit(0);
}
static float getGLVersion() {
std::string str = reinterpret_cast<const char *>(glGetString(GL_VERSION));
str.erase(str.begin()+3,str.end());
return std::stof(str);
}
void initGL() {
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
//glOrtho(-1.0f, 1.0f, -1.0f, 1.0f, -0.5f, 0.5f);
gluPerspective(this->fov, this->windowWidth / this->windowHeight, this->nearPlane, this->farPlane);
this->glError = glGetError();
if (glError != GL_NO_ERROR) {
std::cerr << "OpenGL: " << gluErrorString(glError);
exit (1);
}
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
if (glError != GL_NO_ERROR) {
std::cerr << "OpenGL: " << gluErrorString(glError);
exit (1);
}
glClearColor(0.f, 0.f, 0.f, 1.f);
glViewport(0,0,this->windowWidth,this->windowHeight);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glDepthMask(GL_TRUE);
glClearDepth(1.0f);
glDisable(GL_CULL_FACE);
//glDepthRange(-0.5f,0.5f);
}
void initVideo() {
Uint32 sdl_initializer_flags = SDL_INIT_VIDEO;// | SDL_INIT_AUDIO;
// Start SDL, check for errors
if (SDL_Init(sdl_initializer_flags) < 0) {
std::cerr << "SDL_Error: " << SDL_GetError() << std::endl;
exit(1);
}
// Create the window, set resizable, check for errors
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 1);
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 4);
SDL_GL_SetSwapInterval(0);
this->window = SDL_CreateWindow("SDL3D", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, this->windowWidth, this->windowHeight, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN);
SDL_SetWindowResizable(this->window,SDL_FALSE);
this->glContext = SDL_GL_CreateContext(window);
if (isless(this->getGLVersion(),1.4f)) {
std::cerr << "Driver OpenGL version: " << this->getGLVersion() << std::endl;
std::cerr << "OpenGL >= 1.4 is required." << std::endl;
exit(1);
}
if (this->window == nullptr) {
std::cerr << "SDL_Error: " << SDL_GetError() << std::endl;
exit(1);
}
}
void debugInfo() {
if (this->debug) {
std::cout << "Last tick completed in: " << (tickDelta / 1000) << "ms, " << "sleeping " << ((minimumTickDelta - tickDelta) / 1000) << "ms." << std::endl;
std::cout << "Last frame dT: " << this->frameDelta << std::endl;
std::cout << "Framerate: " << framerate() << std::endl;;
//std::cout << "EntityCount: " << entityList.list.size() << std::endl;
//std::cout << "Camera Position: " << "X " << entityList.getCamera()->position.x << " Y " << entityList.getCamera()->position.y << " Z " << entityList.getCamera()->position.z << std::endl;
}
}
};
auto* engine = new(Engine);

129
src/engine/render.h Normal file
View File

@@ -0,0 +1,129 @@
#pragma once
#include <chrono>
#include <thread>
#include "engine.h"
#include "../types/camera.h"
#include "../types/entityList.h"
void process_sdl_events() {
while (SDL_PollEvent(&engine->event)) {
// TODO: Consider switch statements as opposed to ifs
// This adds control flow in the form of "break" statement.
// Which will save checking every single event type unnecessarily
if (engine->event.type == SDL_QUIT) {engine->quit();}
if (engine->event.type == SDL_DROPFILE) {}
// Window Events
if (engine->event.type == SDL_WINDOWEVENT) {
auto window_ev = engine->event.window;
auto ev_type = window_ev.event;
if (ev_type == SDL_WINDOWEVENT_FOCUS_LOST) {}
if (ev_type == SDL_WINDOWEVENT_FOCUS_GAINED) {}
if (ev_type == SDL_WINDOWEVENT_CLOSE) {}
if (ev_type == SDL_WINDOWEVENT_SHOWN) {}
if (ev_type == SDL_WINDOWEVENT_HIDDEN) {}
if (ev_type == SDL_WINDOWEVENT_EXPOSED) {}
if (ev_type == SDL_WINDOWEVENT_MOVED) {}
if (ev_type == SDL_WINDOWEVENT_RESIZED) {}
if (ev_type == SDL_WINDOWEVENT_SIZE_CHANGED) {}
if (ev_type == SDL_WINDOWEVENT_MINIMIZED) {}
if (ev_type == SDL_WINDOWEVENT_MAXIMIZED) {}
if (ev_type == SDL_WINDOWEVENT_RESTORED) {}
if (ev_type == SDL_WINDOWEVENT_ENTER) {}
if (ev_type == SDL_WINDOWEVENT_LEAVE) {}
}
}
}
void pre_render() {
if(engine->frameCount == 0) {
engine->initVideo();
engine->initGL();
auto camera = new(Camera);
entityList.storeEntity(camera);
entityList.getCamera()->position.set(0.0f,0.0f,5.0f);
}
process_sdl_events();
engine->frameCount++;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
entityList.getCamera()->render();
}
void render() {
glBegin(GL_QUADS);
glColor3f(1,0.5,1);
// Front face
glVertex3f(-0.5f, -0.5f, 0.5f);
glVertex3f(0.5f, -0.5f, 0.5f);
glVertex3f(0.5f, 0.5f, 0.5f);
glVertex3f(-0.5f, 0.5f, 0.5f);
// Back face
glVertex3f(-0.5f, -0.5f, -0.5f);
glVertex3f(0.5f, -0.5f, -0.5f);
glVertex3f(0.5f, 0.5f, -0.5f);
glVertex3f(-0.5f, 0.5f, -0.5f);
// Left face
glVertex3f(-0.5f, -0.5f, -0.5f);
glVertex3f(-0.5f, -0.5f, 0.5f);
glVertex3f(-0.5f, 0.5f, 0.5f);
glVertex3f(-0.5f, 0.5f, -0.5f);
// Right face
glVertex3f(0.5f, -0.5f, -0.5f);
glVertex3f(0.5f, -0.5f, 0.5f);
glVertex3f(0.5f, 0.5f, 0.5f);
glVertex3f(0.5f, 0.5f, -0.5f);
// Top face
glVertex3f(-0.5f, 0.5f, -0.5f);
glVertex3f(0.5f, 0.5f, -0.5f);
glVertex3f(0.5f, 0.5f, 0.5f);
glVertex3f(-0.5f, 0.5f, 0.5f);
// Bottom face
glVertex3f(-0.5f, -0.5f, -0.5f);
glVertex3f(0.5f, -0.5f, -0.5f);
glVertex3f(0.5f, -0.5f, 0.5f);
glVertex3f(-0.5f, -0.5f, 0.5f);
glEnd();
glPopMatrix();
glPushMatrix();
//glBegin(GL_QUADS);
//glColor3f(0,1,0);
//glVertex3f(-0.25f, -0.75f,0.0);
//glVertex3f(0.25f, -0.75f,0.0);
//glVertex3f(0.25f, 0.75f,0.0);
//glVertex3f(-0.25f, 0.75f,0.0);
//glEnd();
}
void post_render() {
SDL_GL_SwapWindow(engine->window);
engine->takeScreenshot();
//Resets all of the transformations for the next frame.
glPopMatrix();
glPushMatrix();
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
[[noreturn]] void render_loop() {
while (true) {
auto start = std::chrono::high_resolution_clock::now();
pre_render();
render();
post_render();
auto stop = std::chrono::high_resolution_clock::now();
float dT = (std::chrono::duration_cast<std::chrono::microseconds>(stop - start).count());
engine->frameDelta = dT / 1000000;
}
}

39
src/engine/tick.h Normal file
View File

@@ -0,0 +1,39 @@
#pragma once
#include <chrono>
#include <thread>
#include "engine.h"
#include "../types/entityList.h"
[[noreturn]] void gameTick() {
while (true) {
auto start = std::chrono::high_resolution_clock::now();
//do stuff
engine->tickCount++;
switch (engine->gameState) {
case gamestate::NORMAL:
break;
case gamestate::IN_MAIN_MENU:
break;
case gamestate::IN_PAUSE_MENU:
break;
case gamestate::IN_LEVEL_ANIMATION:
break;
case gamestate::IN_QUIT:
engine->quit();
break;
}
if (engine->tickCount % 64 == 0)
engine->debugInfo();
if(engine->frameCount > 0)
entityList.getCamera()->update();
auto stop = std::chrono::high_resolution_clock::now();
//limit to 64 ticks per second.
if ((int) engine->tickDelta < engine->minimumTickDelta)
std::this_thread::sleep_for(
std::chrono::microseconds(engine->minimumTickDelta - (uint16_t) engine->tickDelta));
//Pause without potential deSync issues.
engine->tickDelta = std::chrono::duration_cast<std::chrono::microseconds>(stop - start).count();
}
}

10
src/main.cpp Normal file
View File

@@ -0,0 +1,10 @@
#include <thread>
#include "engine/tick.h"
#include "engine/render.h"
int main() {
std::thread renderThread(render_loop);
std::thread tickThread (gameTick);
renderThread.join();
tickThread.join();
return 0;
}

85
src/types/camera.h Normal file
View File

@@ -0,0 +1,85 @@
#pragma once
#include "entity.h"
#include "../engine/engine.h"
enum class cameramode: uint8_t {
FREECAM = 0,
FOLLOW_PLAYER = 1
};
class Camera : public entity {
public:
uint8_t mode = 0;
void move(vector3 offset) {
position.x += offset.x;
position.y += offset.y;
position.z += offset.z;
}
void move(float x, float y, float z) {
position.x += x;
position.y += y;
position.z += z;
}
void update() {
if (mode == 0) {
const Uint8* keyState = SDL_GetKeyboardState(nullptr);
//TODO: Make the direction that is considered "forward" not a set axis direction
//But instead determined by the yaw.
if (keyState[SDL_SCANCODE_S] == 1) {
this->position.z = this->position.z+0.1f;
}
if (keyState[SDL_SCANCODE_W] == 1) {
this->position.z = this->position.z-0.1f;
}
if (keyState[SDL_SCANCODE_A] == 1) {
this->position.x = this->position.x-0.025f;;
}
if (keyState[SDL_SCANCODE_D] == 1) {
this->position.x = this->position.x+0.025f;
}
if (keyState[SDL_SCANCODE_SPACE] == 1) {
this->position.y -= 0.025;
}
if (keyState[SDL_SCANCODE_LSHIFT] == 1) {
this->position.y += 0.025;
}
if (keyState[SDL_SCANCODE_LEFT] == 1) {
this->angles.y +=2.0f;
}
if (keyState[SDL_SCANCODE_RIGHT] == 1) {
this->angles.y -=2.0f;
//std::cout << this->angles.y << std::endl;
}
if (keyState[SDL_SCANCODE_UP] == 1) {
this->angles.x +=2.0f;
//std::cout << this->angles.x << std::endl;
}
if (keyState[SDL_SCANCODE_DOWN] == 1) {
this->angles.x -=2.0f;
//std::cout << this->angles.x << std::endl;
}
}
this->angles.clamp();
}
void render() {
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
//glTranslatef(-position.x, -position.y, -position.z);
// up vector
glRotatef(-angles.x,1.0f, 0.0f, 0.0f);
glRotatef(-angles.y,0.0f, 1.0f, 0.0f);
glTranslatef(0.0f, 0.0f, 0.0f);
gluLookAt(position.x, position.y, position.z, // camera position
position.x, position.y, -engine->farPlane+position.z, // target position, We're always *looking at* the far plane straight ahead so the camera never turns around.
0.0f, 1.0f, 0.0f);
std::cout << "Camera:" << std::endl;
std::cout << "X: " << position.x << " Y: " << position.y << " Z: " << position.z << std::endl;
std::cout << "Pitch: " << angles.x << " Yaw: " << angles.y << " Roll: " << angles.z << std::endl;
}
};

19
src/types/entity.h Normal file
View File

@@ -0,0 +1,19 @@
#pragma once
#include <cstdint>
#include "vector3.h"
class entity {
public:
bool draw;
bool collidable;
uint32_t ticksAlive; //At 64tps it'd take 776 days to overflow.
Position position; //X Y Z
angle angles = {0,0,0}; //Pitch Yaw Roll, The orientation of the entity in the world,
angle velAngles = {0,0,0}; //The angle of an entities velocity.
void destruct() {
//TODO: Search entity list for this entity and remove it to avoid use-after-free.
delete this;
}
virtual ~entity() {}
};

28
src/types/entityList.h Normal file
View File

@@ -0,0 +1,28 @@
#pragma once
#include <vector>
#include "entity.h"
#include "player.h"
#include "camera.h"
class entitylist {
public:
std::vector<entity*> list;
inline player* getPlayer() {
for (auto& e : this->list)
if (auto* p = dynamic_cast<player*>(e) )
return dynamic_cast<player *>(e);
}
inline Camera* getCamera() {
for (auto& e : this->list)
if (auto* p = dynamic_cast<Camera*>(e) )
return dynamic_cast<Camera *>(e);
}
inline void storeEntity(entity* e) {
this->list.push_back(e);
}
};
//the primary entity list.
entitylist entityList;

10
src/types/geometry.h Normal file
View File

@@ -0,0 +1,10 @@
#pragma once
#include <SDL2/SDL_opengl.h>
#include <vector>
class Geometry {
public:
//I would use array for this, But from what I've read array of unknown size
//as a member of class was removed after c++99.
std::vector<GLfloat> vertices;
std::vector<GLuint> indices;
};

21
src/types/player.h Normal file
View File

@@ -0,0 +1,21 @@
#pragma once
#include "entity.h"
class player : public entity {
public:
bool alive;
uint8_t health;
uint8_t state;
Position cameraTarget; //The point where the 3rd-person camera will want to look at.
//Each type of entity will have an "update" function and a "render" function.
//These will be declared in each type of entity and not in the base entity because
//It'll be different for each one.
void update() {
}
void render() {
}
};

48
src/types/skybox.h Normal file
View File

@@ -0,0 +1,48 @@
#pragma once
#include <SDL2/SDL_opengl.h>
#include "entity.h"
#include "geometry.h"
class skybox : public entity {
public:
const Geometry geometry = {
{0.000f, 1.000f, 0.000f,
0.894f, 0.2235f, 0.000f,
0.276f, 0.2235f, -0.4255f,
-0.724f, 0.2235f, -0.263f,
-0.724f, 0.2235f, 0.263f,
0.276f, 0.2235f, 0.4255f,
0.724f, -0.2235f, -0.263f,
-0.276f, -0.2235f, -0.4255f,
-0.894f, -0.2235f, 0.000f,
-0.276f, -0.2235f, 0.4255f,
0.724f, -0.2235f, 0.263f,
0.000f, -1.000f, 0.000f},
{0, 1, 2,
0, 2, 3,
0, 3, 4,
0, 4, 5,
0, 5, 1,
1, 6, 2,
2, 7, 3,
3, 8, 4,
4, 9, 5,
5, 10, 1,
11, 6, 1,
11, 7, 2,
11, 8, 3,
11, 9, 4,
11, 10, 5,
6, 7, 2,
7, 8, 3,
8, 9, 4,
9, 10, 5,
10, 6, 1}
};
void render() {
//TODO: Teleport the sphere such that the center of the sphere is the cameras position every frame.
}
};

69
src/types/vector3.h Normal file
View File

@@ -0,0 +1,69 @@
#pragma once
#include <cstdint>
#include <cmath>
#include <cstdlib>
class vector3{
public:
float x = 0; //pitch
float y = 0; //yaw
float z = 0; //roll
};
class angle : public vector3 {
public:
void clamp() {
if (this->x > 89.0f)
this->x = 89.0f;
if (this->x <= -89.0f)
this->x = -89.0f;
//TODO: Make this seamless by getting the amount they rotated passed -180 and +180 by.
if (this->y <= -180.0f)
this->y = 180.0f;
if (this->y >= 180.01f)
this->y = -179.9f;
if (this->z >= 360.0f)
this->z = 0.0;
if(this->z <= -360.0f)
this->z = 0.0;
}
};
class Position : public vector3 {
public:
Position* get() {
return this;
}
void set(Position p) {
this->x = p.x;
this->y = p.y;
this->z = p.z;
}
void set(Position* p) {
this->x = p->x;
this->y = p->y;
this->z = p->z;
}
void set(vector3 v) {
this->x = v.x;
this->y = v.y;
this->z = v.z;
}
void set(vector3* v) {
this->x = v->x;
this->y = v->y;
this->z = v->z;
}
void set(float x, float y, float z) {
this->x = x;
this->y = y;
this->z = z;
}
};

45
src/types/vertex.h Normal file
View File

@@ -0,0 +1,45 @@
#pragma once
#include <SDL2/SDL_opengl.h>
#include <vector>
#include <iostream>
#include <fstream>
#include <sstream>
#include <SDL2/SDL_opengl.h>
#include <GL/glu.h>
#include <GL/gl.h>
struct vertex {
GLfloat x, y, z;
};
class vertexarray {
public:
std::vector<vertex> vertices;
std::vector<GLint> indices;
void load(const char* file) {
//This will take a .obj
std::ifstream f(file);
std::string line;
while (std::getline(f, line)) {
std::istringstream iss(line);
std::string prefix;
iss >> prefix;
if (prefix == "v") {
vertex v;
iss >> v.x >> v.y >> v.z;
this->vertices.push_back(v);
}
else if (prefix == "f") {
unsigned int v1, v2, v3;
char slash;
iss >> v1 >> slash >> slash >> v2 >> slash >> slash >> v3;
this->indices.push_back(v1 - 1);
this->indices.push_back(v2 - 1);
this->indices.push_back(v3 - 1);
}
}
f.close();
}
};

0
src/types/world.h Normal file
View File