Add a basic game controller and basic GL wrappers.

This commit is contained in:
Hannes Mann 2016-07-26 05:52:46 +02:00
parent 0a9d60b210
commit 961a144d46
18 changed files with 700 additions and 78 deletions

5
.gitignore vendored
View file

@ -38,4 +38,7 @@ build_win/
# SDL2 windows # SDL2 windows
!win32/SDL2/libSDL2.dll.a !win32/SDL2/libSDL2.dll.a
!win32/SDL2/SDL2.dll !win32/SDL2/SDL2.dll
# CLion
.idea/

View file

@ -121,7 +121,7 @@ namespace openrayman
encoded_buf::int_type encoded_buf::underflow() encoded_buf::int_type encoded_buf::underflow()
{ {
int value = m_in.peek(); auto value = m_in.peek();
if(value == traits_type::eof()) if(value == traits_type::eof())
return traits_type::eof(); return traits_type::eof();
return traits_type::to_int_type(decode_char((char)value)); return traits_type::to_int_type(decode_char((char)value));
@ -129,10 +129,10 @@ namespace openrayman
encoded_buf::int_type encoded_buf::uflow() encoded_buf::int_type encoded_buf::uflow()
{ {
int value = m_in.get(); auto value = m_in.get();
if(value == traits_type::eof()) if(value == traits_type::eof())
return traits_type::eof(); return traits_type::eof();
char c = decode_char((char)value); auto c = decode_char((char)value);
advance_virtual_position(1, true); advance_virtual_position(1, true);
return traits_type::to_int_type(c); return traits_type::to_int_type(c);
} }

View file

@ -59,34 +59,34 @@ namespace openrayman
config_stream >> config_json; config_stream >> config_json;
if(config_json.count("game") > 0) if(config_json.count("game") > 0)
game = config_json["game"]; game = config_json["game"];
if(config_json.count("gfx") > 0) if(config_json.count("renderer") > 0)
{ {
vsync = config_json["gfx"]["vsync"]; vsync = config_json["renderer"]["vsync"];
fullscreen = config_json["gfx"]["fullscreen"]; fullscreen = config_json["renderer"]["fullscreen"];
max_fps = config_json["gfx"]["max_fps"]; max_fps = config_json["renderer"]["max_fps"];
} }
if(config_json.count("keyboard_map") > 0) if(config_json.count("input") > 0 && config_json["input"].count("keyboard") > 0)
{ {
keyboard_map["stick(strength)"] = config_json["keyboard_map"]["stick(strength)"]; keyboard_map["stick(strength)"] = config_json["input"]["keyboard"]["stick(strength)"];
keyboard_map["stick(x, -)"] = config_json["keyboard_map"]["stick(x, -)"]; keyboard_map["stick(x, -)"] = config_json["input"]["keyboard"]["stick(x, -)"];
keyboard_map["stick(x, +)"] = config_json["keyboard_map"]["stick(x, +)"]; keyboard_map["stick(x, +)"] = config_json["input"]["keyboard"]["stick(x, +)"];
keyboard_map["stick(y, -)"] = config_json["keyboard_map"]["stick(y, -)"]; keyboard_map["stick(y, -)"] = config_json["input"]["keyboard"]["stick(y, -)"];
keyboard_map["stick(y, +)"] = config_json["keyboard_map"]["stick(y, +)"]; keyboard_map["stick(y, +)"] = config_json["input"]["keyboard"]["stick(y, +)"];
keyboard_map["start"] = config_json["keyboard_map"]["start"]; keyboard_map["start"] = config_json["input"]["keyboard"]["start"];
keyboard_map["a"] = config_json["keyboard_map"]["a"]; keyboard_map["a"] = config_json["input"]["keyboard"]["a"];
keyboard_map["b"] = config_json["keyboard_map"]["b"]; keyboard_map["b"] = config_json["input"]["keyboard"]["b"];
keyboard_map["l"] = config_json["keyboard_map"]["l"]; keyboard_map["l"] = config_json["input"]["keyboard"]["l"];
keyboard_map["r"] = config_json["keyboard_map"]["r"]; keyboard_map["r"] = config_json["input"]["keyboard"]["r"];
keyboard_map["z"] = config_json["keyboard_map"]["z"]; keyboard_map["z"] = config_json["input"]["keyboard"]["z"];
keyboard_map["cbtn(x, -)"] = config_json["keyboard_map"]["cbtn(x, -)"]; keyboard_map["cbtn(x, -)"] = config_json["input"]["keyboard"]["cbtn(x, -)"];
keyboard_map["cbtn(x, +)"] = config_json["keyboard_map"]["cbtn(x, +)"]; keyboard_map["cbtn(x, +)"] = config_json["input"]["keyboard"]["cbtn(x, +)"];
keyboard_map["cbtn(y, -)"] = config_json["keyboard_map"]["cbtn(y, -)"]; keyboard_map["cbtn(y, -)"] = config_json["input"]["keyboard"]["cbtn(y, -)"];
keyboard_map["cbtn(y, +)"] = config_json["keyboard_map"]["cbtn(y, +)"]; keyboard_map["cbtn(y, +)"] = config_json["input"]["keyboard"]["cbtn(y, +)"];
} }
return true; return true;
} }
@ -101,34 +101,35 @@ namespace openrayman
nlohmann::json config_json; nlohmann::json config_json;
config_json["game"] = game; config_json["game"] = game;
config_json["gfx"] = nlohmann::json::object(); config_json["renderer"] = nlohmann::json::object();
config_json["gfx"]["vsync"] = vsync; config_json["renderer"]["vsync"] = vsync;
config_json["gfx"]["fullscreen"] = fullscreen; config_json["renderer"]["fullscreen"] = fullscreen;
config_json["gfx"]["max_fps"] = max_fps; config_json["renderer"]["max_fps"] = max_fps;
config_json["keyboard_map"] = nlohmann::json::object(); config_json["input"] = nlohmann::json::object();
config_json["input"]["keyboard"] = nlohmann::json::object();
config_json["keyboard_map"]["stick(strength)"] = keyboard_map["stick(strength)"]; config_json["input"]["keyboard"]["stick(strength)"] = keyboard_map["stick(strength)"];
config_json["keyboard_map"]["stick(x, -)"] = keyboard_map["stick(x, -)"]; config_json["input"]["keyboard"]["stick(x, -)"] = keyboard_map["stick(x, -)"];
config_json["keyboard_map"]["stick(x, +)"] = keyboard_map["stick(x, +)"]; config_json["input"]["keyboard"]["stick(x, +)"] = keyboard_map["stick(x, +)"];
config_json["keyboard_map"]["stick(y, -)"] = keyboard_map["stick(y, -)"]; config_json["input"]["keyboard"]["stick(y, -)"] = keyboard_map["stick(y, -)"];
config_json["keyboard_map"]["stick(y, +)"] = keyboard_map["stick(y, +)"]; config_json["input"]["keyboard"]["stick(y, +)"] = keyboard_map["stick(y, +)"];
config_json["keyboard_map"]["start"] = keyboard_map["start"]; config_json["input"]["keyboard"]["start"] = keyboard_map["start"];
config_json["keyboard_map"]["a"] = keyboard_map["a"]; config_json["input"]["keyboard"]["a"] = keyboard_map["a"];
config_json["keyboard_map"]["b"] = keyboard_map["b"]; config_json["input"]["keyboard"]["b"] = keyboard_map["b"];
config_json["keyboard_map"]["l"] = keyboard_map["l"]; config_json["input"]["keyboard"]["l"] = keyboard_map["l"];
config_json["keyboard_map"]["r"] = keyboard_map["r"]; config_json["input"]["keyboard"]["r"] = keyboard_map["r"];
config_json["keyboard_map"]["z"] = keyboard_map["z"]; config_json["input"]["keyboard"]["z"] = keyboard_map["z"];
config_json["keyboard_map"]["cbtn(x, -)"] = keyboard_map["cbtn(x, -)"]; config_json["input"]["keyboard"]["cbtn(x, -)"] = keyboard_map["cbtn(x, -)"];
config_json["keyboard_map"]["cbtn(x, +)"] = keyboard_map["cbtn(x, +)"]; config_json["input"]["keyboard"]["cbtn(x, +)"] = keyboard_map["cbtn(x, +)"];
config_json["keyboard_map"]["cbtn(y, -)"] = keyboard_map["cbtn(y, -)"]; config_json["input"]["keyboard"]["cbtn(y, -)"] = keyboard_map["cbtn(y, -)"];
config_json["keyboard_map"]["cbtn(y, +)"] = keyboard_map["cbtn(y, +)"]; config_json["input"]["keyboard"]["cbtn(y, +)"] = keyboard_map["cbtn(y, +)"];
std::ofstream config_stream(config_json_file, std::ofstream::trunc); std::ofstream config_stream(config_json_file, std::ofstream::trunc);
if(config_stream.is_open()) if(config_stream.is_open())

View file

@ -72,7 +72,7 @@ namespace openrayman
// Provide more complete info for the user. // Provide more complete info for the user.
std::stringstream title; std::stringstream title;
title << "OpenRayman " << openrayman::version << " " title << "OpenRayman " << openrayman::version << " "
<< (this_platform == platform::windows ? "Win32" : "Linux") << map_platform(this_platform)
<< " (OpenGL " << gl_major << "." << gl_minor << ")" << " (OpenGL " << gl_major << "." << gl_minor << ")"
<< " (Game \"" << load_game << "\")"; << " (Game \"" << load_game << "\")";
m_window.set_title(title.str()); m_window.set_title(title.str());
@ -82,13 +82,15 @@ namespace openrayman
if(!lodepng::decode(icon_data, width, height, file::fix_string(m_backend_specifics.data_path() + "/common/icon.png"))) if(!lodepng::decode(icon_data, width, height, file::fix_string(m_backend_specifics.data_path() + "/common/icon.png")))
m_window.set_icon(icon_data.data(), width, height); m_window.set_icon(icon_data.data(), width, height);
m_game_controller = std::unique_ptr<game_controller>(new game_controller(*this, *m_game, m_renderer));
m_last_timer_value = m_backend_specifics.time(); m_last_timer_value = m_backend_specifics.time();
input_provider& provider = m_window.create_input_provider(); input_provider& provider = m_window.create_input_provider();
while(!m_exit_requested) while(!m_exit_requested)
{ {
double current_timer_value = m_backend_specifics.time(); double current_timer_value = m_backend_specifics.time();
m_current_delta_time = current_timer_value - m_last_timer_value; m_current_delta_time = current_timer_value - m_last_timer_value;
m_accumulated_time_fixed += m_current_delta_time; m_accumulated_time_timed += m_current_delta_time;
m_accumulated_time_fps += m_current_delta_time; m_accumulated_time_fps += m_current_delta_time;
m_total_time += m_current_delta_time; m_total_time += m_current_delta_time;
m_last_timer_value = current_timer_value; m_last_timer_value = current_timer_value;
@ -99,6 +101,7 @@ namespace openrayman
m_window.poll_events(); m_window.poll_events();
m_last_input = m_current_input; m_last_input = m_current_input;
const input_state& st = provider.poll(); const input_state& st = provider.poll();
m_current_input = input_state(st.buttons, st.commands, st.stick_x, st.stick_y); m_current_input = input_state(st.buttons, st.commands, st.stick_x, st.stick_y);
if(m_current_input.command(input_command::toggle_fullscreen) && !m_last_input.command(input_command::toggle_fullscreen)) if(m_current_input.command(input_command::toggle_fullscreen) && !m_last_input.command(input_command::toggle_fullscreen))
@ -112,10 +115,10 @@ namespace openrayman
m_fps = m_accumulated_frames_fps; m_fps = m_accumulated_frames_fps;
m_accumulated_time_fps = m_accumulated_frames_fps = 0; m_accumulated_time_fps = m_accumulated_frames_fps = 0;
} }
while(m_accumulated_time_fixed >= 1 / 60.0) while(m_accumulated_time_timed >= 1 / 60.0)
{ {
m_total_fixed_updates++; m_total_timed_updates++;
m_accumulated_time_fixed -= 1 / 60.0; m_accumulated_time_timed -= 1 / 60.0;
} }
m_window.present(); m_window.present();

View file

@ -13,13 +13,16 @@
#include <GL/gl3w.h> #include <GL/gl3w.h>
#include <config/config.h> #include <config/config.h>
#include <game.h> #include <game.h>
#include <game_controller.h>
#include <renderer/renderer.h>
#include <cstdint> #include <cstdint>
#include <cstdlib>
#include <string> #include <string>
#include <memory> #include <memory>
namespace openrayman namespace openrayman
{ {
// Core of OpenRayman. Handles timing, input and GL contexts. // The engine controls game initialization, timing and renderer/input/window management.
class engine class engine
{ {
public: public:
@ -36,12 +39,14 @@ public:
m_last_timer_value(0), m_last_timer_value(0),
m_current_delta_time(0), m_current_delta_time(0),
m_total_time(0), m_total_time(0),
m_accumulated_time_fixed(0), m_accumulated_time_timed(0),
m_accumulated_time_fps(0), m_accumulated_time_fps(0),
m_total_frames(0), m_total_frames(0),
m_total_fixed_updates(0), m_total_timed_updates(0),
m_accumulated_frames_fps(0), m_accumulated_frames_fps(0),
m_fps(0), m_fps(0),
m_game(nullptr),
m_game_controller(nullptr),
#ifdef LIBRETRO_CORE #ifdef LIBRETRO_CORE
m_window(*(new libretro_window())) m_window(*(new libretro_window()))
#else #else
@ -70,13 +75,13 @@ public:
} }
// Returns a reference to the engine window. // Returns a reference to the engine window.
inline window& current_window() const inline window& active_window() const
{ {
return m_window; return m_window;
} }
// Returns a reference to the backend specifics that are currently in use. // Returns a reference to the backend specifics that are currently in use.
inline backend_specifics& current_backend_specifics() const inline backend_specifics& active_backend_specifics() const
{ {
return m_backend_specifics; return m_backend_specifics;
} }
@ -111,40 +116,49 @@ public:
return m_total_frames; return m_total_frames;
} }
// Returns the total amount of fixed updates that have passed since the start of the game. // Returns the total amount of timed updates that have passed since the start of the game.
inline std::uint64_t total_fixed_updates() const inline std::uint64_t total_timed_updates() const
{ {
return m_total_fixed_updates; return m_total_timed_updates;
} }
// Returns the amount of frames that were executed during the previous second. // Returns the amount of frames that were executed during the previous second.
inline double fps() const inline double fps() const
{ {
if(std::abs((int)(m_fps - (1 / m_current_delta_time))) > 2)
return 1 / m_current_delta_time;
// this is more accurate // this is more accurate
return (m_fps + (1 / m_current_delta_time)) / 2; return (m_fps + (1 / m_current_delta_time)) / 2;
} }
// Returns a reference to the active config. // Returns a reference to the active config.
inline const config& current_config() const inline const config& active_config() const
{ {
return m_config; return m_config;
} }
// Returns a reference to the active game. // Returns a reference to the active game.
inline const game& current_game() const inline const game& active_game() const
{ {
return *m_game; return *m_game;
} }
// Returns a reference to the active game controller.
inline game_controller& active_game_controller()
{
return *m_game_controller;
}
private: private:
window& m_window; window& m_window;
backend_specifics& m_backend_specifics; backend_specifics& m_backend_specifics;
input_state m_current_input; input_state m_current_input;
input_state m_last_input; input_state m_last_input;
double m_last_timer_value, m_current_delta_time, m_total_time, m_accumulated_time_fixed, m_accumulated_time_fps; double m_last_timer_value, m_current_delta_time, m_total_time, m_accumulated_time_timed, m_accumulated_time_fps;
std::uint64_t m_total_frames, m_total_fixed_updates, m_accumulated_frames_fps, m_fps; std::uint64_t m_total_frames, m_total_timed_updates, m_accumulated_frames_fps, m_fps;
config m_config; config m_config;
std::unique_ptr<game> m_game; std::unique_ptr<game> m_game;
renderer m_renderer;
std::unique_ptr<game_controller> m_game_controller;
bool m_exit_requested; bool m_exit_requested;
}; };
} }

30
src/game_controller.cc Normal file
View file

@ -0,0 +1,30 @@
#include <game_controller.h>
namespace openrayman
{
game_controller::game_controller(engine &active_engine, game &active_game, renderer &active_renderer) :
m_engine(active_engine), m_game(active_game), m_renderer(active_renderer)
{
}
game_controller::~game_controller()
{
}
void game_controller::tick(double delta, std::uint64_t update, input_state &input, input_state &last_input)
{
}
void game_controller::timed_tick(std::uint64_t update, input_state &input, input_state &last_input)
{
}
void game_controller::render()
{
}
}

46
src/game_controller.h Normal file
View file

@ -0,0 +1,46 @@
#ifndef GAME_CONTROLLER_H
#define GAME_CONTROLLER_H
#include <game.h>
#include <renderer/renderer.h>
#include <input/input_state.h>
#include <cstdint>
namespace openrayman
{
class engine;
// The game controller controls the world, collision and entities within it.
// It is also responsible for loading resources and showing "vignettes" (images, loading screens).
class game_controller
{
public:
game_controller(engine& active_engine, game& active_game, renderer& active_renderer);
~game_controller();
void tick(double delta, std::uint64_t update, input_state& input, input_state& last_input);
void timed_tick(std::uint64_t update, input_state& input, input_state& last_input);
void render();
inline engine& active_engine()
{
return m_engine;
}
inline game& active_game()
{
return m_game;
}
inline renderer& active_renderer()
{
return m_renderer;
}
private:
engine& m_engine;
game& m_game;
renderer& m_renderer;
};
}
#endif

View file

@ -2,6 +2,7 @@
#define INFO_H #define INFO_H
#include <string> #include <string>
#include <map>
namespace openrayman namespace openrayman
{ {
@ -12,18 +13,32 @@ namespace openrayman
const int version_code = 10; const int version_code = 10;
// Platform definitions. // Platform definitions.
#ifdef linux enum class platform { win32, linux_desktop, libretro, android };
#undef linux
#endif inline std::string map_platform(platform p)
enum class platform { windows, linux }; {
switch(p)
{
case platform::win32:
return "Windows (Win32)";
case platform::linux_desktop:
return "Linux";
case platform::libretro:
return "libretro";
case platform::android:
return "Android";
}
}
// The platform that engine is running on (that it was compiled for). // The platform that engine is running on (that it was compiled for).
// Assume we're on linux unless _WIN32 is specified, as those are the only two platforms we support. // Assume we're on linux unless _WIN32 is specified, as those are the only two platforms we support right now.
// TODO: platform for libretro?!?
#ifdef _WIN32 #ifdef _WIN32
const platform this_platform = platform::windows; const platform this_platform = platform::win32;
#else #else
const platform this_platform = platform::linux; const platform this_platform = platform::linux_desktop;
#endif #endif
} }

View file

@ -80,7 +80,7 @@ int main(int argc, char** argv)
return openrayman::gf_tools::print_info(path); return openrayman::gf_tools::print_info(path);
return fail_and_print("Invalid format was specified"); return fail_and_print("Invalid format was specified");
} }
if(str == "--force-reset-rayman2") if(str == "--force-reset-base-rayman2")
{ {
openrayman::standalone_backend_specifics backend_specifics; openrayman::standalone_backend_specifics backend_specifics;
openrayman::file::delete_directory(backend_specifics.storage_path() + "/games/rayman2"); openrayman::file::delete_directory(backend_specifics.storage_path() + "/games/rayman2");
@ -122,7 +122,7 @@ int main(int argc, char** argv)
std::cout << " \"cnt\": Prints file hierarchy" << std::endl; std::cout << " \"cnt\": Prints file hierarchy" << std::endl;
std::cout << " \"gf\": Prints width, height and number of channels" << std::endl; std::cout << " \"gf\": Prints width, height and number of channels" << std::endl;
std::cout << " --extract-cnt-to \"archive\" \"path\" \"target\" Extracts a file from a CNT archive" << std::endl; std::cout << " --extract-cnt-to \"archive\" \"path\" \"target\" Extracts a file from a CNT archive" << std::endl;
std::cout << " --force-reset-rayman2 Forces a removal of the rayman2 base game, if it exists" << std::endl; std::cout << " --force-reset-base-rayman2 Forces a removal of the rayman2 base game, if it exists" << std::endl;
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
if(str == "--version") if(str == "--version")
@ -136,7 +136,7 @@ int main(int argc, char** argv)
} }
} }
std::cout << "OpenRayman " << openrayman::version << std::endl; std::cout << "OpenRayman " << openrayman::version << std::endl;
std::cout << "Running on " << (openrayman::this_platform == openrayman::platform::windows ? "Windows" : "Linux") << std::endl; std::cout << "Running on " << openrayman::map_platform(openrayman::this_platform) << std::endl;
std::cout << "Using game " << (selected_game == "" ? "from config" : "\"" + selected_game + "\"") << std::endl; std::cout << "Using game " << (selected_game == "" ? "from config" : "\"" + selected_game + "\"") << std::endl;
openrayman::engine engine; openrayman::engine engine;
return engine.run(selected_game, selected_install_folder); return engine.run(selected_game, selected_install_folder);

48
src/math/math.h Normal file
View file

@ -0,0 +1,48 @@
#ifndef MATH_H
#define MATH_H
#include <glm/vec2.hpp>
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
namespace openrayman
{
// Math helpers.
namespace math
{
// Interpolates between values "from" - "to".
inline float interp(float from, float to, float value)
{
return from + (to - from) * value;
}
// Interpolates between values "from" - "to".
inline glm::vec2 interp(glm::vec2 from, glm::vec2 to, float value)
{
return glm::vec2(
interp(from.x, to.x, value),
interp(from.y, to.y, value));
}
// Interpolates between values "from" - "to".
inline glm::vec3 interp(glm::vec3 from, glm::vec3 to, float value)
{
return glm::vec3(
interp(from.x, to.x, value),
interp(from.y, to.y, value),
interp(from.z, to.z, value));
}
// Interpolates between values "from" - "to".
inline glm::vec4 interp(glm::vec4 from, glm::vec4 to, float value)
{
return glm::vec4(
interp(from.r, to.r, value),
interp(from.g, to.g, value),
interp(from.b, to.b, value),
interp(from.a, to.a, value));
}
}
}
#endif

30
src/renderer/bind_guard.h Normal file
View file

@ -0,0 +1,30 @@
#ifndef BIND_GUARD_H
#define BIND_GUARD_H
#include <renderer/buf.h>
#include <renderer/texture.h>
namespace openrayman
{
// Holds a bind on a GL resource until it goes out of scope.
template<typename T>
class bind_guard
{
static_assert(std::is_same<vertex_buf, T>::value ||
std::is_same<element_buf, T>::value ||
std::is_same<texture, T>::value,
"Not a GL resource type");
public:
bind_guard(const T& t)
{
t.bind();
}
~bind_guard()
{
T::bind_null();
}
};
}
#endif

102
src/renderer/buf.cc Normal file
View file

@ -0,0 +1,102 @@
#include <renderer/buf.h>
#include <renderer/bind_guard.h>
namespace openrayman
{
thread_local GLuint vertex_buf::m_current_bound = 0;
thread_local GLuint element_buf::m_current_bound = 0;
vertex_buf::vertex_buf(std::vector<glm::vec4>& initial_data, buf_mod_freq frequency) :
m_local_data(initial_data), m_frequency(frequency)
{
glGenBuffers(1, &m_gl_object);
commit();
}
void vertex_buf::commit()
{
bind_guard<vertex_buf> guard(*this);
GLenum usage;
switch(m_frequency)
{
case buf_mod_freq::never:
usage = GL_STATIC_DRAW;
break;
case buf_mod_freq::some:
usage = GL_DYNAMIC_DRAW;
break;
case buf_mod_freq::every_frame:
usage = GL_STREAM_DRAW;
break;
}
glBufferData(GL_ARRAY_BUFFER, m_local_data.size() * 4, m_local_data.data(), usage);
}
vertex_buf::~vertex_buf()
{
glDeleteBuffers(1, &m_gl_object);
}
void vertex_buf::bind() const
{
if(!is_bound())
glBindBuffer(GL_ARRAY_BUFFER, m_gl_object);
m_current_bound = m_gl_object;
}
void vertex_buf::bind_null()
{
if(m_current_bound != 0)
glBindBuffer(GL_ARRAY_BUFFER, 0);
m_current_bound = 0;
}
element_buf::element_buf(std::vector<std::uint32_t>& initial_data, buf_mod_freq frequency) :
m_local_data(initial_data), m_frequency(frequency)
{
glGenBuffers(1, &m_gl_object);
commit();
}
void element_buf::commit()
{
bind_guard<element_buf> guard(*this);
GLenum usage;
switch(m_frequency)
{
case buf_mod_freq::never:
usage = GL_STATIC_DRAW;
break;
case buf_mod_freq::some:
usage = GL_DYNAMIC_DRAW;
break;
case buf_mod_freq::every_frame:
usage = GL_STREAM_DRAW;
break;
}
glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_local_data.size(), m_local_data.data(), usage);
}
void element_buf::bind() const
{
if(!is_bound())
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_gl_object);
m_current_bound = m_gl_object;
}
void element_buf::bind_null()
{
if(m_current_bound != 0)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
m_current_bound = 0;
}
element_buf::~element_buf()
{
glDeleteBuffers(1, &m_gl_object);
}
}

93
src/renderer/buf.h Normal file
View file

@ -0,0 +1,93 @@
#ifndef BUF_H
#define BUF_H
#include <GL/gl3w.h>
#include <glm/vec4.hpp>
#include <cstdint>
#include <vector>
namespace openrayman
{
// The suggested frequency at which a buffer will be modified, to enable for driver optimizations.
enum class buf_mod_freq
{
// The buffer will have data uploaded in its constructor and then preferably never again.
never,
// The buffer will be modified from time to time, but drawn many more times than that.
some,
// The buffer will be modified as many times at it is drawn (usually once every frame).
every_frame
};
// Buffer on the GPU that holds vertices.
class vertex_buf
{
public:
vertex_buf(std::vector<glm::vec4>& initial_data, buf_mod_freq frequency = buf_mod_freq::some);
~vertex_buf();
// Data stored in RAM. This can be modified and then committed via commit().
inline std::vector<glm::vec4>& local_data()
{
return m_local_data;
}
// Commits applied changes to the GPU.
void commit();
// Binds this element buffer as the currently active buffer.
void bind() const;
// Returns true if this buffer is the currently bound one.
inline bool is_bound() const
{
return m_current_bound == m_gl_object;
}
// Binds a null buffer as the currently active buffer.
static void bind_null();
private:
GLuint m_gl_object;
thread_local static GLuint m_current_bound;
std::vector<glm::vec4> m_local_data;
buf_mod_freq m_frequency;
};
// Buffer on the GPU that holds elements.
class element_buf
{
public:
element_buf(std::vector<std::uint32_t>& initial_data, buf_mod_freq frequency = buf_mod_freq::some);
~element_buf();
// Data stored in RAM. This can be modified and then committed via commit().
inline std::vector<std::uint32_t>& local_data()
{
return m_local_data;
}
// Commits applied changes to the GPU.
void commit();
// Binds this element buffer as the currently active buffer.
void bind() const;
// Returns true if this buffer is the currently bound one.
inline bool is_bound() const
{
return m_current_bound == m_gl_object;
}
// Binds a null buffer as the currently active buffer.
static void bind_null();
private:
GLuint m_gl_object;
thread_local static GLuint m_current_bound;
std::vector<std::uint32_t> m_local_data;
buf_mod_freq m_frequency;
};
}
#endif

12
src/renderer/renderer.h Normal file
View file

@ -0,0 +1,12 @@
#ifndef RENDERER_H
#define RENDERER_H
namespace openrayman
{
class renderer
{
};
}
#endif

136
src/renderer/texture.cc Normal file
View file

@ -0,0 +1,136 @@
#include <renderer/texture.h>
#include <renderer/bind_guard.h>
#include <lodepng.h>
#include <cstdint>
#include <cmath>
#include <algorithm>
namespace openrayman
{
thread_local GLuint texture::m_current_bound = 0;
texture::texture(glm::vec2 size, texture_filter filter = texture_filter::linear) :
texture::texture(size, glm::vec4(1, 1, 1, 1), filter)
{
}
texture::texture(glm::vec2 size, glm::vec4 fill_color, texture_filter filter = texture_filter::linear) :
m_size((int)size.x, (int)size.y),
m_filter(filter), m_is_expected_data(false),
m_local_cpy((int)size.x * (int)size.y, fill_color)
{
glGenTextures(1, &m_gl_object);
bind_guard<texture> guard(*this);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (int)size.x, (int)size.y, 0, GL_RGBA, GL_FLOAT, m_local_cpy.data());
m_is_expected_data = true;
}
texture::texture(glm::vec2 size, const std::vector<glm::vec4>& image, texture_filter filter = texture_filter::linear) :
m_size((int)size.x, (int)size.y),
m_filter(filter),
m_is_expected_data(false),
m_local_cpy(image)
{
glGenTextures(1, &m_gl_object);
bind_guard<texture> guard(*this);
if(m_size.x * m_size.y == m_local_cpy.size())
{
m_is_expected_data = false;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (int)size.x, (int)size.y, 0, GL_RGBA, GL_FLOAT, m_local_cpy.data());
}
else
{
std::cout << "[openrayman::renderer::texture (constructor)] input data does not match input size" << std::endl;
fill_with_white();
}
}
texture::texture(std::istream& stream, texture_filter filter = texture_filter::linear) :
m_size(1, 1),
m_filter(filter),
m_is_expected_data(false)
{
glGenTextures(1, &m_gl_object);
bind_guard<texture> guard(*this);
std::vector<unsigned char> data;
stream.seekg(0);
unsigned char buffer[1024 * 1024];
std::streamsize read = 0;
while((read = stream.read(&buffer, 1024 * 1024)) > 0)
{
if(stream.fail())
{
std::cout << "[openrayman::renderer::texture (constructor)] stream.fail()" << std::endl;
fill_with_white();
return;
}
data.insert(data.end(), buffer, buffer + read);
}
if(data.size() == 0)
{
std::cout << "[openrayman::renderer::texture (constructor)] no data in stream" << std::endl;
fill_with_white();
return;
}
std::vector<unsigned char> result;
int result_w, result_h;
std::uint32_t err = lodepng::decode(result, result_w, result_h, data);
if(err)
{
fill_with_white();
std::cout << "[openrayman::renderer::texture (constructor)] LodePNG error (" << lodepng_error_text(err) << ")" << std::endl;
}
else
{
m_size.w = result_w;
m_size.h = result_h;
m_local_cpy.resize(result_w * result_h);
for(std::size_t n = 0; n < result_w * result_h, n++)
m_local_cpy[n] = glm::vec4(
result[(n * 4)] / 255.0,
result[(n * 4) + 1] / 255.0,
result[(n * 4) + 2] / 255.0,
result[(n * 4) + 3] / 255.0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (int)size.x, (int)size.y, 0, GL_RGBA, GL_FLOAT, m_local_cpy.data());
m_is_expected_data = true;
}
}
void texture::fill_with_white()
{
m_local_cpy.clear();
m_local_cpy.resize((int)size.x * (int)size.y);
std::fill(m_local_cpy.begin(), m_local_cpy.end(), glm::vec4(1, 1, 1, 1));
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (int)size.x, (int)size.y, 0, GL_RGBA, GL_FLOAT, m_local_cpy.data());
}
glm::vec4 texture::sample_color(glm::vec2 at) const
{
at = glm::vec2(
std::fmod(at.x, size.w),
std::fmod(at.y, size.h));
return m_local_cpy[((int)at.y * size.w) + (int)at.x];
}
void texture::bind() const
{
if(!is_bound())
glBindTexture(GL_TEXTURE_2D, m_gl_object);
m_current_bound = m_gl_object;
}
void texture::bind_null() const
{
if(m_current_bound != 0)
glBindTexture(GL_TEXTURE_2D, 0);
m_current_bound = 0;
}
texture::~texture()
{
glDeleteTexture(1, &m_gl_object);
}
}

89
src/renderer/texture.h Normal file
View file

@ -0,0 +1,89 @@
#ifndef TEXTURE_H
#define TEXTURE_H
#include <GL/gl3w.h>
#include <glm/vec2.hpp>
#include <glm/vec4.hpp>
#include <istream>
namespace openrayman
{
enum class texture_filter
{
nearest = GL_NEAREST,
linear = GL_LINEAR
};
// RGBA32 texture on the GPU.
class texture
{
// Creates a new texture filled with white pixels (1, 1, 1, 1).
texture(glm::vec2 size, texture_filter filter = texture_filter::linear);
// Creates a new texture filled with the color specified by "fill_color".
texture(glm::vec2 size, glm::vec4 fill_color, texture_filter filter = texture_filter::linear);
// Creates a new texture filled with the data in the vector "image".
texture(glm::vec2 size, const std::vector<glm::vec4>& image, texture_filter filter = texture_filter::linear);
// Creates a new texture from a stream.
// Current supported file formats are PNG.
texture(std::istream& stream, texture_filter filter = texture_filter::linear);
~texture();
// Returns true if this texture contains the expected data.
// This can return false if a file failed to load,
// size x/y == 0, or creation from a vector did not match the size parameter.
// In this case the texture will contain all white pixels (1, 1, 1, 1).
inline bool is_expected_data() const
{
return m_is_expected_data;
}
// Returns the size of this texture.
inline glm::vec2 size() const
{
return m_size;
}
// Returns the filtering of this texture.
inline texture_filter filter() const
{
return m_filter;
}
// Returns the color of the pixel at the sample position.
glm::vec4 sample_color(glm::vec2 at) const;
// Returns an array of all colors in this texture.
inline const std::vector<glm::vec4>& sample_all() const
{
return m_local_cpy;
}
// Binds this texture as the currently active texture.
void bind() const;
// Returns true if this texture is the currently bound one.
inline bool is_bound() const
{
return m_current_bound == m_gl_object;
}
// Binds a null texture as the currently active texture.
static void bind_null();
private:
GLuint m_gl_object;
thread_local static GLuint m_current_bound;
void fill_with_white();
bool m_is_expected_data;
glm::vec2 m_size;
texture_filter m_filter;
std::vector<glm::vec4> m_local_cpy;
};
}
#endif

View file

@ -8,7 +8,7 @@ namespace openrayman
sdl_window::sdl_window(config& config) : sdl_window::sdl_window(config& config) :
m_window(nullptr), m_window(nullptr),
m_current_fullscreen(false), m_is_fullscreen(false),
m_vsync_enabled(false), m_vsync_enabled(false),
m_wants_close(false), m_wants_close(false),
m_input_provider(config) m_input_provider(config)
@ -64,7 +64,7 @@ namespace openrayman
SDL_GL_SetSwapInterval(0); SDL_GL_SetSwapInterval(0);
SDL_ShowCursor(SDL_DISABLE); SDL_ShowCursor(SDL_DISABLE);
m_current_fullscreen = initial_fullscreen; m_is_fullscreen = initial_fullscreen;
return true; return true;
} }
@ -166,7 +166,7 @@ namespace openrayman
void sdl_window::set_size(int w, int h) void sdl_window::set_size(int w, int h)
{ {
if(!m_current_fullscreen) if(!m_is_fullscreen)
SDL_SetWindowSize(m_window, w, h); SDL_SetWindowSize(m_window, w, h);
} }
@ -184,10 +184,10 @@ namespace openrayman
void sdl_window::set_fullscreen(bool fullscreen) void sdl_window::set_fullscreen(bool fullscreen)
{ {
if(fullscreen == m_current_fullscreen) if(fullscreen == m_is_fullscreen)
return; return;
SDL_SetWindowFullscreen(m_window, fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0x00); SDL_SetWindowFullscreen(m_window, fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0x00);
m_current_fullscreen = fullscreen; m_is_fullscreen = fullscreen;
} }
void sdl_window::set_icon(std::uint8_t* rgba32_data, int w, int h) void sdl_window::set_icon(std::uint8_t* rgba32_data, int w, int h)

View file

@ -42,7 +42,7 @@ public:
private: private:
SDL_Window* m_window; SDL_Window* m_window;
SDL_GLContext m_gl_ctx; SDL_GLContext m_gl_ctx;
bool m_current_fullscreen, m_vsync_enabled, m_wants_close; bool m_is_fullscreen, m_vsync_enabled, m_wants_close;
standalone_input_provider m_input_provider; standalone_input_provider m_input_provider;
static int m_sdl_ref_count; static int m_sdl_ref_count;