279 lines
9.0 KiB
C++
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();
|
|
} |