Merge pull request #1 from jsrobson10/graphics

Graphics
This commit is contained in:
Jay Robson 2024-02-06 01:16:12 +11:00 committed by GitHub
commit ac5fc2258f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
124 changed files with 4346 additions and 741 deletions

7
.gitattributes vendored
View File

@ -1,7 +1,8 @@
* !text !filter !merge !diff
*.blend filter=lfs diff=lfs merge=lfs -text
*.glb filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text *.png filter=lfs diff=lfs merge=lfs -text
*.stl filter=lfs diff=lfs merge=lfs -text
*.glb filter=lfs diff=lfs merge=lfs -text
*.blend filter=lfs diff=lfs merge=lfs -text
* !text !filter !merge !diff
*.obj filter=lfs diff=lfs merge=lfs -text *.obj filter=lfs diff=lfs merge=lfs -text
*.fbx filter=lfs diff=lfs merge=lfs -text *.fbx filter=lfs diff=lfs merge=lfs -text
*.bin filter=lfs diff=lfs merge=lfs -text *.bin filter=lfs diff=lfs merge=lfs -text

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
build build
*.blend1

View File

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

23
README.md Normal file
View File

@ -0,0 +1,23 @@
# How to build
This is built using CMake. You will also need the required libraries to build.
This project currently is only tested on Linux.
```
mkdir build
cd build
cmake ..
make
```
# Required libraries
- GLEW
- GLFW
- OpenGL
- FreeType
- AssImp
# Credits
- [Potted House Plants](https://skfb.ly/opQN8) by FacultyManBruce is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/).

BIN
assets/font/DroidSans.ttf Normal file

Binary file not shown.

BIN
assets/model/primary_coolant_pump_switch.glb (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,3 @@
linear_extrude(0.0001)
square([1, 1], center = true);

Binary file not shown.

View File

@ -0,0 +1,10 @@
$fn = 256;
linear_extrude(0.0001)
translate([0.5, 0.5])
difference()
{
circle(d = 0.81);
circle(d = 0.8);
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
assets/model/secondary_coolant_pump_switch.glb (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
assets/scene-baked.glb (Stored with Git LFS) Normal file

Binary file not shown.

20
assets/shader/main.fsh Normal file
View File

@ -0,0 +1,20 @@
#version 460 core
#extension GL_ARB_bindless_texture : require
in float brightness;
in flat sampler2D tex;
in vec2 texPos;
out vec4 FragColour;
uniform mat4 tex_mat;
void main()
{
vec4 texdata = texture2D(tex, texPos);
FragColour = tex_mat * texdata * vec4(vec3(brightness), 1);
if(FragColour.a == 0) discard;
}

29
assets/shader/main.vsh Normal file
View File

@ -0,0 +1,29 @@
#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 vec4 aPos;
layout (location = 3) in vec3 aNormal;
uniform mat4 model;
uniform mat4 camera;
uniform mat4 projection;
out float brightness;
out flat sampler2D tex;
out vec2 texPos;
void main()
{
vec4 pos = camera * model * aPos;
vec3 cNormal = vec3(0.f, 0.f, 1.f) * mat3(camera * model);
brightness = dot(normalize(aNormal), normalize(cNormal)) * 0.25f + 0.75f;
gl_Position = projection * pos;
texPos = aTexPos;
tex = aTex;
}

View File

@ -0,0 +1,82 @@
# Blender 4.0.2 MTL File: 'None'
# www.blender.org
newmtl M_ClayPot1
Ns 380.250000
Ka 1.000000 1.000000 1.000000
Ks 0.370000 0.370000 0.370000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 2
map_Kd PlantsLargeUV.png
newmtl M_ClayPot2
Ns 380.250000
Ka 1.000000 1.000000 1.000000
Ks 0.370000 0.370000 0.370000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 2
map_Kd PlantsSmallUV.png
newmtl M_Dirt
Ns 36.000000
Ka 1.000000 1.000000 1.000000
Ks 0.050000 0.050000 0.050000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 2
map_Kd PlantsLargeUV.png
newmtl M_Dirt2
Ns 36.000000
Ka 1.000000 1.000000 1.000000
Ks 0.050000 0.050000 0.050000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 2
map_Kd PlantsSmallUV.png
newmtl M_GrassyLeaf
Ns 302.760040
Ka 1.000000 1.000000 1.000000
Ks 0.250000 0.250000 0.250000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 2
map_Kd PlantsLargeUV.png
newmtl M_IvyLeaf
Ns 324.000031
Ka 1.000000 1.000000 1.000000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 2
map_Kd PlantsSmallUV.png
newmtl M_LargeLeaf
Ns 492.839996
Ka 1.000000 1.000000 1.000000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 2
map_Kd PlantsLargeUV.png
newmtl M_SmallLeaf
Ns 380.250000
Ka 1.000000 1.000000 1.000000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 2
map_Kd PlantsSmallUV.png

BIN
assets/unbaked/AllPlants/AllPlants.obj (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/unbaked/AllPlants/PlantsLargeUV.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/unbaked/AllPlants/PlantsSmallUV.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/unbaked/scene.blend (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/unbaked/scene.stl Normal file

Binary file not shown.

BIN
assets/unbaked/scene/Keys.001.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/unbaked/scene/SCRAM.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/unbaked/scene/labels.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/unbaked/scene/labels.xcf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/unbaked/scene/smooth+white+tile-1024x1024.png (Stored with Git LFS) Normal file

Binary file not shown.

27
src/coolant/condenser.cpp Normal file
View File

@ -0,0 +1,27 @@
#include "condenser.hpp"
#include <cmath>
#include <iostream>
using namespace sim::coolant;
constexpr static double calc_cylinder(double h, double d)
{
double r = d / 2;
return M_PI * r * r * h;
}
condenser::condenser(fluid_t type, double height, double diameter, double mass, double level) :
height(height), diameter(diameter), fluid_holder(type, calc_cylinder(height, diameter), mass)
{
this->level = level;
}
void condenser::update(double secs)
{
((sim::coolant::fluid_holder*)this)->update(secs);
}

22
src/coolant/condenser.hpp Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#include "fluid_holder.hpp"
namespace sim::coolant
{
class condenser : public fluid_holder
{
const double height;
const double diameter;
public:
condenser(fluid_t type, double height, double diameter, double mass, double level);
void update(double dt);
};
};

View File

@ -0,0 +1,135 @@
#include "fluid_holder.hpp"
#include "../util/constants.hpp"
#include "../conversions/temperature.hpp"
#include "../reactor/fuel/half_life.hpp"
#include <cmath>
#include <iostream>
using namespace sim::coolant;
fluid_holder::fluid_holder(fluid_t fluid, double volume, double extra_mass) : fluid(fluid), volume(volume), extra_mass(extra_mass)
{
}
double fluid_holder::add_heat(double m1, double t1)
{
double t2 = get_heat();
double t = t1 - t2;
double m2 = get_thermal_mass();
double m = m1 + m2;
if(m1 == 0 || m2 == 0)
return t1;
heat = t1 - t * m2 / m;
return heat;
}
double fluid_holder::add_fluid(double v2, double t2)
{
if(level + v2 > volume)
{
v2 = volume - level;
}
int m1 = get_thermal_mass();
int m2 = fluid.l_to_g(v2);
double t1 = get_heat();
double t = t1 - t2;
heat = t1 - t * m2 / (m1 + m2);
level += v2;
return v2;
}
double fluid_holder::extract_fluid(double amount)
{
if(amount < level)
{
level -= amount;
}
else
{
amount = level;
level = 0;
}
return amount;
}
void fluid_holder::add_steam(double m2, double t2)
{
double m1 = get_thermal_mass();
double t1 = heat;
double m = m1 + m2;
if(m > 0)
{
heat = t1 - (t1 - t2) * m2 / (m1 + m2);
}
steam += m2;
}
double fluid_holder::get_pressure() const
{
double T = conversions::temperature::c_to_k(heat);
double V = get_steam_volume() * 0.001;
double n = fluid.g_to_mol(steam);
if(V == 0)
{
return 0;
}
return (n * T * constants::R) / V;
}
double fluid_holder::get_steam_density() const
{
double v = get_steam_volume();
return v > 0 ? steam / v : 0;
}
void fluid_holder::update(double secs)
{
double mass = get_thermal_mass();
if(mass > 0)
{
double V = get_steam_volume() * 0.001;
double P = fluid.vapor_pressure.calc_p(heat);
double T = conversions::temperature::c_to_k(heat);
double n = fluid.mol_to_g((V * P) / (T * constants::R)) - steam;
double s = steam + n;
double l = fluid.l_to_g(level) - n;
double v = fluid.l_to_g(volume);
if(l < 0)
{
s += l;
l = 0;
}
if(l > v)
{
l = v;
s = 0;
}
double diff = s - steam;
steam = s;
level = fluid.g_to_l(l);
heat -= diff * fluid.jPg / mass;
}
}

View File

@ -0,0 +1,45 @@
#pragma once
#include "fluid_t.hpp"
namespace sim::coolant
{
class fluid_holder
{
protected:
double level = 0; // litres
double steam = 0; // grams
double heat = 0; // celsius
public:
fluid_holder(fluid_t fluid, double volume, double extra_mass);
const fluid_t fluid;
const double volume; // litres
const double extra_mass; // grams
virtual double add_heat(double m, double t);
virtual double extract_fluid(double amount);
virtual double add_fluid(double amount, double heat);
virtual void add_steam(double amount, double t);
virtual double get_volume() const { return volume; } // litres
virtual double get_level() const { return level; } // litres
virtual double get_heat() const { return heat; } // celsius
virtual double get_steam() const { return steam; } // grams
virtual double get_steam_volume() const { return get_volume() - get_level(); } // litres
virtual double get_mass() const { return fluid.l_to_g(get_level()) + get_steam(); } // grams
virtual double get_thermal_mass() const { return get_mass() + extra_mass; } // grams
virtual double get_pressure() const; // pascals
virtual double get_steam_density() const; // g/L
void update(double dt);
};
};

View File

@ -11,14 +11,12 @@ struct fluid_t
const double gPl; // g/L const double gPl; // g/L
const double gPmol; // g/mol const double gPmol; // g/mol
const double jPg; // J/g latent heat of vaporisation const double jPg; // J/g latent heat of vaporisation
const double jPgk; // J/g/K heat capacity
const double bubble_speed; // m/s const double bubble_speed; // m/s
const coolant::vapor_pressure vapor_pressure; const coolant::vapor_pressure vapor_pressure;
constexpr fluid_t(double gPl, double gPmol, double jPg, double jPgk, double bubble_speed, coolant::vapor_pressure vapor_pressure) : constexpr fluid_t(double gPl, double gPmol, double jPg, double bubble_speed, coolant::vapor_pressure vapor_pressure) :
gPl(gPl), gPmol(gPmol), gPl(gPl), gPmol(gPmol), jPg(jPg),
jPg(jPg), jPgk(jPgk),
vapor_pressure(vapor_pressure), vapor_pressure(vapor_pressure),
bubble_speed(bubble_speed) bubble_speed(bubble_speed)
{ {
@ -33,7 +31,7 @@ struct fluid_t
constexpr double l_to_mol(double l) const { return g_to_mol(l_to_g(l)); } constexpr double l_to_mol(double l) const { return g_to_mol(l_to_g(l)); }
}; };
constexpr const fluid_t WATER = fluid_t(1000, 18, 2257, 4.1816, 0.3, {8.07131, 1730.63, 233.426}); constexpr const fluid_t WATER = fluid_t(1000, 18, 2257, 4.1816, {8.07131, 1730.63, 233.426});
} }

98
src/coolant/pump.cpp Normal file
View File

@ -0,0 +1,98 @@
#include "pump.hpp"
#include <cmath>
#include <iostream>
using namespace sim::coolant;
pump::pump(fluid_holder* src, fluid_holder* dst, double mass, double radius, double power, double l_per_rev, double friction) :
src(src), dst(dst), mass(mass), radius(radius), l_per_rev(l_per_rev), friction(friction)
{
this->power = power;
}
double pump::get_flow() const
{
return l_per_rev * get_rpm() * 60;
}
double pump::get_flow_mass() const
{
return src->fluid.l_to_g(get_flow());
}
double pump::get_rpm() const
{
return velocity / (M_PI * mass * 0.001 * radius * radius);
}
const char* pump::get_state_string()
{
if(!powered)
{
return "Off";
}
if(idling && std::abs(get_flow()) < 1e-3)
{
return "Idle";
}
if(idling)
{
return "Coasting";
}
return "Revving";
}
static double calc_work(double j, double mass)
{
double m = 1;
if(j < 0)
{
m = -1;
}
return m * std::sqrt(m * j / (mass * 0.001));
}
void pump::update(double dt)
{
idling = false;
if(powered && !idling)
{
velocity += calc_work(dt * power, mass);
}
fluid_holder fh_src(*src);
fluid_holder fh_dst(*dst);
double src_heat = src->get_heat();
double p_diff_1 = dst->get_pressure() - src->get_pressure();
double src_volume = fh_src.extract_fluid(get_flow() * dt);
double dst_volume = fh_dst.add_fluid(src_volume, src_heat);
src->extract_fluid(dst_volume);
dst->add_fluid(dst_volume, src_heat);
double p_diff_2 = dst->get_pressure() - src->get_pressure();
double p_diff = (p_diff_1 + p_diff_2) / 2;
double work = p_diff * dst_volume * 0.001 + get_rpm() * 60 * dt * friction;
velocity -= calc_work(work, mass);
if(dst->get_level() > 400 || src->get_level() < 10)
{
// idling = true;
}
else
{
// idling = false;
}
}

View File

@ -1,56 +1,37 @@
#pragma once #pragma once
#include <ostream> #include "fluid_holder.hpp"
namespace sim::coolant namespace sim::coolant
{ {
template <class A>
class pump class pump
{ {
const double max; fluid_holder* const src;
const double heat; fluid_holder* const dst;
A* a; const double mass; // grams
double rate = 0; const double radius; // meters
const double l_per_rev; // litres
const double friction; // J/rev
double velocity = 0; // m/s
double power = 0; // W
public: public:
constexpr pump(A& a, double max, double heat) : a(&a), max(max), heat(heat) bool powered = false;
{ bool idling = false;
} pump(fluid_holder* src, fluid_holder* dst, double mass, double radius, double power, double l_per_rev, double friction);
constexpr double get_rate() const double get_flow() const; // L/s
{ double get_flow_mass() const; // g/s
return rate; double get_rpm() const; // rev/min
}
constexpr double get_flow() const const char* get_state_string();
{ void update(double dt);
return rate * max;
}
constexpr void change_speed(double amount)
{
rate += amount;
if(rate < 0) rate = 0;
if(rate > 1) rate = 1;
}
void update(double secs)
{
a->add_fluid(rate * max * secs, heat);
}
friend std::ostream& operator<<(std::ostream& o, const pump& p)
{
o << "Rate: " << (p.get_rate() * 100) << " %\n";
o << "Flow: " << (p.get_flow() * 0.001) << " kg/s\n";
return o;
}
}; };
}; };

87
src/coolant/valve.cpp Normal file
View File

@ -0,0 +1,87 @@
#include "valve.hpp"
#include "../conversions/temperature.hpp"
#include "../util/constants.hpp"
#include <cmath>
#include <iostream>
using namespace sim::coolant;
valve::valve(fluid_holder* src, fluid_holder* dst, double state, double max) : src(src), dst(dst), max(max)
{
this->state = state;
}
void valve::add_open_speed(double v)
{
speed += v;
}
void valve::clear_open_speed()
{
speed = 0;
}
void valve::update(double dt)
{
state += speed * dt;
if(state > 1) state = 1;
if(state < 0) state = 0;
if(src->get_steam_volume() == 0 || dst->get_steam_volume() == 0)
{
flow = 0;
return;
}
double pressure1 = src->get_pressure(); // Pa
double pressure2 = dst->get_pressure();
int overshoots = 0;
double m = max * state * dt;
double temp, mass;
for(;;)
{
double diff = (pressure1 - pressure2) * m; // L
if(diff > 0)
{
temp = src->get_heat();
mass = std::min(diff * src->get_steam_density(), src->get_steam());
}
else
{
temp = dst->get_heat();
mass = std::min(diff * dst->get_steam_density(), dst->get_steam());
}
fluid_holder fh_src(*src);
fluid_holder fh_dst(*dst);
fh_src.add_steam(-mass, temp);
fh_dst.add_steam(mass, temp);
// if((pressure1 > fh_dst.get_pressure()) == (pressure2 < fh_src.get_pressure()))
{
break;
}
overshoots += 1;
m *= 0.5;
}
if(overshoots > 0)
{
std::cout << "Warning: overshot " << overshoots << " times\n";
}
src->add_steam(-mass, temp);
dst->add_steam(mass, temp);
this->flow = mass / dt;
}

View File

@ -1,55 +1,32 @@
#pragma once #pragma once
#include "fluid_holder.hpp"
namespace sim::coolant namespace sim::coolant
{ {
template <class A>
class valve class valve
{ {
A* a;
const double max; const double max;
const double pressure;
fluid_holder* const src;
fluid_holder* const dst;
double speed = 0;
double state = 0; double state = 0;
double rate = 0; double flow = 0; // L/s
public: public:
constexpr valve(A& a, double max, double pressure) : a(&a), max(max), pressure(pressure) valve(fluid_holder* src, fluid_holder* dst, double state, double max);
{
} void update(double secs);
void add_open_speed(double v);
void clear_open_speed();
constexpr double get_state() const constexpr double get_state() const { return state; }
{ constexpr double get_flow() const { return flow; }
return state;
}
constexpr void set_state(double v)
{
if(v > 1) v = 1;
if(v < 0) v = 0;
state = v;
}
constexpr void open(double v)
{
set_state(state + v);
}
constexpr void update(double secs)
{
rate = a->extract_steam(secs, state * max, pressure) / secs;
}
friend std::ostream& operator<<(std::ostream& o, const valve& v)
{
o << "Opened: " << (v.state * 100) << " %\n";
o << "Rate: " << v.rate << " g/s\n";
return o;
}
}; };
}; };

View File

@ -1,54 +0,0 @@
#include "display.hpp"
#include <curses.h>
#include <string.h>
#include <stdlib.h>
void display::draw_text(int x, int y, const char* at)
{
for(int i = 0;; i++)
{
const char* start = at;
char c = (at++)[0];
while(c != '\n' && c != '\0')
{
c = (at++)[0];
}
mvaddnstr(x + i, y, start, (size_t)(at - start));
if(c == '\0')
{
return;
}
}
}
void display::draw_box(int x, int y, int h, int w)
{
mvaddch(x, y, '+');
for(int i = 0; i < w - 2; i++)
{
addch('-');
}
addch('+');
for(int i = 0; i < h - 2; i++)
{
mvaddch(x + i + 1, y, '|');
mvaddch(x + i + 1, y + w - 1, '|');
}
mvaddch(x + h - 1, y, '+');
for(int i = 0; i < w - 2; i++)
{
addch('-');
}
addch('+');
}

View File

@ -1,11 +0,0 @@
#pragma once
namespace display
{
void draw_text(int x, int y, const char* str);
void draw_box(int x, int y, int h, int w);
}

34
src/electric/turbine.cpp Normal file
View File

@ -0,0 +1,34 @@
#include "turbine.hpp"
#include "../system.hpp"
#include <cmath>
#include <iostream>
using namespace sim::electric;
constexpr static double calc_cylinder(double h, double d)
{
double r = d / 2;
return M_PI * r * r * h;
}
turbine::turbine(coolant::fluid_t type, coolant::condenser* condenser, double length, double diameter, double mass) :
length(length), diameter(diameter), condenser(condenser),
sim::coolant::fluid_holder(type, calc_cylinder(length, diameter), mass)
{
this->level = level;
}
void turbine::update(double secs)
{
}
void turbine::add_steam(double amount, double t)
{
condenser->add_steam(amount, t);
}

40
src/electric/turbine.hpp Normal file
View File

@ -0,0 +1,40 @@
#pragma once
#include "../coolant/fluid_holder.hpp"
#include "../coolant/condenser.hpp"
namespace sim::electric
{
class turbine : public sim::coolant::fluid_holder
{
coolant::condenser* const condenser;
const double length;
const double diameter;
public:
turbine(coolant::fluid_t type, coolant::condenser* condenser, double length, double diameter, double mass);
void update(double dt);
virtual double add_heat(double m, double t) { return condenser->add_heat(m, t); }
virtual double extract_fluid(double amount) { return condenser->extract_fluid(amount); }
virtual double add_fluid(double amount, double heat) { return condenser->add_fluid(amount, heat); }
virtual void add_steam(double amount, double t);
virtual double get_volume() const { return condenser->get_volume(); }
virtual double get_level() const { return condenser->get_level(); }
virtual double get_heat() const { return condenser->get_heat(); } // celsius
virtual double get_steam() const { return condenser->get_steam(); } // grams
virtual double get_steam_volume() const { return condenser->get_steam_volume(); } // litres
virtual double get_mass() const { return condenser->get_mass(); } // grams
virtual double get_thermal_mass() const { return condenser->get_thermal_mass(); } // grams
virtual double get_pressure() const { return condenser->get_pressure(); } // pascals
virtual double get_steam_density() const { return condenser->get_steam_density(); } // g/L
};
};

109
src/graphics/camera.cpp Normal file
View File

@ -0,0 +1,109 @@
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "camera.hpp"
#include "input/keyboard.hpp"
#include "../util/math.hpp"
#include <cmath>
#include <iostream>
#include <glm/matrix.hpp>
#include <glm/vec3.hpp>
#include <glm/ext/matrix_transform.hpp>
using namespace sim::graphics;
static bool on_ground = false;
static double yaw = 0, pitch = 0;
static glm::vec<3, double> pos(0, 0, 2);
static glm::vec<3, double> velocity(0);
static glm::mat4 camera_mat;
void camera::rotate(double y, double p)
{
yaw += y * 0.05;
pitch -= p * 0.05;
if(pitch < 0) pitch = 0;
if(pitch > 180) pitch = 180;
}
void camera::move(double xoff, double yoff, double zoff)
{
pos.x += xoff;
pos.y += yoff;
pos.z += zoff;
}
glm::vec<3, double> camera::get_normal()
{
glm::mat<3, 3, double> mat(camera_mat);
return glm::vec<3, double>(0, 0, -1) * mat;
}
glm::vec<3, double> camera::get_pos()
{
return pos;
}
void camera::update(double dt)
{
glm::vec<2, double> off(0, 0);
double m = 30;
if(keyboard::is_pressed(GLFW_KEY_W))
off.y += 1;
if(keyboard::is_pressed(GLFW_KEY_S))
off.y -= 1;
if(keyboard::is_pressed(GLFW_KEY_A))
off.x -= 1;
if(keyboard::is_pressed(GLFW_KEY_D))
off.x += 1;
if(keyboard::is_pressed(GLFW_KEY_LEFT_SHIFT))
m *= 1.5;
if(off.x != 0 || off.y != 0)
off = glm::normalize(off);
double angle = glm::radians<double>(yaw);
glm::mat<2, 2, double> mat = {
std::cos(angle), std::sin(angle),
-std::sin(angle), std::cos(angle)
};
glm::vec<2, double> rotated = glm::vec<2, double>(off.x, off.y) * mat;
velocity.z -= 9.81 * dt;
if(on_ground)
{
velocity.x += rotated.x * m * dt;
velocity.y += rotated.y * m * dt;
}
if(on_ground && keyboard::is_pressed(GLFW_KEY_SPACE))
{
velocity.z += 3.5;
}
glm::vec<3, double> normal_last(0);
glm::vec<3, double> velocity2;
velocity2 = system::active.scene.calc_intersect(pos, velocity * dt, normal_last);
velocity2 = system::active.scene.calc_intersect(pos + glm::vec<3, double>(0, 0, -1.5), velocity2, normal_last) / dt;
pos += velocity2 * dt;
on_ground = ((velocity * dt / dt).z != velocity2.z);
velocity = velocity2 * std::pow(0.5, dt / (on_ground ? 0.05 : 10));
camera_mat = glm::mat4(1);
camera_mat = glm::rotate(camera_mat, (float)glm::radians(-pitch), glm::vec3(1, 0, 0));
camera_mat = glm::rotate(camera_mat, (float)glm::radians(yaw), glm::vec3(0, 0, 1));
camera_mat = glm::translate(camera_mat, glm::vec3(-pos.x, -pos.y, -pos.z));
}
glm::mat4 camera::get_matrix()
{
return camera_mat;
}

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

@ -0,0 +1,20 @@
#pragma once
#include <glm/matrix.hpp>
#include "../system.hpp"
namespace sim::graphics::camera
{
glm::mat4 get_matrix();
glm::vec<3, double> get_normal();
glm::vec<3, double> get_pos();
void rotate(double pitch, double yaw);
void move(double x, double y, double z);
void update(double dt);
};

View File

@ -0,0 +1,180 @@
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "focus.hpp"
#include "../../util/math.hpp"
#include "../window.hpp"
#include "../camera.hpp"
#include "../resize.hpp"
#include "mouse.hpp"
#include <glm/gtc/matrix_transform.hpp>
#include <iostream>
#include <vector>
using namespace sim::graphics;
static glm::vec<3, double> trigger_near;
static glm::vec<3, double> trigger_far;
static std::vector<std::unique_ptr<focus::focus_t>> stack;
static std::unique_ptr<focus::focus_t> state = nullptr;
static bool mouse_visible = false;
static bool mouse_locked = false;
static bool triggered = false;
void focus::on_keypress(int key, int sc, int action, int mods)
{
if(key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
{
if(is_mouse_locked())
{
clear_mouse_locked();
}
else
{
mouse_locked = true;
}
}
if(state)
{
state->on_keypress(key, sc, action, mods);
}
}
void focus::on_mouse_button(int button, int action, int mods)
{
if(state)
{
state->on_mouse_button(button, action, mods);
}
if(button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS)
{
if(is_mouse_locked() && mouse_visible)
{
double mx, my;
mouse::get(mx, my);
glm::vec2 wsize = resize::get_size();
glm::vec4 viewport = glm::vec4(0, 0, wsize);
glm::vec2 mouse(mx, wsize.y - my);
trigger_near = glm::unProject(glm::vec3(mouse, -1), camera::get_matrix(), window::projection_matrix, viewport);
trigger_far = glm::unProject(glm::vec3(mouse, 1), camera::get_matrix(), window::projection_matrix, viewport);
triggered = true;
}
else if(!mouse_visible)
{
trigger_near = camera::get_pos();
trigger_far = trigger_near + camera::get_normal();
triggered = true;
}
}
}
void focus::on_cursor_pos(double x, double y)
{
if(state)
{
state->on_cursor_pos(x, y);
}
}
void focus::on_charcode(unsigned int c)
{
if(state)
{
state->on_charcode(c);
}
}
glm::vec<3, double> focus::get_trigger_near()
{
return trigger_near;
}
glm::vec<3, double> focus::get_trigger_far()
{
return trigger_far;
}
void focus::update(double dt)
{
triggered = false;
bool c = is_mouse_locked();
if(state && !state->cursor_is_visible())
{
c = false;
}
if(c != mouse_visible)
{
if(c)
{
mouse::show_cursor();
}
else
{
mouse::hide_cursor();
}
mouse_visible = c;
}
if(state)
{
state->update(dt);
}
}
bool focus::is_focused()
{
return (state != nullptr);
}
void focus::clear_focus()
{
state = nullptr;
if(stack.size() != 0)
{
state = std::move(stack.back());
stack.pop_back();
}
}
bool focus::is_mouse_locked()
{
return is_focused() || mouse_locked;
}
void focus::clear_mouse_locked()
{
mouse_locked = false;
clear_focus();
}
void focus::set(std::unique_ptr<focus_t> f)
{
if(state != nullptr)
{
stack.push_back(std::move(state));
}
state = std::move(f);
}
bool focus::is_triggered()
{
return triggered;
}

View File

@ -0,0 +1,37 @@
#pragma once
#include <glm/matrix.hpp>
#include <memory>
namespace sim::graphics::focus
{
struct focus_t
{
virtual ~focus_t() { }
virtual bool cursor_is_visible() { return true; }
virtual void on_keypress(int key, int sc, int action, int mods) { }
virtual void on_mouse_button(int button, int action, int mods) { }
virtual void on_cursor_pos(double x, double y) { }
virtual void on_charcode(unsigned int c) { }
virtual void update(double dt) { }
};
bool is_focused();
void clear_focus();
bool is_triggered();
bool is_mouse_locked();
void clear_mouse_locked();
glm::vec<3, double> get_trigger_near();
glm::vec<3, double> get_trigger_far();
void set(std::unique_ptr<focus_t> f);
void on_keypress(int key, int sc, int action, int mods);
void on_mouse_button(int button, int action, int mods);
void on_cursor_pos(double x, double y);
void on_charcode(unsigned int c);
void update(double dt);
};

View File

@ -0,0 +1,88 @@
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <unordered_map>
#include "focus.hpp"
#include "keyboard.hpp"
#include "../window.hpp"
#include "../resize.hpp"
#include "../camera.hpp"
#include "../../system.hpp"
using namespace sim::graphics;
static std::unordered_map<int, bool> pressed;
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();
}
if(action == GLFW_PRESS)
{
pressed[key] = true;
switch(key)
{
case GLFW_KEY_1:
sim::system::active.speed = 1;
break;
case GLFW_KEY_2:
sim::system::active.speed = 10;
break;
case GLFW_KEY_3:
sim::system::active.speed = 60;
break;
case GLFW_KEY_4:
sim::system::active.speed = 600;
break;
case GLFW_KEY_5:
sim::system::active.speed = 3600;
break;
}
}
if(action == GLFW_RELEASE)
{
pressed[key] = false;
}
focus::on_keypress(key, sc, action, mods);
}
static void cb_charcode(GLFWwindow* win, unsigned int code)
{
focus::on_charcode(code);
}
bool keyboard::is_pressed(int key)
{
if(focus::is_mouse_locked())
{
return false;
}
auto it = pressed.find(key);
if(it == pressed.end())
{
return false;
}
else
{
return it->second;
}
}
void keyboard::init()
{
GLFWwindow* win = window::get_window();
glfwSetKeyCallback(win, cb_keypress);
glfwSetCharCallback(win, cb_charcode);
}

View File

@ -0,0 +1,11 @@
#pragma once
namespace sim::graphics::keyboard
{
void init();
bool is_pressed(int key);
};

View File

@ -0,0 +1,72 @@
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "focus.hpp"
#include "mouse.hpp"
#include "../window.hpp"
#include "../camera.hpp"
using namespace sim::graphics;
static double xpos = 0, ypos = 0;
static void cb_cursor_pos(GLFWwindow* win, double x, double y)
{
if(focus::is_mouse_locked())
{
focus::on_cursor_pos(x - xpos, y - ypos);
}
else
{
camera::rotate(x - xpos, y - ypos);
}
xpos = x;
ypos = y;
}
void cb_mouse_button(GLFWwindow* window, int button, int action, int mods)
{
focus::on_mouse_button(button, action, mods);
}
void mouse::get(double& x, double& y)
{
x = xpos;
y = ypos;
}
glm::vec2 mouse::get()
{
return {xpos, ypos};
}
void mouse::show_cursor()
{
double x, y;
GLFWwindow* win = window::get_window();
glfwSetInputMode(win, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
glfwGetCursorPos(win, &x, &y);
cb_cursor_pos(win, x, y);
}
void mouse::hide_cursor()
{
double x, y;
GLFWwindow* win = window::get_window();
glfwSetInputMode(win, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
glfwGetCursorPos(win, &x, &y);
cb_cursor_pos(win, x, y);
}
void mouse::init()
{
GLFWwindow* win = window::get_window();
glfwSetCursorPosCallback(win, cb_cursor_pos);
glfwSetMouseButtonCallback(win, cb_mouse_button);
glfwSetInputMode(win, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
glfwSetCursorPos(win, 0, 0);
}

View File

@ -0,0 +1,16 @@
#pragma once
#include <glm/vec2.hpp>
namespace sim::graphics::mouse
{
void init();
glm::vec2 get();
void get(double& x, double& y);
void show_cursor();
void hide_cursor();
};

View File

@ -0,0 +1,30 @@
#include "locations.hpp"
#include <glm/ext/matrix_transform.hpp>
using namespace sim::graphics;
const glm::mat4 locations::monitors[4] = {
(
glm::translate(glm::mat4(1), glm::vec3(-2.949, -1.7778 + 0.05, 3 - 0.05)) *
glm::rotate(glm::mat4(1), glm::radians<float>(-90), glm::vec3(1, 0, 0)) *
glm::rotate(glm::mat4(1), glm::radians<float>(-90), glm::vec3(0, 1, 0)) *
glm::scale(glm::mat4(1), glm::vec3(1.9, 1.9, 1.9))
),
(
glm::translate(glm::mat4(1), glm::vec3(-1.5 + 0.05, 3.949, 3 - 0.05)) *
glm::rotate(glm::mat4(1), glm::radians<float>(-90), glm::vec3(1, 0, 0)) *
glm::scale(glm::mat4(1), glm::vec3(1.9, 1.9, 1.9))
),
(
glm::translate(glm::mat4(1), glm::vec3(1 + 0.05, 3.949, 3 - 0.05)) *
glm::rotate(glm::mat4(1), glm::radians<float>(-90), glm::vec3(1, 0, 0)) *
glm::scale(glm::mat4(1), glm::vec3(1.9, 1.9, 1.9))
),
(
glm::translate(glm::mat4(1), glm::vec3(3.5 + 0.05, 3.949, 3 - 0.05)) *
glm::rotate(glm::mat4(1), glm::radians<float>(-90), glm::vec3(1, 0, 0)) *
glm::scale(glm::mat4(1), glm::vec3(1.9, 1.9, 1.9))
)
};

View File

@ -0,0 +1,12 @@
#pragma once
#include <glm/matrix.hpp>
namespace sim::graphics::locations
{
extern const glm::mat4 monitors[4];
};

View File

@ -0,0 +1,46 @@
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <assimp/Importer.hpp>
#include "../shader.hpp"
#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);
}
void arrays::vertex_attrib_pointers()
{
vertex v;
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, 4, GL_FLOAT, false, sizeof(v), ptr_diff(&v.pos, &v));
glEnableVertexAttribArray(2);
glVertexAttribPointer(3, 3, GL_FLOAT, false, sizeof(v), ptr_diff(&v.normal, &v));
glEnableVertexAttribArray(3);
}
glm::mat4 arrays::colour(glm::vec4 c)
{
return glm::mat4({
c.r, c.g, c.b, c.a,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0
});
}

View File

@ -0,0 +1,21 @@
#pragma once
#include <glm/matrix.hpp>
namespace sim::graphics::arrays
{
struct vertex
{
unsigned long texid = 0;
glm::vec2 texpos = {0, 0};
glm::vec4 pos = {0, 0, 0, 1};
glm::vec3 normal = {0, 0, 0};
};
void vertex_attrib_pointers();
glm::mat4 colour(glm::vec4 code);
};

145
src/graphics/mesh/font.cpp Normal file
View File

@ -0,0 +1,145 @@
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include <glm/vec2.hpp>
#include <iostream>
#include <vector>
#include "mesh.hpp"
#include "arrays.hpp"
#include "font.hpp"
using namespace sim::graphics;
struct character
{
unsigned long handle;
float advance;
glm::vec2 size;
glm::vec2 bearing;
};
static character 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, "../assets/font/DroidSans.ttf", 0, &face))
{
std::cout << "Error: failed to load freetype font\n";
return;
}
int size = 256;
float m = 1.0f / size;
FT_Set_Pixel_Sizes(face, 0, size);
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";
}
int width = face->glyph->bitmap.width;
int height = face->glyph->bitmap.rows;
int offx = face->glyph->bitmap_left;
int offy = face->glyph->bitmap_top;
character& c = chars[i];
c.advance = face->glyph->advance.x * m / 64.0;
c.size = {width * m, height * m};
c.bearing = {offx * m, offy * m};
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, width, height);
glTextureSubImage2D(texids[i], 0, 0, 0, width, height, 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;
}
FT_Done_FreeType(ft);
}
void mesh::load_text(const char* text, double size)
{
std::vector<arrays::vertex> vertices;
std::vector<unsigned int> indices;
float x = 0, y = size;
unsigned int at = 0;
for(unsigned int i = 0; text[i] != '\0'; i++)
{
char c = text[i];
character ch = chars[c];
if(c == '\n')
{
x = 0;
y += size;
continue;
}
if(ch.handle == 0)
{
x += ch.advance * size;
continue;
}
unsigned int index[6] = {
at, at + 1, at + 3,
at, at + 3, at + 2
};
float sx = x + ch.bearing.x * size;
float sy = y - ch.bearing.y * size;
float ex = sx + ch.size.x * size;
float ey = sy + ch.size.y * size;
vertices.push_back(arrays::vertex(ch.handle, {0, 0}, {sx, sy, 0, 1}, {0, 0, -1}));
vertices.push_back(arrays::vertex(ch.handle, {0, 1}, {sx, ey, 0, 1}, {0, 0, -1}));
vertices.push_back(arrays::vertex(ch.handle, {1, 0}, {ex, sy, 0, 1}, {0, 0, -1}));
vertices.push_back(arrays::vertex(ch.handle, {1, 1}, {ex, ey, 0, 1}, {0, 0, -1}));
indices.insert(indices.end(), &index[0], &index[6]);
at += 4;
x += ch.advance * size;
}
set_vertices(&vertices[0], vertices.size());
set_indices(&indices[0], indices.size());
}

View File

@ -0,0 +1,15 @@
#pragma once
#include "mesh.hpp"
#include <string>
#include <sstream>
namespace sim::graphics::font
{
void init();
};

View File

@ -0,0 +1,82 @@
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "glmesh.hpp"
#include "arrays.hpp"
#include "../shader.hpp"
#include "../camera.hpp"
using namespace sim::graphics;
constexpr static void init(glmesh* m)
{
if(m->vao != 0)
{
return;
}
glGenVertexArrays(1, &m->vao);
glGenBuffers(1, &m->vbo);
glGenBuffers(1, &m->ebo);
glBindVertexArray(m->vao);
glBindBuffer(GL_ARRAY_BUFFER, m->vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m->ebo);
arrays::vertex_attrib_pointers();
}
glmesh::glmesh(glmesh&& o)
{
vbo = o.vbo;
ebo = o.ebo;
vao = o.vao;
size = o.size;
colour_matrix = o.colour_matrix;
model_matrix = o.model_matrix;
o.vbo = 0;
o.ebo = 0;
o.vao = 0;
}
glmesh::~glmesh()
{
if(vbo) glDeleteBuffers(1, &vbo);
if(ebo) glDeleteBuffers(1, &ebo);
if(vao) glDeleteVertexArrays(1, &vao);
}
void glmesh::bind()
{
init(this);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
}
void glmesh::uniform()
{
glUniformMatrix4fv(shader::gl_model, 1, false, &model_matrix[0][0]);
glUniformMatrix4fv(shader::gl_tex_mat, 1, false, &colour_matrix[0][0]);
}
void glmesh::set(const mesh& m, int mode)
{
glBufferData(GL_ARRAY_BUFFER, m.vertices.size() * sizeof(m.vertices[0]), &m.vertices[0], mode);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, m.indices.size() * sizeof(m.indices[0]), &m.indices[0], mode);
this->size = m.indices.size();
}
void glmesh::render()
{
render(GL_TRIANGLES);
}
void glmesh::render(int type)
{
glDrawElements(type, size, GL_UNSIGNED_INT, 0);
}

View File

@ -0,0 +1,35 @@
#pragma once
#include <string>
#include "arrays.hpp"
#include "mesh.hpp"
#include <glm/matrix.hpp>
namespace sim::graphics
{
struct glmesh
{
unsigned int vao = 0, vbo = 0, ebo = 0, size = 0;
glm::mat4 model_matrix {1.0f};
glm::mat4 colour_matrix {1.0f};
constexpr glmesh() { }
glmesh(glmesh&& o);
glmesh(const glmesh& o) = delete;
~glmesh();
void bind();
void uniform();
void set(const mesh& m, int mode);
void render(int type);
void render();
};
};

257
src/graphics/mesh/mesh.cpp Normal file
View File

@ -0,0 +1,257 @@
#include "mesh.hpp"
#include "arrays.hpp"
#include "../shader.hpp"
#include "../camera.hpp"
#include "../input/focus.hpp"
#include "../../util/math.hpp"
#include <iostream>
using namespace sim::graphics;
void mesh::add(const mesh& o, glm::mat4 mat)
{
unsigned int off = vertices.size();
glm::mat3 mat3(mat);
vertices.reserve(vertices.size() + o.vertices.size());
indices.reserve(indices.size() + o.indices.size());
for(unsigned int i = 0; i < o.vertices.size(); i++)
{
arrays::vertex v = o.vertices[i];
v.normal = v.normal * mat3;
v.pos = v.pos * mat;
vertices.push_back(v);
}
for(unsigned int i = 0; i < o.indices.size(); i++)
{
indices.push_back(o.indices[i] + off);
}
}
void mesh::set_vertices(const arrays::vertex* data, size_t size)
{
vertices.clear();
vertices.reserve(size);
for(unsigned int i = 0; i < size; i++)
{
vertices.push_back(data[i]);
}
}
void mesh::set_indices(const unsigned int* data, size_t size)
{
indices.clear();
indices.reserve(size);
for(unsigned int i = 0; i < size; i++)
{
indices.push_back(data[i]);
}
}
typedef glm::vec<3, double> vec3;
bool ray_intersects_triangle(vec3 ray_origin,
vec3 ray_vector,
const vec3* triangle,
vec3& out_intersection_point)
{
constexpr double epsilon = std::numeric_limits<double>::epsilon();
vec3 edge1 = triangle[1] - triangle[0];
vec3 edge2 = triangle[2] - triangle[0];
vec3 ray_cross_e2 = cross(ray_vector, edge2);
double det = dot(edge1, ray_cross_e2);
if (det > -epsilon && det < epsilon)
return false; // This ray is parallel to this triangle.
double inv_det = 1.0 / det;
vec3 s = ray_origin - triangle[0];
double u = inv_det * dot(s, ray_cross_e2);
if (u < 0 || u > 1)
return false;
vec3 s_cross_e1 = cross(s, edge1);
double v = inv_det * dot(ray_vector, s_cross_e1);
if (v < 0 || u + v > 1)
return false;
// At this stage we can compute t to find out where the intersection point is on the line.
double t = inv_det * dot(edge2, s_cross_e1);
out_intersection_point = ray_origin + ray_vector * t;
if (t > epsilon) // ray intersection
{
return true;
}
else // This means that there is a line intersection but not a ray intersection.
return false;
}
bool mesh::check_focus(double len) const
{
auto near = focus::get_trigger_near();
auto far = focus::get_trigger_far();
return focus::is_triggered() && check_intersect(near, glm::normalize(far - near) * len);
}
bool mesh::check_focus() const
{
return check_focus(2.5);
}
bool mesh::check_intersect(vec3 pos, vec3 path) const
{
double l = glm::length(path);
if(l == 0)
{
return false;
}
vec3 path_n = path / l;
for(unsigned int i = 0; i < indices.size(); i += 3)
{
vec3 v[3] = {
vec3(this->vertices[indices[i]].pos),
vec3(this->vertices[indices[i + 1]].pos),
vec3(this->vertices[indices[i + 2]].pos)
};
vec3 ipoint;
vec3 normal = glm::normalize(glm::cross(v[1] - v[0], v[2] - v[0]));
double d = glm::dot(normal, path);
if(d >= 0)
continue;
if(!ray_intersects_triangle(pos, path_n, v, ipoint))
continue;
if(l < glm::length(ipoint - pos))
continue;
return true;
}
return false;
}
vec3 mesh::calc_intersect(vec3 pos, vec3 path) const
{
vec3 normal_last(0);
return calc_intersect(pos, path, normal_last);
}
static bool calc_intercept_vert(vec3 v[3], vec3 pos, vec3& path, vec3& path_n, vec3& normal_last, double& l)
{
vec3 ipoint;
vec3 normal = glm::normalize(glm::cross(v[1] - v[0], v[2] - v[0]));
double d = glm::dot(normal, path);
if(d >= 0)
return false;
if(!ray_intersects_triangle(pos, path_n, v, ipoint))
return false;
if(l < glm::length(ipoint - pos))
return false;
if(normal_last != vec3(0))
{
vec3 n = glm::cross(normal_last, normal);
if(glm::length(n) > 0)
{
normal = glm::normalize(glm::cross(glm::cross(normal_last, normal), normal_last));
d = glm::dot(normal, path);
}
}
path -= normal * d;
normal_last = normal;
l = glm::length(path);
if(l > 0)
{
path_n = path / l;
}
return true;
}
vec3 mesh::calc_intersect(vec3 pos, vec3 path, vec3& normal_last) const
{
double l = glm::length(path);
if(l == 0)
{
return path;
}
vec3 path_n = path / l;
unsigned int i_found = 0;
for(unsigned int i = 0; i < indices.size(); i += 3)
{
vec3 v[3] = {
vec3(this->vertices[indices[i]].pos),
vec3(this->vertices[indices[i + 1]].pos),
vec3(this->vertices[indices[i + 2]].pos)
};
if(calc_intercept_vert(v, pos, path, path_n, normal_last, l))
{
i_found = i;
}
if(l == 0)
{
return path;
}
}
for(unsigned int i = 0; i < i_found; i += 3)
{
vec3 v[3] = {
vec3(this->vertices[indices[i]].pos),
vec3(this->vertices[indices[i + 1]].pos),
vec3(this->vertices[indices[i + 2]].pos)
};
calc_intercept_vert(v, pos, path, path_n, normal_last, l);
if(l == 0)
{
return path;
}
}
return path;
}
mesh mesh::to_lines() const
{
mesh m;
m.vertices = vertices;
for(int i = 0; i < indices.size(); i += 3)
{
m.indices.push_back(indices[i]);
m.indices.push_back(indices[i + 1]);
m.indices.push_back(indices[i + 1]);
m.indices.push_back(indices[i + 2]);
m.indices.push_back(indices[i]);
m.indices.push_back(indices[i + 2]);
}
return m;
}

View File

@ -0,0 +1,47 @@
#pragma once
#include <string>
#include <sstream>
#include <vector>
#include <glm/matrix.hpp>
#include "arrays.hpp"
namespace sim::graphics
{
struct mesh
{
std::vector<arrays::vertex> vertices;
std::vector<unsigned int> indices;
constexpr mesh() { }
void set_vertices(const arrays::vertex* data, size_t size);
void set_indices(const unsigned int* data, size_t size);
void load_model(std::string base, std::string path);
void load_model(std::string path) { load_model(".", path); }
void load_text(const char* text, double size);
void add(const mesh& o, glm::mat4 mat);
mesh to_lines() const;
bool check_focus() const;
bool check_focus(double len) const;
bool check_intersect(glm::vec<3, double> pos, glm::vec<3, double> path) const;
glm::vec<3, double> calc_intersect(glm::vec<3, double> pos, glm::vec<3, double> path) const;
glm::vec<3, double> calc_intersect(glm::vec<3, double> pos, glm::vec<3, double> path, glm::vec<3, double>& normal_last) const;
template <class T>
void load_text(const char* header, T& item, double size)
{
std::stringstream ss;
ss << header << item;
load_text(ss.str().c_str(), size);
}
};
};

172
src/graphics/mesh/model.cpp Normal file
View File

@ -0,0 +1,172 @@
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#include <glm/matrix.hpp>
#include <unordered_map>
#include <iostream>
#include <vector>
#include "mesh.hpp"
#include "arrays.hpp"
#include "texture.hpp"
using namespace sim::graphics;
struct proc_state
{
unsigned int offset = 0;
std::string base;
std::vector<arrays::vertex> vertices;
std::vector<unsigned int> indices;
std::unordered_map<const aiTexture*, unsigned int> handles;
};
static unsigned int proc_texture(const proc_state& state, aiMaterial* mat, const aiScene* scene)
{
for(int i = 0; i < 0x0d; i++)
{
aiTextureType type = (aiTextureType)i;
if(mat->GetTextureCount(type) == 0)
{
continue;
}
aiString str;
mat->GetTexture(type, 0, &str);
const aiTexture* tex = scene->GetEmbeddedTexture(str.C_Str());
if(tex != nullptr)
{
unsigned int handle = state.handles.find(tex)->second;
std::cout << "Using preloaded texture: " << tex->mFilename.C_Str() << "\n";
return handle;
}
std::string filename(str.C_Str());
std::replace(filename.begin(), filename.end(), '\\', '/');
return texture::load(state.base + "/" + filename);
}
return texture::handle_white;
}
static void proc_mesh(proc_state& state, glm::mat4 mat, aiMesh* mesh, const aiScene* scene)
{
aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];
unsigned int handle = proc_texture(state, material, scene);
unsigned int offset = state.offset;
glm::mat3 mat3(mat);
for(unsigned int i = 0; i < mesh->mNumVertices; i++)
{
arrays::vertex vertex;
auto [x, y, z] = mesh->mVertices[i];
vertex.pos = glm::vec4(x, y, z, 1) * mat;
vertex.texid = handle;
if(mesh->HasNormals())
{
auto [x, y, z] = mesh->mNormals[i];
vertex.normal = glm::vec3(x, y, z) * mat3;
}
if(mesh->mTextureCoords[0])
{
auto [x, y, z] = mesh->mTextureCoords[0][i];
vertex.texpos = {x, y};
}
state.vertices.push_back(vertex);
}
for(unsigned int i = 0; i < mesh->mNumFaces; i++)
{
aiFace face = mesh->mFaces[i];
for(unsigned int j = 0; j < face.mNumIndices; j++)
{
state.indices.push_back(face.mIndices[j] + offset);
}
}
state.offset += mesh->mNumVertices;
}
static void proc_node(proc_state& state, glm::mat4 mat, aiNode* node, const aiScene* scene)
{
auto m = node->mTransformation;
mat = glm::mat4(
m.a1, m.a2, m.a3, m.a4,
m.b1, m.b2, m.b3, m.b4,
m.c1, m.c2, m.c3, m.c4,
m.d1, m.d2, m.d3, m.d4
) * mat;
for(size_t i = 0; i < node->mNumMeshes; i++)
{
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
proc_mesh(state, mat, mesh, scene);
}
for(size_t i = 0; i < node->mNumChildren; i++)
{
proc_node(state, mat, node->mChildren[i], scene);
}
}
static unsigned int proc_embedded_texture(aiTexture* tex)
{
std::cout << "Loading embedded data: " << tex->mFilename.C_Str() << "\n";
if(tex->mHeight == 0)
{
return texture::load_mem((unsigned char*)tex->pcData, tex->mWidth);
}
// swizzle each pixel to get RGBA
for(int i = 0; i < tex->mWidth * tex->mHeight; i++)
{
aiTexel t = tex->pcData[i];
tex->pcData[i] = {t.r, t.g, t.b, t.a};
}
return texture::load_mem((unsigned char*)tex->pcData, tex->mWidth, tex->mHeight, 4);
}
void mesh::load_model(std::string base, std::string filename)
{
proc_state state {.base = base};
std::string path = base + "/" + filename;
Assimp::Importer importer;
const aiScene *scene = importer.ReadFile(path.c_str(), aiProcess_Triangulate | aiProcess_FlipUVs);
if(scene == nullptr)
{
std::cerr << "AssImp: Error loading model\n";
return;
}
for(int i = 0; i < scene->mNumTextures; i++)
{
aiTexture* tex = scene->mTextures[i];
unsigned int handle = proc_embedded_texture(tex);
state.handles[tex] = handle;
}
proc_node(state, glm::mat4(1), scene->mRootNode, scene);
set_vertices(&state.vertices[0], state.vertices.size());
set_indices(&state.indices[0], state.indices.size());
}

View File

@ -0,0 +1,100 @@
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <stb/stb_image.h>
#include <unordered_map>
#include <iostream>
#include "texture.hpp"
using namespace sim::graphics;
static std::unordered_map<std::string, unsigned int> loaded;
unsigned int texture::handle_white;
void texture::init()
{
unsigned char pixels[] = {255, 255, 255, 255};
handle_white = load_mem(pixels, 1, 1, 4);
}
unsigned int texture::load_mem(const unsigned char* data, int width, int height, int channels)
{
if(!data)
{
return 0;
}
GLenum format, format_in;
switch(channels)
{
case 1:
format = GL_RED;
format_in = GL_R8;
break;
case 2:
format = GL_RG;
format_in = GL_RG8;
break;
case 3:
format = GL_RGB;
format_in = GL_RGB8;
break;
case 4:
format = GL_RGBA;
format_in = GL_RGBA8;
break;
}
unsigned int texid;
glCreateTextures(GL_TEXTURE_2D, 1, &texid);
glTextureStorage2D(texid, 1, format_in, width, height);
glTextureSubImage2D(texid, 0, 0, 0, width, height, format, GL_UNSIGNED_BYTE, data);
glTextureParameteri(texid, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTextureParameteri(texid, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTextureParameteri(texid, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTextureParameteri(texid, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenerateTextureMipmap(texid);
unsigned int handle = glGetTextureHandleARB(texid);
glMakeTextureHandleResidentARB(handle);
return handle;
}
unsigned int texture::load_mem(const unsigned char* filedata, size_t len)
{
int width, height, channels;
unsigned char* data = stbi_load_from_memory(filedata, len, &width, &height, &channels, 0);
unsigned int handle = load_mem(data, width, height, channels);
stbi_image_free(data);
return handle;
}
unsigned int texture::load(std::string path)
{
const auto it = loaded.find(path);
if(it != loaded.end())
{
return it->second;
}
int width, height, channels;
unsigned char* data = stbi_load(path.c_str(), &width, &height, &channels, 0);
unsigned int handle = load_mem(data, width, height, channels);
stbi_image_free(data);
if(handle == 0)
{
throw std::runtime_error("Failed to load path: " + path);
}
std::cout << "Loaded Image: " << path << "\n";
loaded[path] = handle;
return handle;
}

View File

@ -0,0 +1,17 @@
#pragma once
#include <string>
namespace sim::graphics::texture
{
extern unsigned int handle_white;
void init();
unsigned int load(std::string path);
unsigned int load_mem(const unsigned char* data, int width, int height, int channels);
unsigned int load_mem(const unsigned char* data, size_t len);
};

View File

@ -0,0 +1,219 @@
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "core.hpp"
#include "helpers.hpp"
#include "../locations.hpp"
#include "../input/focus.hpp"
#include "../mesh/arrays.hpp"
#include "../../system.hpp"
#include <glm/ext/matrix_transform.hpp>
#include <iostream>
#include <sstream>
using namespace sim::graphics;
using namespace sim::graphics::monitor;
static void set_all(bool state)
{
for(int i = 0; i < sim::system::active.reactor->rods.size(); i++)
{
sim::reactor::rod* r = sim::system::active.reactor->rods[i].get();
if(r->should_select())
{
r->selected = state;
}
}
}
struct core_monitor : public focus::focus_t
{
virtual void on_keypress(int key, int sc, int action, int mods)
{
if(action != GLFW_PRESS)
{
return;
}
sim::system& sys = sim::system::active;
switch(key)
{
case GLFW_KEY_KP_7:
set_all(true);
break;
case GLFW_KEY_KP_8:
sys.reactor->move_cursor(-sys.reactor->height);
break;
case GLFW_KEY_KP_9:
set_all(false);
break;
case GLFW_KEY_KP_4:
sys.reactor->move_cursor(-1);
break;
case GLFW_KEY_KP_5:
sys.reactor->toggle_selected();
break;
case GLFW_KEY_KP_6:
sys.reactor->move_cursor(1);
break;
case GLFW_KEY_KP_1:
sys.reactor->reset_rod_speed();
break;
case GLFW_KEY_KP_2:
sys.reactor->move_cursor(sys.reactor->height);
break;
}
}
};
struct core_joystick : public focus::focus_t
{
virtual void on_cursor_pos(double x, double y)
{
sim::system::active.reactor->add_rod_speed(y * 1e-6);
}
virtual ~core_joystick()
{
sim::system::active.reactor->reset_rod_speed();
}
virtual void on_mouse_button(int button, int action, int mods)
{
if(button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_RELEASE)
{
focus::clear_focus();
}
}
virtual bool cursor_is_visible()
{
return false;
}
};
core::core()
{
}
void core::init()
{
mesh1.model_matrix = locations::monitors[2];
mesh1.colour_matrix = arrays::colour({1, 1, 1, 1});
sim::graphics::mesh rmesh1, rmesh2;
rmesh1.load_text("Reactor Core", 0.04);
rmesh2.load_model("../assets/model/", "reactor_core_interface_circle.stl");
rmesh1.add(rmesh2, glm::mat4(1));
mesh1.bind();
mesh1.set(rmesh1, GL_STATIC_DRAW);
rmesh2.load_model("../assets/model/", "reactor_core_interface_cell.stl");
mesh2.bind();
mesh2.set(rmesh2, GL_STATIC_DRAW);
m_buttons[0].load_model("../assets/model/", "reactor_core_button1.stl");
m_buttons[1].load_model("../assets/model/", "reactor_core_button2.stl");
m_buttons[2].load_model("../assets/model/", "reactor_core_button3.stl");
m_buttons[3].load_model("../assets/model/", "reactor_core_button4.stl");
m_buttons[4].load_model("../assets/model/", "reactor_core_button5.stl");
m_buttons[5].load_model("../assets/model/", "reactor_core_button6.stl");
m_buttons[6].load_model("../assets/model/", "reactor_core_button7.stl");
m_buttons[7].load_model("../assets/model/", "reactor_core_button8.stl");
m_buttons[8].load_model("../assets/model/", "reactor_core_button9.stl");
m_joystick.load_model("../assets/model/", "reactor_core_joystick.stl");
m_monitor.load_model("../assets/model/", "reactor_core_input.stl");
m_scram.load_model("../assets/model/", "reactor_core_scram.stl");
}
void core::update()
{
sim::system& sys = sim::system::active;
if(m_monitor.check_focus())
focus::set(std::make_unique<core_monitor>());
if(m_joystick.check_focus())
focus::set(std::make_unique<core_joystick>());
if(m_scram.check_focus())
sys.reactor->scram();
if(m_buttons[0].check_focus())
set_all(true);
if(m_buttons[1].check_focus())
sys.reactor->move_cursor(-sys.reactor->height);
if(m_buttons[2].check_focus())
set_all(false);
if(m_buttons[3].check_focus())
sys.reactor->move_cursor(-1);
if(m_buttons[4].check_focus())
sys.reactor->toggle_selected();
if(m_buttons[5].check_focus())
sys.reactor->move_cursor(1);
if(m_buttons[6].check_focus())
sys.reactor->reset_rod_speed();
if(m_buttons[7].check_focus())
sys.reactor->move_cursor(sys.reactor->height);
}
void core::render()
{
sim::system& sys = sim::system::active;
double step = 1 / (sys.vessel->diameter / sys.reactor->cell_width * 0.8);
double sx = 0.5 - (sys.reactor->width - 1) * step / 2.0;
double sy = 0.5 - (sys.reactor->height - 1) * step / 2.0;
glm::mat4 mat_scale = glm::scale(glm::mat4(1), glm::vec3(step * 0.4, step * 0.4, 1));
glm::mat4 mat_select = glm::translate(glm::mat4(1), glm::vec3(-0.8, -0.8, -0.001)) * glm::scale(glm::mat4(1), glm::vec3(0.5, 0.5, 1));
glm::mat4 mat_cursor = glm::translate(glm::mat4(1), glm::vec3(-0.8, 0.8, -0.001)) * glm::scale(glm::mat4(1), glm::vec3(0.5, 0.5, 1));
mesh1.bind();
mesh1.uniform();
mesh1.render();
mesh2.bind();
for(int i = 0; i < sys.reactor->size; i++)
{
int x = i % sys.reactor->width;
int y = i / sys.reactor->width;
double ox = sx + x * step;
double oy = sy + y * step;
reactor::rod* r = sys.reactor->rods[i].get();
glm::vec4 colour = r->get_colour();
if(colour[3] == 0)
{
continue;
}
glm::mat4 mat = mesh1.model_matrix * glm::translate(glm::mat4(1), glm::vec3(ox, oy, 0)) * mat_scale;
mesh2.model_matrix = mat;
mesh2.colour_matrix = arrays::colour(colour);
mesh2.uniform();
mesh2.render();
if(sys.reactor->cursor == i)
{
mesh2.model_matrix = mat * mat_cursor;
mesh2.colour_matrix = arrays::colour({1, 0, 0, 1});
mesh2.uniform();
mesh2.render();
}
if(r->selected)
{
mesh2.model_matrix = mat * mat_select;
mesh2.colour_matrix = arrays::colour({1, 1, 0, 1});
mesh2.uniform();
mesh2.render();
}
}
}

View File

@ -0,0 +1,27 @@
#pragma once
#include "../mesh/glmesh.hpp"
namespace sim::graphics::monitor
{
class core
{
sim::graphics::glmesh mesh1, mesh2;
sim::graphics::mesh m_monitor;
sim::graphics::mesh m_buttons[9];
sim::graphics::mesh m_joystick;
sim::graphics::mesh m_scram;
public:
core();
void init();
void update();
void render();
};
};

View File

@ -0,0 +1,15 @@
#pragma once
#include <cmath>
constexpr double show(double v, double m)
{
return std::round(v * m) / m;
}
constexpr double show(double a)
{
double b = std::round(a * 1e3) * 1e-3;
return b == 0 ? 0 : b;
}

View File

@ -0,0 +1,163 @@
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "helpers.hpp"
#include "primary_loop.hpp"
#include "../locations.hpp"
#include "../../system.hpp"
#include "../../coolant/valve.hpp"
#include "../input/focus.hpp"
#include <glm/ext/matrix_transform.hpp>
#include <iostream>
using namespace sim::graphics;
using namespace sim::graphics::monitor;
struct valve_joystick : public focus::focus_t
{
sim::coolant::valve* active;
valve_joystick(sim::coolant::valve* v) : active(v)
{
}
virtual ~valve_joystick()
{
active->clear_open_speed();
}
virtual void on_cursor_pos(double x, double y)
{
active->add_open_speed(-y * 1e-6);
}
virtual void on_mouse_button(int button, int action, int mods)
{
if(button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_RELEASE)
{
focus::clear_focus();
}
}
virtual bool cursor_is_visible()
{
return false;
}
};
primary_loop::primary_loop()
{
}
void primary_loop::toggle_primary_pump()
{
system& sys = sim::system::active;
bool state;
sys.primary_pump->powered = state = !sys.primary_pump->powered;
gm_switch_primary.model_matrix = glm::translate(glm::mat4(1), glm::vec3(0, state ? 0.07 : 0, 0));
}
void primary_loop::init()
{
mesh1.model_matrix = locations::monitors[3];
mesh2.model_matrix = glm::translate(mesh1.model_matrix, glm::vec3(0.5, 0, 0));
mesh1.colour_matrix = mesh2.colour_matrix = {
1, 1, 1, 1,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0
};
std::stringstream ss;
sim::graphics::mesh rmesh;
ss << "Turbine Bypass Valve\n\n";
ss << "Opened\nFlow\n\n";
ss << "Turbine Inlet Valve\n\n";
ss << "Opened\nFlow\n\n";
ss << "Primary Pump\n\n";
ss << "Power\nSpeed\nFlow\n\n";
ss << "Condenser\n\n";
ss << "Heat\n";
ss << "Steam\n";
ss << "Pressure\n";
ss << "Level\n\n";
rmesh.load_text(ss.str().c_str(), 0.04);
mesh1.bind();
mesh1.set(rmesh, GL_STATIC_DRAW);
rmesh.load_model("../assets/model", "primary_coolant_pump_switch.glb");
gm_switch_primary.bind();
gm_switch_primary.set(rmesh, GL_STATIC_DRAW);
rmesh.load_model("../assets/model", "secondary_coolant_pump_switch.glb");
gm_switch_secondary.bind();
gm_switch_secondary.set(rmesh, GL_STATIC_DRAW);
m_joystick_turbine_bypass.load_model("../assets/model", "turbine_valve_bypass_joystick.stl");
m_joystick_turbine_inlet.load_model("../assets/model", "turbine_valve_inlet_joystick.stl");
m_switch_primary.load_model("../assets/model", "primary_coolant_pump_switch.stl");
m_switch_secondary.load_model("../assets/model", "secondary_coolant_pump_switch.stl");
}
void primary_loop::update()
{
std::stringstream ss;
sim::graphics::mesh rmesh;
system& sys = sim::system::active;
ss << "\n\n";
ss << show( sys.turbine_bypass_valve->get_state() * 100 ) << " %\n";
ss << show( sys.turbine_bypass_valve->get_flow() / 1000 ) << " kg/s\n";
ss << "\n\n\n";
ss << show( sys.turbine_inlet_valve->get_state() * 100 ) << " %\n";
ss << show( sys.turbine_inlet_valve->get_flow() / 1000 ) << " kg/s\n";
ss << "\n\n\n";
ss << sys.primary_pump->get_state_string() << "\n";
ss << show( sys.primary_pump->get_rpm() ) << " r/min\n";
ss << show( sys.primary_pump->get_flow_mass() / 1000 ) << " kg/s\n";
ss << "\n\n\n";
ss << show( sys.condenser->get_heat() ) << " C\n";
ss << show( sys.condenser->get_steam() ) << " g\n";
ss << show( sys.condenser->get_pressure() / 1000 ) << " kPa\n";
ss << show( sys.condenser->get_level() ) << " / " << show( sys.condenser->get_volume() ) << " L\n";
rmesh.load_text(ss.str().c_str(), 0.04);
mesh2.bind();
mesh2.set(rmesh, GL_DYNAMIC_DRAW);
if(m_joystick_turbine_bypass.check_focus())
focus::set(std::make_unique<valve_joystick>(sys.turbine_bypass_valve.get()));
if(m_joystick_turbine_inlet.check_focus())
focus::set(std::make_unique<valve_joystick>(sys.turbine_inlet_valve.get()));
if(m_switch_primary.check_focus())
toggle_primary_pump();
}
void primary_loop::render()
{
mesh1.bind();
mesh1.uniform();
mesh1.render();
mesh2.bind();
mesh2.uniform();
mesh2.render();
gm_switch_primary.bind();
gm_switch_primary.uniform();
gm_switch_primary.render();
gm_switch_secondary.bind();
gm_switch_secondary.uniform();
gm_switch_secondary.render();
}

View File

@ -0,0 +1,32 @@
#pragma once
#include "../mesh/glmesh.hpp"
namespace sim::graphics::monitor
{
class primary_loop
{
sim::graphics::glmesh mesh1, mesh2;
sim::graphics::glmesh gm_switch_primary;
sim::graphics::glmesh gm_switch_secondary;
sim::graphics::mesh m_joystick_turbine_bypass;
sim::graphics::mesh m_joystick_turbine_inlet;
sim::graphics::mesh m_switch_primary;
sim::graphics::mesh m_switch_secondary;
void toggle_primary_pump();
public:
primary_loop();
void init();
void update();
void render();
};
};

View File

@ -0,0 +1,120 @@
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "vessel.hpp"
#include "helpers.hpp"
#include "../../reactor/rod.hpp"
#include "../../reactor/control/boron_rod.hpp"
#include "../locations.hpp"
#include "../../system.hpp"
#include <glm/ext/matrix_transform.hpp>
#include <sstream>
using namespace sim::graphics::monitor;
vessel::vessel()
{
}
void vessel::init()
{
mesh1.model_matrix = locations::monitors[1];
mesh2.model_matrix = glm::translate(mesh1.model_matrix, glm::vec3(0.5, 0, 0));
mesh1.colour_matrix = mesh2.colour_matrix = {
1, 1, 1, 1,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0
};
std::stringstream ss;
sim::graphics::mesh rmesh;
ss << "Reactor Vessel\n\n";
ss << "Heat\n";
ss << "Steam\n";
ss << "Pressure\n";
ss << "Level\n";
ss << "Void Ratio\n\n";
ss << "Reactor Core\n\n";
ss << "Energy Output\n";
ss << "Neutron Flux\n\n";
ss << "Temperature\nMin\nMax\n\n";
ss << "Control Rods\nMin\nMax\nSpeed\n";
rmesh.load_text(ss.str().c_str(), 0.04);
mesh1.bind();
mesh1.set(rmesh, GL_STATIC_DRAW);
}
void vessel::update()
{
sim::system& sys = sim::system::active;
std::stringstream ss;
sim::graphics::mesh rmesh;
double temp_min, temp_max;
double crod_min = INFINITY, crod_max = -INFINITY;
sys.reactor->get_stats(sim::reactor::rod::val_t::HEAT, temp_min, temp_max);
for(int i = 0; i < sys.reactor->size; i++)
{
sim::reactor::rod* r = sys.reactor->rods[i].get();
if(r->get_id() != 5)
{
continue;
}
auto br = (sim::reactor::control::boron_rod*)r;
double v = br->get_inserted();
if(v > crod_max)
{
crod_max = v;
}
if(v < crod_min)
{
crod_min = v;
}
}
ss << "\n\n";
ss << show( sys.vessel->get_heat() ) << " C\n";
ss << show( sys.vessel->get_steam() ) << " g\n";
ss << show( sys.vessel->get_pressure() * 0.001 ) << " kPa\n";
ss << show( sys.vessel->get_level() ) << " / " << show( sys.vessel->get_volume() ) << " L\n";
ss << show( sys.vessel->get_void_ratio() * 100 ) << " %\n\n\n\n";
ss << show( sys.reactor->get_energy_output() * 0.001 ) << " kW\n";
ss << show( sys.reactor->get_flux() ) << " n/cm2/s\n\n\n";
ss << show( temp_min ) << " C\n";
ss << show( temp_max ) << " C\n\n\n";
ss << show( 100 - crod_max * 100 ) << " %\n";
ss << show( 100 - crod_min * 100 ) << " %\n";
ss << show( sys.reactor->rod_speed * 100, 1e6 ) << " %/s";
if(sys.reactor->rod_speed == 0) ss << " (Stopped)";
ss << "\n";
rmesh.load_text(ss.str().c_str(), 0.04);
mesh2.bind();
mesh2.set(rmesh, GL_DYNAMIC_DRAW);
}
void vessel::render()
{
mesh1.bind();
mesh1.uniform();
mesh1.render();
mesh2.bind();
mesh2.uniform();
mesh2.render();
}

View File

@ -0,0 +1,22 @@
#pragma once
#include "../mesh/glmesh.hpp"
namespace sim::graphics::monitor
{
class vessel
{
sim::graphics::glmesh mesh1, mesh2;
public:
vessel();
void init();
void update();
void render();
};
};

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

@ -0,0 +1,65 @@
#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;
glm::vec<2, int> resize::get_size()
{
return {win_w, win_h};
}
float resize::get_aspect()
{
return (float)win_w / (float)win_h;
}
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);
}

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

@ -0,0 +1,15 @@
#pragma once
#include <glm/vec2.hpp>
namespace sim::graphics::resize
{
void init();
void toggle_fullscreen();
glm::vec<2, int> get_size();
float get_aspect();
};

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

@ -0,0 +1,81 @@
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include "shader.hpp"
#include "window.hpp"
using namespace sim::graphics;
static unsigned int prog_id;
int shader::gl_tex_mat;
int shader::gl_model;
int shader::gl_camera;
int shader::gl_projection;
static int load_shader(const char* src, int type)
{
int id = glCreateShader(type);
glShaderSource(id, 1, &src, nullptr);
glCompileShader(id);
return id;
}
static std::string read_shader(const char* path)
{
std::stringstream ss;
std::ifstream file(path, std::ios::binary);
char buff[1024];
while(!file.eof())
{
file.read(buff, 1024);
ss.write(buff, file.gcount());
}
return ss.str();
}
unsigned int shader::init_program()
{
std::string shader_vsh = read_shader("../assets/shader/main.vsh");
std::string shader_fsh = read_shader("../assets/shader/main.fsh");
int success;
int vsh_id = load_shader(shader_vsh.c_str(), GL_VERTEX_SHADER);
int fsh_id = load_shader(shader_fsh.c_str(), 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;
}
gl_tex_mat = glGetUniformLocation(prog_id, "tex_mat");
gl_model = glGetUniformLocation(prog_id, "model");
gl_camera = glGetUniformLocation(prog_id, "camera");
gl_projection = glGetUniformLocation(prog_id, "projection");
glUseProgram(prog_id);
glDeleteShader(vsh_id);
glDeleteShader(fsh_id);
return prog_id;
}

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

@ -0,0 +1,15 @@
#pragma once
namespace sim::graphics::shader
{
extern int gl_tex_mat;
extern int gl_model;
extern int gl_camera;
extern int gl_projection;
unsigned int init_program();
};

67
src/graphics/ui.cpp Normal file
View File

@ -0,0 +1,67 @@
#include "ui.hpp"
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/ext/matrix_transform.hpp>
#include "mesh/mesh.hpp"
#include "mesh/glmesh.hpp"
#include "mesh/arrays.hpp"
#include "mesh/font.hpp"
#include "mesh/texture.hpp"
#include "resize.hpp"
#include "shader.hpp"
#include "widget/clock.hpp"
using namespace sim::graphics;
static glmesh StaticMeshData;
static widget::clock WidgetClock;
void ui::init()
{
mesh m;
unsigned int handle = texture::handle_white;
const unsigned int indices[] = {0, 1, 3, 0, 3, 2};
const arrays::vertex vertices[] = {
arrays::vertex(handle, {0, 0}, {-1, -1, 0, 1}, {0, 0, -1}),
arrays::vertex(handle, {0, 1}, {-1, 1, 0, 1}, {0, 0, -1}),
arrays::vertex(handle, {1, 0}, { 1, -1, 0, 1}, {0, 0, -1}),
arrays::vertex(handle, {1, 1}, { 1, 1, 0, 1}, {0, 0, -1}),
};
m.set_indices(indices, 6);
m.set_vertices(vertices, 4);
StaticMeshData.bind();
StaticMeshData.set(m, GL_STATIC_DRAW);
StaticMeshData.colour_matrix = glm::scale(glm::mat4(1), glm::vec3(1) * 0.75f);
}
void ui::update(double dt)
{
WidgetClock.update(dt);
}
void ui::render()
{
glClear(GL_DEPTH_BUFFER_BIT);
glm::vec2 wsize(resize::get_size() / 2);
glm::mat4 mat_projection = glm::mat4(1);
glm::mat4 mat_camera = glm::scale(glm::mat4(1), glm::vec3(1.0f / wsize * glm::vec2(1, -1), -1));
glUniformMatrix4fv(shader::gl_projection, 1, false, &mat_projection[0][0]);
glUniformMatrix4fv(shader::gl_camera, 1, false, &mat_camera[0][0]);
StaticMeshData.bind();
StaticMeshData.uniform();
StaticMeshData.render();
WidgetClock.render();
}

12
src/graphics/ui.hpp Normal file
View File

@ -0,0 +1,12 @@
#pragma once
namespace sim::graphics::ui
{
void init();
void update(double dt);
void render();
};

View File

@ -0,0 +1,51 @@
#include "clock.hpp"
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/ext/matrix_transform.hpp>
#include <iomanip>
#include <sstream>
#include <cmath>
#include "../mesh/arrays.hpp"
#include "../mesh/font.hpp"
#include "../mesh/arrays.hpp"
#include "../resize.hpp"
#include "../../system.hpp"
using namespace sim::graphics::widget;
void clock::update(double dt)
{
mesh m;
glm::vec2 wsize(resize::get_size() / 2);
std::stringstream ss;
int t_s = std::fmod(at, 60);
int t_m = std::fmod(at / 60, 60);
int t_h = std::fmod(at / 3600, 24);
ss << "Time: " << std::setfill('0') << std::setw(2) << t_h << ":";
ss << std::setfill('0') << std::setw(2) << t_m << ":";
ss << std::setfill('0') << std::setw(2) << t_s << "\n";
ss << "Day: " << std::floor(at / (3600 * 24)) << "\n";
at += dt * sim::system::active.speed;
m.load_text(ss.str().c_str(), 20);
data.bind();
data.model_matrix = glm::translate(glm::mat4(1), glm::vec3(-wsize + glm::vec2(2, 2), 0));
data.colour_matrix = arrays::colour({1, 1, 1, 1});
data.set(m, GL_DYNAMIC_DRAW);
}
void clock::render()
{
data.bind();
data.uniform();
data.render();
}

View File

@ -0,0 +1,19 @@
#pragma once
#include "../mesh/glmesh.hpp"
namespace sim::graphics::widget
{
struct clock
{
double at = 3600 * 12;
glmesh data;
void update(double dt);
void render();
};
};

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

@ -0,0 +1,179 @@
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/matrix.hpp>
#include <glm/ext/matrix_transform.hpp> // glm::translate, glm::rotate, glm::scale
#include <glm/ext/matrix_clip_space.hpp> // glm::perspective
#include <iostream>
#include "mesh/mesh.hpp"
#include "mesh/arrays.hpp"
#include "input/keyboard.hpp"
#include "input/mouse.hpp"
#include "camera.hpp"
#include "resize.hpp"
#include "window.hpp"
#include "shader.hpp"
#include "mesh/font.hpp"
#include "locations.hpp"
#include "monitor/vessel.hpp"
#include "monitor/core.hpp"
#include "monitor/primary_loop.hpp"
#include "mesh/texture.hpp"
#include "ui.hpp"
using namespace sim::graphics;
static GLFWwindow* win;
static bool win_should_close = false;
static glmesh MeshScene;
static monitor::vessel MonitorVessel;
static monitor::core MonitorCore;
static monitor::primary_loop MonitorPrimaryLoop;
glm::mat4 window::projection_matrix;
void GLAPIENTRY cb_debug_message(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam)
{
if(severity != GL_DEBUG_SEVERITY_NOTIFICATION)
{
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_DEBUG_CONTEXT, true);
glfwWindowHint(GLFW_VISIBLE, false);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, true);
#endif
win = glfwCreateWindow(800, 600, "FastNuclearSim", nullptr, nullptr);
glfwMakeContextCurrent(win);
glfwSwapInterval(1);
GLenum err = glewInit();
if(err != GLEW_OK)
{
std::cerr << "GLEW Init Failed: " << glewGetErrorString(err) << "\n";
close();
return;
}
if(!glGetTextureHandleARB || !glMakeTextureHandleResidentARB)
{
std::cerr << "Fatal: Bindless textures not supported\n";
if(!glGetTextureHandleARB)
std::cerr << " Missing: glGetTextureHandleARB\n";
if(!glMakeTextureHandleResidentARB)
std::cerr << " Missing: glMakeTextureHandleResidentARB\n";
close();
return;
}
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDebugMessageCallback(cb_debug_message, nullptr);
keyboard::init();
mouse::init();
resize::init();
texture::init();
font::init();
ui::init();
shader::init_program();
sim::system& sys = sim::system::active;
mesh m;
m.load_model("../assets", "scene-baked.glb");
MeshScene.bind();
MeshScene.set(m, GL_STATIC_DRAW);
sys.scene.load_model("../assets/model", "scene_collisions.stl");
// MeshCollisionScene.bind();
// MeshCollisionScene.set(sys.scene.to_lines(), GL_STATIC_DRAW);
MonitorCore.init();
MonitorVessel.init();
MonitorPrimaryLoop.init();
glfwShowWindow(win);
glViewport(0, 0, 800, 600);
}
void window::update(double dt)
{
glfwPollEvents();
MonitorCore.update();
MonitorVessel.update();
MonitorPrimaryLoop.update();
ui::update(dt);
}
void window::render()
{
glm::mat4 mat_camera = camera::get_matrix();
glm::mat4 mat_projection = glm::perspective(glm::radians(90.0f), resize::get_aspect(), 0.01f, 20.f);
glUniformMatrix4fv(shader::gl_projection, 1, false, &mat_projection[0][0]);
glUniformMatrix4fv(shader::gl_camera, 1, false, &mat_camera[0][0]);
projection_matrix = mat_projection;
glClearColor(0, 0, 0, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
MeshScene.bind();
MeshScene.uniform();
MeshScene.render();
MonitorCore.render();
MonitorVessel.render();
MonitorPrimaryLoop.render();
ui::render();
glfwSwapBuffers(win);
}
bool window::should_close()
{
return win_should_close || glfwWindowShouldClose(win);
}
void window::close()
{
win_should_close = true;
}
void window::destroy()
{
glfwDestroyWindow(win);
glfwTerminate();
}
GLFWwindow* window::get_window()
{
return win;
}

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

@ -0,0 +1,24 @@
#pragma once
#include <GLFW/glfw3.h>
#include <glm/matrix.hpp>
#include "../system.hpp"
namespace sim::graphics::window
{
extern glm::mat4 projection_matrix;
bool should_close();
void create();
void update(double dt);
void render();
void destroy();
void close();
GLFWwindow* get_window();
}

View File

@ -1,23 +1,25 @@
#include "reactor/builder.hpp" #include <sys/time.h>
#include "reactor/control/control_rod.hpp"
#include "reactor/fuel/fuel_rod.hpp" #include <random>
#include "reactor/coolant/pipe.hpp" #include <sstream>
#include "reactor/coolant/heater.hpp" #include <cmath>
#include <cfenv>
#include "reactor/coolant/vessel.hpp" #include "reactor/coolant/vessel.hpp"
#include "coolant/fluid_t.hpp" #include "coolant/fluid_t.hpp"
#include "coolant/valve.hpp" #include "coolant/valve.hpp"
#include "coolant/pump.hpp" #include "coolant/pump.hpp"
#include "display.hpp"
#include <cmath> #include "graphics/mesh/mesh.hpp"
#include <sstream> #include "graphics/input/focus.hpp"
#include <unistd.h>
#include <curses.h>
#include <sys/time.h>
const bool do_graphics = true; #include "graphics/window.hpp"
const bool do_clock = true; #include "graphics/camera.hpp"
#include "system.hpp"
using namespace sim;
unsigned long get_now() unsigned long get_now()
{ {
@ -28,189 +30,27 @@ unsigned long get_now()
int main() int main()
{ {
std::random_device rd; feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW);
std::mt19937 rand(rd());
if(do_graphics) graphics::window::create();
{
initscr();
cbreak();
noecho();
keypad(stdscr, TRUE);
nodelay(stdscr, TRUE);
curs_set(0);
}
sim::reactor::coolant::vessel vessel(8, 10, 300, 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(); long clock = get_now();
double speed = 1;
int framerate = 100;
int steps_extra = 1;
for(;;) while(!graphics::window::should_close())
{ {
std::stringstream ss; long now = get_now();
ss << "Reactor Core\n\n"; long passed = now - clock;
double dt = (double)passed / 1e6;
clock += passed;
{ sim::system::active.update(dt);
long mins = secs / 60;
long hours = mins / 60;
long days = hours / 24;
long years = days / 365;
double s = fmod(secs, 60);
mins %= 60; graphics::camera::update(dt);
hours %= 24; graphics::window::update(dt);
days %= 365; graphics::focus::update(dt);
graphics::window::render();
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(dt);
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; graphics::window::destroy();
} }

47
src/reactor/builder.cpp Normal file
View File

@ -0,0 +1,47 @@
#include "builder.hpp"
#include <cmath>
#include <vector>
#include <cstdlib>
using namespace sim::reactor;
sim::reactor::reactor sim::reactor::builder(const int W, const int H, const double CW, const double CH, fuel::fuel_rod fr, coolant::vessel* v, const char** lines)
{
std::vector<std::unique_ptr<rod>> arr(W * H);
for(int y = 0; y < H; y++)
for(int x = 0; x < W; x++)
{
char c = lines[y][x];
std::unique_ptr<rod> r;
switch(c)
{
case 'F':
r = std::make_unique<fuel::fuel_rod>(fr);
break;
case 'C':
r = std::make_unique<control::boron_rod>(v);
break;
case 'G':
r = std::make_unique<control::graphite_rod>();
break;
case 'H':
r = std::make_unique<coolant::heater>();
break;
case 'P':
r = std::make_unique<coolant::pipe>(v);
break;
default:
r = std::make_unique<rod>();
break;
}
arr[y * W + x] = std::move(r);
}
return reactor(&arr[0], W, H, CW, CH);
}

View File

@ -3,52 +3,17 @@
#include "rod.hpp" #include "rod.hpp"
#include "fuel/fuel_rod.hpp" #include "fuel/fuel_rod.hpp"
#include "control/control_rod.hpp" #include "control/boron_rod.hpp"
#include "control/graphite_rod.hpp" #include "control/graphite_rod.hpp"
#include "coolant/pipe.hpp" #include "coolant/pipe.hpp"
#include "coolant/heater.hpp" #include "coolant/heater.hpp"
#include "coolant/vessel.hpp"
#include "reactor.hpp" #include "reactor.hpp"
namespace sim::reactor namespace sim::reactor
{ {
template <int W, int H> reactor builder(const int W, const int H, const double CW, const double CH, fuel::fuel_rod fr, coolant::vessel* v, const char** lines);
reactor<W, H> builder(fuel::fuel_rod fr, control::control_rod cr, coolant::pipe p, std::array<const char*, H> lines)
{
std::array<rod*, W * H> arr;
for(int y = 0; y < H; y++)
for(int x = 0; x < W; x++)
{
char c = lines[y][x];
rod* r;
switch(c)
{
case 'F':
r = new fuel::fuel_rod(fr);
break;
case 'C':
r = new control::control_rod(cr);
break;
case 'G':
r = new control::graphite_rod();
break;
case 'H':
r = new coolant::heater();
break;
case ' ':
r = new coolant::pipe(p);
break;
case '#':
r = new rod();
}
arr[y * W + x] = r;
}
return reactor<W, H>(arr);
}
}; };

View File

@ -0,0 +1,58 @@
#include "boron_rod.hpp"
#include <cmath>
using namespace sim::reactor::control;
constexpr double boron_density = 2340000; // g/m^3
constexpr double boron_molar_mass = 10; // g/mol
constexpr double boron_molar_density = boron_density / boron_molar_mass; // mol/m^3
boron_rod::boron_rod(coolant::vessel* v) : coolant::pipe(v)
{
}
void boron_rod::display(std::ostream& o) const
{
double limit = get_volume() * boron_molar_density;
o << "Inserted: " << (inserted * 100) << "%\n";
o << "Use: " << (absorbed * limit) << " / " << limit << "\n";
};
void boron_rod::set_reactivity(double a)
{
inserted = 1 - a;
}
glm::vec4 boron_rod::get_colour() const
{
double v = inserted * 0.75 + 0.25;
return {v, v, v, 1};
}
void boron_rod::update(double secs)
{
double limit = get_volume() * boron_molar_density;
update_rod(secs);
double m = (1 - absorbed) * inserted;
double r_fast = vals[val_t::N_FAST] * m;
vals[val_t::N_FAST] -= r_fast;
absorbed += r_fast / limit;
update_pipe(secs);
}
void boron_rod::update_selected(double a)
{
inserted -= a;
if(inserted > 1) inserted = 1;
if(inserted < 0) inserted = 0;
}

View File

@ -6,28 +6,28 @@
namespace sim::reactor::control namespace sim::reactor::control
{ {
class control_rod : public sim::reactor::coolant::pipe class boron_rod : public coolant::pipe
{ {
const double limit;
const double max;
double inserted = 1; double inserted = 1;
double absorbed = 0; double absorbed = 0;
virtual void display(std::ostream& o) const; virtual void display(std::ostream& o) const;
virtual const char* get_name() const { return "Boron Control Rod"; }
virtual const char* get_name() const { return "Control Rod"; } virtual glm::vec4 get_colour() const;
virtual int get_id() const { return 5; }
public: public:
control_rod(coolant::vessel& v, double limit, double max); boron_rod(coolant::vessel* v);
virtual void update(double secs); virtual void update(double secs);
void set_reactivity(double a); void set_reactivity(double a);
double get_inserted() { return inserted; }
virtual bool should_display() const { return true; } virtual bool should_display() const { return true; }
virtual bool should_select() const { return true; } virtual bool should_select() const { return true; }
virtual void update_selected(double a); virtual void update_selected(double a);
virtual std::unique_ptr<rod> clone() const { return std::make_unique<boron_rod>(*this); }
}; };
} }

View File

@ -1,47 +0,0 @@
#include "control_rod.hpp"
#include <cmath>
using namespace sim::reactor::control;
control_rod::control_rod(coolant::vessel& v, double limit, double max) : coolant::pipe(v), limit(limit), max(max)
{
}
void control_rod::display(std::ostream& o) const
{
o << "Inserted: " << (inserted * 100) << "%\n";
o << "Use: " << absorbed << " / " << limit << " mol\n";
};
void control_rod::set_reactivity(double a)
{
inserted = 1 - a;
}
void control_rod::update(double secs)
{
update_rod(secs);
double k = (1 - absorbed / limit) * inserted * max;
double m = 1 - std::pow(0.5, secs * -std::log2(1 - k));
double r_fast = vals[val_t::N_FAST] * m;
double r_slow = vals[val_t::N_SLOW] * m;
vals[val_t::N_FAST] -= r_fast;
vals[val_t::N_SLOW] -= r_slow;
absorbed += r_fast + r_slow;
update_pipe(secs);
}
void control_rod::update_selected(double a)
{
inserted += a;
if(inserted > 1) inserted = 1;
if(inserted < 0) inserted = 0;
}

View File

@ -10,6 +10,12 @@ void graphite_rod::display(std::ostream& o) const
o << "Inserted: " << (inserted * 100) << "%\n"; o << "Inserted: " << (inserted * 100) << "%\n";
}; };
glm::vec4 graphite_rod::get_colour() const
{
double v = inserted * 0.75 + 0.25;
return {v, v, v, 1};
}
double graphite_rod::get_k(val_t type) const double graphite_rod::get_k(val_t type) const
{ {
if(type == val_t::HEAT) return 0.5; if(type == val_t::HEAT) return 0.5;

Some files were not shown because too many files have changed in this diff Show More