Files
Re3D/src/engine/engine.cpp

279 lines
9.0 KiB
C++

#include <sstream>
#include <thread>
#include <JGL/JGL.h>
#include <Redacted3D/engine/engine.h>
#include <Redacted3D/types/entity/3D/camera.h>
#include <jlog/jlog.hpp>
using namespace J3ML;
std::vector<float> perspective(float fov, float aspect, float nearPlane, float farPlane) {
std::vector<float> result(16);
float f = 1.0f / tan(fov * 0.5f * M_PI / 180.0f);
result[0] = f / aspect;
result[5] = f;
result[10] = (farPlane + nearPlane) / (nearPlane - farPlane);
result[11] = -1.0f;
result[14] = (2.0f * farPlane * nearPlane) / (nearPlane - farPlane);
return result;
}
void Engine::quit() const {
//window->destroyWindow();
exit(0);
}
void outputErrorCode() {
switch (engine->getError().error) {
case ENGINE_ERROR_CODE::NO_ERROR:
std::cerr << "NO ERROR" << std::endl;
break;
case ENGINE_ERROR_CODE::ANIM_OUT_OF_BOUNDS:
std::cerr << "ANIM OUT OF BOUNDS" << std::endl;
break;
case ENGINE_ERROR_CODE::TEXTURE_NOT_FOUND:
std::cerr << "TEXTURE NOT FOUND" << std::endl;
break;
case ENGINE_ERROR_CODE::SHADER_COMPILATION_ERROR:
std::cerr << "SHADER COMPILATION ERROR" << std::endl;
break;
case ENGINE_ERROR_CODE::SHADER_LINKING_ERROR:
std::cerr << "SHADER LINKING ERROR" << std::endl;
break;
case ENGINE_ERROR_CODE::INVALID_SHADER_TYPE:
std::cerr << "INVALID SHADER TYPE" << std::endl;
break;
case ENGINE_ERROR_CODE::SHADER_NOT_FOUND:
std::cerr << "SHADER NOT FOUND" << std::endl;
break;
case ENGINE_ERROR_CODE::MODEL_NOT_FOUND:
std::cerr << "MODEL NOT FOUND" << std::endl;
break;
case ENGINE_ERROR_CODE::MULTI_TEXTURE_SIZE_EXCEEDS:
std::cerr << "MULTI-TEXTURE SIZE EXCEEDS" << std::endl;
break;
case ENGINE_ERROR_CODE::TEXTURE_ERASED_WHILE_IN_USE:
std::cerr << "TEXTURE ERASED WHILE IN USE" << std::endl;
case ENGINE_ERROR_CODE::VERTEX_ARRAY_ERASED_WHILE_IN_USE:
std::cerr << "VERTEX ARRAY ERASED WHILE IN USE" << std::endl;
break;
}
}
float Engine::getGLVersion() {
const std::string gl_major = std::string(((const char*) (glGetString(GL_VERSION)))).substr(0, 3);
return std::stof(gl_major);
}
void Engine::initGL() const {
gladLoadGL();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
auto window_size = window->getSize();
auto aspect = (float) window_size[0] / (float) window_size[1];
//glMultMatrixf(Matrix4x4::OpenGLPerspProjRH(nearPlane, farPlane, window_size.x, window_size.y).ptr());
glMultMatrixf(perspective(fov, aspect, nearPlane, farPlane).data());
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glClearColor(0.f, 0.f, 0.f, 0.f);
glViewport(0,0,window_size.x,window_size.y);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glDepthMask(GL_TRUE);
glEnable(GL_CULL_FACE);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
glEnable(GL_LIGHT0);
}
void Engine::init() {
engine->window->Open();
engine->initGL();
engine->loadConfig();
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
}
void Engine::preRender() {
engine->window->pollEvents();
engine->frameCount++;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
for (auto& e : world->GetChildren())
e->pre_render();
}
void Engine::render() {
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
if(world->getActiveCamera() != nullptr)
world->getActiveCamera()->render();
for (auto& e : world->GetChildren())
if (e->draw)
if (auto* c = dynamic_cast<Camera*>(e); c == nullptr) //If it's not a camera.
e->render();
int glError = glGetError();
if (glError != GL_NO_ERROR) {
FATAL("GL Error: " + std::to_string(glError))
exit(0);
}
}
void Engine::jglRenderPass() {
JGL::Update(engine->window->getSize());
glDisable(GL_LIGHTING);
///If elements are attached to a camera that's not the active one then they shouldn't be drawn.
if(world->getActiveCamera() != nullptr)
world->getActiveCamera()->jglRenderPass();
for (auto& e : world->GetChildren())
if (e->draw)
if (auto* c = dynamic_cast<Camera*>(e); c == nullptr) //If it's not a camera.
e->jglRenderPass();
glEnable(GL_LIGHTING);
}
void Engine::postRender() {
for (auto& e : engine->world->GetChildren())
e->post_render();
ReWindow::RWindow::glSwapBuffers();
}
void Engine::renderPass() {
preRender();
render();
jglRenderPass();
postRender();
}
[[noreturn]] void Engine::renderLoop() {
while (true) {
auto start = std::chrono::high_resolution_clock::now();
renderPass();
auto stop = std::chrono::high_resolution_clock::now();
auto dT = std::chrono::duration_cast<std::chrono::microseconds>(stop - start);
if (dT < std::chrono::microseconds(1000)) {
auto remaining = std::chrono::microseconds(1000) - dT;
std::this_thread::sleep_for(remaining);
dT += remaining;
}
// Decrease framerate when focus is lost.
if (!engine->window->getFlag(RWindowFlags::IN_FOCUS)) {
auto remaining = std::chrono::microseconds((int) 41666.67f) - dT;
std::this_thread::sleep_for(remaining);
dT += remaining;
}
engine->frameDelta = dT.count() / 1000000.0f;
}
}
float Engine::framerate() const {
return 1.f / frameDelta;
}
void Engine::loadConfig() {
std::ifstream file("cfg/engine.cfg");
if (!file.is_open()) {
std::cerr << "Couldn't load engine config." << std::endl;
quit();
}
std::string line;
while (std::getline(file, line)) {
std::istringstream stream(line);
std::string prefix;
stream >> prefix;
//if (prefix == "Resolution:") {
//int x, y;
//stream >> x >> y;
//window->setSize(x,y);
//glViewport(0,0,x,y);
//}
if (prefix == "Fullscreen:")
stream >> fullscreen;
if (prefix == "Debug:")
stream >> debug;
if (prefix == "CameraFOV:")
stream >> fov;
}
//If we have a window already.
//if (window != NULL) {
//window->destroyWindow();
//Some window managers won't handle destroying and remaking the window with zero delay very well.
//std::this_thread::sleep_for(std::chrono::milliseconds(500));
//initVideo();
//initGL();
//}
if (debug) {
std::cout << "Fullscreen: " << fullscreen << std::endl;
std::cout << "Resolution: " << window->getSize()[0] << "x" << window->getSize()[1] << std::endl;
std::cout << "Camera FOV: " << fov << std::endl;
std::cout << "Debug: " << debug << std::endl;
}
}
void Engine::setError(ENGINE_ERROR_CODE code, bool critical) {
error.error = code;
error.critical = critical;
outputErrorCode();
if (error.critical)
engine->quit(code);
}
EngineError Engine::getError() {
return error;
}
void Engine::quit(ENGINE_ERROR_CODE code) const {
//window->destroyWindow();
exit((int) code);
}
void Engine::takeScreenshot() {
int width = (int) engine->window->getSize().x;
int height = (int) engine->window->getSize().y;
std::vector<unsigned char> frameBuffer(3 * width * height);
glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, frameBuffer.data());
std::string fileName = std::to_string(engine->frameCount) + ".bmp";
std::ofstream file(fileName, std::ios::out | std::ios::binary);
int fileSize = 54 + 3 * width * height;
unsigned char bmpfileheader[14] = {'B', 'M', 0, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0};
unsigned char bmpinfoheader[40] = {40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 24, 0};
bmpfileheader[2] = (unsigned char) (fileSize);
bmpfileheader[3] = (unsigned char) (fileSize >> 8);
bmpfileheader[4] = (unsigned char) (fileSize >> 16);
bmpfileheader[5] = (unsigned char) (fileSize >> 24);
bmpinfoheader[4] = (unsigned char) (width);
bmpinfoheader[5] = (unsigned char) (width >> 8);
bmpinfoheader[6] = (unsigned char) (width >> 16);
bmpinfoheader[7] = (unsigned char) (width >> 24);
bmpinfoheader[8] = (unsigned char) (height);
bmpinfoheader[9] = (unsigned char) (height >> 8);
bmpinfoheader[10] = (unsigned char) (height >> 16);
bmpinfoheader[11] = (unsigned char) (height >> 24);
file.write((char*)bmpfileheader, 14);
file.write((char*)bmpinfoheader, 40);
int padding = (4 - (width * 3) % 4) % 4;
for (int y = height - 1; y >= 0; --y) {
for (int x = 0; x < width; ++x) {
unsigned char pixel[3];
pixel[0] = frameBuffer[(y * width + x) * 3 + 2];
pixel[1] = frameBuffer[(y * width + x) * 3 + 1];
pixel[2] = frameBuffer[(y * width + x) * 3];
file.write((char*)pixel, 3);
}
for (int p = 0; p < padding; ++p) {
file.write((char*)"\0", 1);
}
}
file.close();
}