got something rendering

This commit is contained in:
Jay Robson 2024-01-21 01:36:21 +11:00
parent 714300930f
commit 480852ff76
15 changed files with 696 additions and 208 deletions

View File

@ -3,10 +3,10 @@ cmake_minimum_required(VERSION 3.25)
project(FastNuclearSim VERSION 1.0)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_FLAGS "-g -lncurses")
set(CMAKE_CXX_FLAGS "-g -lncurses -I/usr/include/freetype2")
file(GLOB_RECURSE SOURCES src/*.cpp)
add_executable(FastNuclearSim ${SOURCES})
target_link_libraries(FastNuclearSim PUBLIC stdc++ m)
target_link_libraries(FastNuclearSim PUBLIC stdc++ m GLEW glfw GL freetype)

56
src/graphics/arrays.cpp Normal file
View File

@ -0,0 +1,56 @@
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "arrays.hpp"
#include "font.hpp"
using namespace sim::graphics;
static unsigned int vao, vbo, ebo;
static void* ptr_diff(void* a, void* b)
{
return (void*)((size_t)a - (size_t)b);
}
unsigned int arrays::init()
{
vertex v;
unsigned long handle = font::chars['*'].handle;
arrays::vertex vertices[4] = {
{handle, {0.0f, 1.0f}, {-0.5f, -0.5f, 0.0f}},
{handle, {0.0f, 0.0f}, {-0.5f, 0.5f, 0.0f}},
{handle, {1.0f, 1.0f}, { 0.5f, -0.5f, 0.0f}},
{handle, {1.0f, 0.0f}, { 0.5f, 0.5f, 0.0f}}
};
int indices[6] = {
0, 1, 3, 0, 2, 3
};
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(1, &vbo);
glGenBuffers(1, &ebo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glVertexAttribLPointer(0, 1, GL_UNSIGNED_INT64_ARB, sizeof(v), ptr_diff(&v.texid, &v));
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, false, sizeof(v), ptr_diff(&v.texpos, &v));
glEnableVertexAttribArray(1);
glVertexAttribPointer(2, 3, GL_FLOAT, false, sizeof(v), ptr_diff(&v.pos, &v));
glEnableVertexAttribArray(2);
return vao;
}

20
src/graphics/arrays.hpp Normal file
View File

@ -0,0 +1,20 @@
#pragma once
#include <glm/vec2.hpp>
#include <glm/vec3.hpp>
namespace sim::graphics::arrays
{
struct vertex
{
unsigned long texid;
glm::vec2 texpos;
glm::vec3 pos;
};
unsigned int init();
};

72
src/graphics/font.cpp Normal file
View File

@ -0,0 +1,72 @@
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include <glm/vec2.hpp>
#include <iostream>
#include "font.hpp"
using namespace sim::graphics;
font::character font::chars[128];
void font::init()
{
FT_Library ft;
FT_Face face;
if(FT_Init_FreeType(&ft))
{
std::cout << "Error: failed to init freetype\n";
return;
}
if(FT_New_Face(ft, "/usr/share/fonts/noto/NotoSans-Regular.ttf", 0, &face))
{
std::cout << "Error: failed to load freetype font\n";
return;
}
FT_Set_Pixel_Sizes(face, 0, 1024);
GLuint texids[128];
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
for(int i = 0; i < 128; i++)
{
if(FT_Load_Char(face, (char)i, FT_LOAD_RENDER))
{
std::cout << "Error: failed to load glyph " << i << "\n";
}
character& c = chars[i];
c.advance = face->glyph->advance.x;
c.size = {face->glyph->bitmap.width, face->glyph->bitmap.rows};
c.bearing = {face->glyph->bitmap_left, face->glyph->bitmap_top};
if(c.size.x == 0 || c.size.y == 0)
{
c.handle = 0;
continue;
}
glCreateTextures(GL_TEXTURE_2D, 1, &texids[i]);
glTextureStorage2D(texids[i], 1, GL_R8, c.size.x, c.size.y);
glTextureSubImage2D(texids[i], 0, 0, 0, c.size.x, c.size.y, GL_RED, GL_UNSIGNED_BYTE, face->glyph->bitmap.buffer);
glTextureParameteri(texids[i], GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTextureParameteri(texids[i], GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTextureParameteri(texids[i], GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTextureParameteri(texids[i], GL_TEXTURE_MAG_FILTER, GL_NEAREST);
c.handle = glGetTextureHandleARB(texids[i]);
glMakeTextureHandleResidentARB(c.handle);
chars[i] = c;
}
}

20
src/graphics/font.hpp Normal file
View File

@ -0,0 +1,20 @@
#pragma once
namespace sim::graphics::font
{
struct character
{
unsigned long handle;
long advance;
glm::ivec2 size;
glm::ivec2 bearing;
};
void init();
extern character chars[128];
};

24
src/graphics/keyboard.cpp Normal file
View File

@ -0,0 +1,24 @@
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "keyboard.hpp"
#include "window.hpp"
#include "resize.hpp"
using namespace sim::graphics;
static void cb_keypress(GLFWwindow* win, int key, int sc, int action, int mods)
{
if(key == GLFW_KEY_F11 && action == GLFW_RELEASE)
{
resize::toggle_fullscreen();
}
}
void keyboard::init()
{
GLFWwindow* win = window::get_window();
glfwSetKeyCallback(win, cb_keypress);
}

10
src/graphics/keyboard.hpp Normal file
View File

@ -0,0 +1,10 @@
#pragma once
namespace sim::graphics::keyboard
{
void init();
};

54
src/graphics/resize.cpp Normal file
View File

@ -0,0 +1,54 @@
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "resize.hpp"
#include "window.hpp"
using namespace sim::graphics;
static bool is_fullscreen = false;
static int win_w = 800;
static int win_h = 600;
static int win_restore_w;
static int win_restore_h;
static int win_restore_x;
static int win_restore_y;
void resize::toggle_fullscreen()
{
GLFWwindow* win = window::get_window();
is_fullscreen = !is_fullscreen;
if(is_fullscreen)
{
win_restore_w = win_w;
win_restore_h = win_h;
glfwGetWindowPos(win, &win_restore_x, &win_restore_y);
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
const GLFWvidmode* mode = glfwGetVideoMode(monitor);
glfwSetWindowMonitor(win, monitor, 0, 0, mode->width, mode->height, mode->refreshRate);
}
else
{
glfwSetWindowMonitor(win, nullptr, win_restore_x, win_restore_y, win_restore_w, win_restore_h, 0);
}
}
static void cb_framebuffer_size(GLFWwindow* win, int w, int h)
{
win_w = w;
win_h = h;
glViewport(0, 0, w, h);
}
void resize::init()
{
GLFWwindow* win = window::get_window();
glfwSetFramebufferSizeCallback(win, cb_framebuffer_size);
}

11
src/graphics/resize.hpp Normal file
View File

@ -0,0 +1,11 @@
#pragma once
namespace sim::graphics::resize
{
void init();
void toggle_fullscreen();
};

87
src/graphics/shader.cpp Normal file
View File

@ -0,0 +1,87 @@
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include "shader.hpp"
#include "window.hpp"
using namespace sim::graphics;
static const char* VERTEX_SHADER = R"(
#version 460 core
#extension GL_ARB_bindless_texture : require
layout (location = 0) in sampler2D aTex;
layout (location = 1) in vec2 aTexPos;
layout (location = 2) in vec3 aPos;
out flat sampler2D tex;
out vec2 texPos;
void main()
{
gl_Position = vec4(aPos, 1.0);
texPos = aTexPos;
tex = aTex;
}
)";
static const char* FRAGMENT_SHADER = R"(
#version 460 core
#extension GL_ARB_bindless_texture : require
in flat sampler2D tex;
in vec2 texPos;
out vec4 FragColour;
void main()
{
FragColour = vec4(1) * texture2D(tex, texPos);
}
)";
static unsigned int prog_id;
static int load_shader(const char** src, int type)
{
int id = glCreateShader(type);
glShaderSource(id, 1, src, nullptr);
glCompileShader(id);
return id;
}
unsigned int shader::init_program()
{
int success;
int vsh_id = load_shader(&VERTEX_SHADER, GL_VERTEX_SHADER);
int fsh_id = load_shader(&FRAGMENT_SHADER, GL_FRAGMENT_SHADER);
prog_id = glCreateProgram();
glAttachShader(prog_id, vsh_id);
glAttachShader(prog_id, fsh_id);
glLinkProgram(prog_id);
glGetProgramiv(prog_id, GL_LINK_STATUS, &success);
if(!success)
{
char infoLog[512];
glGetProgramInfoLog(prog_id, 512, NULL, infoLog);
std::cout << "Shader Link Error: " << infoLog << std::endl;
window::close();
return 0;
}
glUseProgram(prog_id);
glDeleteShader(vsh_id);
glDeleteShader(fsh_id);
return prog_id;
}

10
src/graphics/shader.hpp Normal file
View File

@ -0,0 +1,10 @@
#pragma once
namespace sim::graphics::shader
{
unsigned int init_program();
};

89
src/graphics/window.cpp Normal file
View File

@ -0,0 +1,89 @@
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include "arrays.hpp"
#include "keyboard.hpp"
#include "resize.hpp"
#include "window.hpp"
#include "shader.hpp"
#include "font.hpp"
using namespace sim::graphics;
static GLFWwindow* win;
static bool win_should_close = false;
void GLAPIENTRY cb_debug_message(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam)
{
std::cout << "GL CALLBACK: " << message << "\n";
}
void window::create()
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, true);
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, true);
win = glfwCreateWindow(800, 600, "FastNuclearSim", nullptr, nullptr);
glfwMakeContextCurrent(win);
GLenum err = glewInit();
if(err != GLEW_OK)
{
std::cout << "GLEW Init Failed: " << glewGetErrorString(err) << "\n";
close();
return;
}
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
glDebugMessageCallback(cb_debug_message, nullptr);
keyboard::init();
resize::init();
font::init();
shader::init_program();
arrays::init();
glViewport(0, 0, 800, 600);
}
void window::loop()
{
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glfwSwapBuffers(win);
glfwPollEvents();
}
bool window::should_close()
{
return win_should_close || glfwWindowShouldClose(win);
}
void window::close()
{
win_should_close = true;
}
void window::destroy()
{
glfwTerminate();
}
GLFWwindow* window::get_window()
{
return win;
}

18
src/graphics/window.hpp Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include <GLFW/glfw3.h>
namespace sim::graphics::window
{
void create();
bool should_close();
void loop();
void destroy();
void close();
GLFWwindow* get_window();
}

View File

@ -1,216 +1,17 @@
#include "reactor/builder.hpp"
#include "reactor/control/control_rod.hpp"
#include "reactor/fuel/fuel_rod.hpp"
#include "reactor/coolant/pipe.hpp"
#include "reactor/coolant/heater.hpp"
#include "reactor/coolant/vessel.hpp"
#include "coolant/fluid_t.hpp"
#include "coolant/valve.hpp"
#include "coolant/pump.hpp"
#include "display.hpp"
#include "graphics/window.hpp"
#include <cmath>
#include <sstream>
#include <unistd.h>
#include <curses.h>
#include <sys/time.h>
const bool do_graphics = true;
const bool do_clock = true;
unsigned long get_now()
{
struct timeval tv;
gettimeofday(&tv, nullptr);
return (unsigned long)tv.tv_sec * 1000000 + tv.tv_usec;
}
using namespace sim;
int main()
{
std::random_device rd;
std::mt19937 rand(rd());
if(do_graphics)
graphics::window::create();
while(!graphics::window::should_close())
{
initscr();
cbreak();
noecho();
keypad(stdscr, TRUE);
nodelay(stdscr, TRUE);
curs_set(0);
}
sim::reactor::coolant::vessel vessel(200, 400, sim::coolant::WATER);
sim::reactor::reactor<5, 5> reactor = sim::reactor::builder<5, 5>(
sim::reactor::fuel::fuel_rod(2000, 4000),
sim::reactor::control::control_rod(vessel, 10000, 1),
sim::reactor::coolant::pipe(vessel), {
"#C#C#",
"CFCFC",
"#C#C#",
"CFCFC",
"#C#C#"
});
sim::coolant::valve<sim::reactor::coolant::vessel> valve(vessel, 1, 500);
sim::coolant::pump<sim::reactor::coolant::vessel> pump(vessel, 1e4, 15);
double secs = 0;
long clock = get_now();
double speed = 1;
int framerate = 100;
int steps_extra = 1;
for(;;)
{
std::stringstream ss;
ss << "Reactor Core\n\n";
{
long mins = secs / 60;
long hours = mins / 60;
long days = hours / 24;
long years = days / 365;
double s = fmod(secs, 60);
mins %= 60;
hours %= 24;
days %= 365;
ss << "Time:\n";
if(years > 0) goto years;
if(days > 0) goto days;
if(hours > 0) goto hours;
if(mins > 0) goto mins;
goto secs;
years: ss << years << "y ";
days: ss << days << "d ";
hours: ss << hours << "h ";
mins: ss << mins << "m ";
secs: ss << s << "s\n";
ss << "Speed: " << speed << "x\n\n";
}
for(int i = 0; i < steps_extra; i++)
{
double dt = speed / framerate / steps_extra;
reactor.update(rand, dt);
pump.update(dt);
valve.update(dt);
vessel.update();
secs += dt;
}
ss << "Vessel\n" << vessel << "\n";
ss << "Steam Valve\n" << valve << "\n";
ss << "Coolant Pump\n" << pump << "\n";
if(do_graphics)
{
erase();
display::draw_text(1, 0, ss.str().c_str());
}
const int X = 1, Y = 30;
const int W = 36, H = 11;
for(int x = 0; x < reactor.width; x++)
for(int y = 0; y < reactor.height; y++)
{
int id = y * reactor.width + x;
sim::reactor::rod* r = reactor.rods[id];
if(!r->should_display())
{
continue;
}
std::stringstream ss;
ss << *r;
int px = X + (H - 1) * y;
int py = Y + (W - 1) * x;
if(do_graphics)
{
display::draw_text(px + 1, py + 2, ss.str().c_str());
display::draw_box(px, py, H, W);
}
if(do_graphics && r->should_select() && id == reactor.cursor)
{
display::draw_text(px + 1, py + W - 5, "[ ]");
}
if(do_graphics && r->is_selected())
{
display::draw_text(px + 1, py + W - 4, "#");
}
}
int c = 0;
if(do_graphics)
{
refresh();
c = getch();
}
switch(c)
{
case KEY_LEFT:
reactor.move_cursor(-1);
break;
case KEY_RIGHT:
reactor.move_cursor(1);
break;
case KEY_UP:
reactor.update_selected(1);
break;
case KEY_DOWN:
reactor.update_selected(-1);
break;
case ' ':
reactor.toggle_selected();
break;
case 't':
speed *= 10;
break;
case 'g':
speed /= 10;
break;
case 'r':
valve.open(0.001);
break;
case 'f':
valve.open(-0.001);
break;
case 'e':
pump.change_speed(0.01);
break;
case 'd':
pump.change_speed(-0.01);
break;
}
if(do_clock)
{
long now = get_now();
while(clock + 1e6 / framerate > now)
{
usleep(clock + 1e6 / framerate - now);
now = get_now();
}
clock += 1e6 / framerate;
}
graphics::window::loop();
}
return 0;
graphics::window::destroy();
}

216
src/main.cpp.old Normal file
View File

@ -0,0 +1,216 @@
#include "reactor/builder.hpp"
#include "reactor/control/control_rod.hpp"
#include "reactor/fuel/fuel_rod.hpp"
#include "reactor/coolant/pipe.hpp"
#include "reactor/coolant/heater.hpp"
#include "reactor/coolant/vessel.hpp"
#include "coolant/fluid_t.hpp"
#include "coolant/valve.hpp"
#include "coolant/pump.hpp"
#include "display.hpp"
#include <cmath>
#include <sstream>
#include <unistd.h>
#include <curses.h>
#include <sys/time.h>
const bool do_graphics = true;
const bool do_clock = true;
unsigned long get_now()
{
struct timeval tv;
gettimeofday(&tv, nullptr);
return (unsigned long)tv.tv_sec * 1000000 + tv.tv_usec;
}
int main()
{
std::random_device rd;
std::mt19937 rand(rd());
if(do_graphics)
{
initscr();
cbreak();
noecho();
keypad(stdscr, TRUE);
nodelay(stdscr, TRUE);
curs_set(0);
}
sim::reactor::coolant::vessel vessel(200, 400, sim::coolant::WATER);
sim::reactor::reactor<5, 5> reactor = sim::reactor::builder<5, 5>(
sim::reactor::fuel::fuel_rod(2000, 4000),
sim::reactor::control::control_rod(vessel, 10000, 1),
sim::reactor::coolant::pipe(vessel), {
"#C#C#",
"CFCFC",
"#C#C#",
"CFCFC",
"#C#C#"
});
sim::coolant::valve<sim::reactor::coolant::vessel> valve(vessel, 1, 500);
sim::coolant::pump<sim::reactor::coolant::vessel> pump(vessel, 1e4, 15);
double secs = 0;
long clock = get_now();
double speed = 1;
int framerate = 100;
int steps_extra = 1;
for(;;)
{
std::stringstream ss;
ss << "Reactor Core\n\n";
{
long mins = secs / 60;
long hours = mins / 60;
long days = hours / 24;
long years = days / 365;
double s = fmod(secs, 60);
mins %= 60;
hours %= 24;
days %= 365;
ss << "Time:\n";
if(years > 0) goto years;
if(days > 0) goto days;
if(hours > 0) goto hours;
if(mins > 0) goto mins;
goto secs;
years: ss << years << "y ";
days: ss << days << "d ";
hours: ss << hours << "h ";
mins: ss << mins << "m ";
secs: ss << s << "s\n";
ss << "Speed: " << speed << "x\n\n";
}
for(int i = 0; i < steps_extra; i++)
{
double dt = speed / framerate / steps_extra;
reactor.update(rand, dt);
pump.update(dt);
valve.update(dt);
vessel.update();
secs += dt;
}
ss << "Vessel\n" << vessel << "\n";
ss << "Steam Valve\n" << valve << "\n";
ss << "Coolant Pump\n" << pump << "\n";
if(do_graphics)
{
erase();
display::draw_text(1, 0, ss.str().c_str());
}
const int X = 1, Y = 30;
const int W = 36, H = 11;
for(int x = 0; x < reactor.width; x++)
for(int y = 0; y < reactor.height; y++)
{
int id = y * reactor.width + x;
sim::reactor::rod* r = reactor.rods[id];
if(!r->should_display())
{
continue;
}
std::stringstream ss;
ss << *r;
int px = X + (H - 1) * y;
int py = Y + (W - 1) * x;
if(do_graphics)
{
display::draw_text(px + 1, py + 2, ss.str().c_str());
display::draw_box(px, py, H, W);
}
if(do_graphics && r->should_select() && id == reactor.cursor)
{
display::draw_text(px + 1, py + W - 5, "[ ]");
}
if(do_graphics && r->is_selected())
{
display::draw_text(px + 1, py + W - 4, "#");
}
}
int c = 0;
if(do_graphics)
{
refresh();
c = getch();
}
switch(c)
{
case KEY_LEFT:
reactor.move_cursor(-1);
break;
case KEY_RIGHT:
reactor.move_cursor(1);
break;
case KEY_UP:
reactor.update_selected(1);
break;
case KEY_DOWN:
reactor.update_selected(-1);
break;
case ' ':
reactor.toggle_selected();
break;
case 't':
speed *= 10;
break;
case 'g':
speed /= 10;
break;
case 'r':
valve.open(0.001);
break;
case 'f':
valve.open(-0.001);
break;
case 'e':
pump.change_speed(0.01);
break;
case 'd':
pump.change_speed(-0.01);
break;
}
if(do_clock)
{
long now = get_now();
while(clock + 1e6 / framerate > now)
{
usleep(clock + 1e6 / framerate - now);
now = get_now();
}
clock += 1e6 / framerate;
}
}
return 0;
}