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 df98535880
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
124 changed files with 1160450 additions and 738 deletions

4
.gitattributes vendored Normal file
View File

@ -0,0 +1,4 @@
*.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

1
.gitignore vendored
View File

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

View File

@ -2,11 +2,11 @@ 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_STANDARD 26)
set(CMAKE_CXX_FLAGS "-g -O3 -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 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.

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

Binary file not shown.

BIN
assets/model/reactor_core_button1.stl (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/model/reactor_core_button2.stl (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/model/reactor_core_button3.stl (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/model/reactor_core_button4.stl (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/model/reactor_core_button5.stl (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/model/reactor_core_button6.stl (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/model/reactor_core_button7.stl (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/model/reactor_core_button8.stl (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/model/reactor_core_button9.stl (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/model/reactor_core_input.stl (Stored with Git LFS) Normal file

Binary file not shown.

View File

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

BIN
assets/model/reactor_core_interface_cell.stl (Stored with Git LFS) Normal file

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);
}

BIN
assets/model/reactor_core_interface_circle.stl (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/model/reactor_core_joystick.stl (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/model/reactor_core_scram.stl (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/model/scene_collisions.stl (Stored with Git LFS) Normal file

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

BIN
assets/model/turbine_valve_bypass_joystick.stl (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/model/turbine_valve_inlet_joystick.stl (Stored with Git LFS) Normal file

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

File diff suppressed because it is too large Load Diff

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 (Stored with Git LFS) 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.

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 gPmol; // g/mol
const double jPg; // J/g latent heat of vaporisation
const double jPgk; // J/g/K heat capacity
const double bubble_speed; // m/s
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) :
gPl(gPl), gPmol(gPmol),
jPg(jPg), jPgk(jPgk),
constexpr fluid_t(double gPl, double gPmol, double jPg, double bubble_speed, coolant::vapor_pressure vapor_pressure) :
gPl(gPl), gPmol(gPmol), jPg(jPg),
vapor_pressure(vapor_pressure),
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 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
#include <ostream>
#include "fluid_holder.hpp"
namespace sim::coolant
{
template <class A>
class pump
{
const double max;
const double heat;
fluid_holder* const src;
fluid_holder* const dst;
A* a;
double rate = 0;
const double mass; // grams
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:
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
{
return rate;
}
double get_flow() const; // L/s
double get_flow_mass() const; // g/s
double get_rpm() const; // rev/min
constexpr double get_flow() const
{
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;
}
const char* get_state_string();
void update(double dt);
};
};

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
#include "fluid_holder.hpp"
namespace sim::coolant
{
template <class A>
class valve
{
A* a;
const double max;
const double pressure;
fluid_holder* const src;
fluid_holder* const dst;
double speed = 0;
double state = 0;
double rate = 0;
double flow = 0; // L/s
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
{
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;
}
constexpr double get_state() const { return state; }
constexpr double get_flow() const { return flow; }
};
};

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

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
{
class control_rod : public sim::reactor::coolant::pipe
class boron_rod : public coolant::pipe
{
const double limit;
const double max;
double inserted = 1;
double absorbed = 0;
virtual void display(std::ostream& o) const;
virtual const char* get_name() const { return "Control Rod"; }
virtual const char* get_name() const { return "Boron Control Rod"; }
virtual glm::vec4 get_colour() const;
virtual int get_id() const { return 5; }
public:
control_rod(coolant::vessel& v, double limit, double max);
boron_rod(coolant::vessel* v);
virtual void update(double secs);
void set_reactivity(double a);
double get_inserted() { return inserted; }
virtual bool should_display() const { return true; }
virtual bool should_select() const { return true; }
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";
};
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
{
if(type == val_t::HEAT) return 0.5;

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