mirror of
https://github.com/imaginaryPineapple/OpenRayman.git
synced 2024-10-19 14:37:36 -04:00
Add a basic game controller and basic GL wrappers.
This commit is contained in:
parent
0a9d60b210
commit
961a144d46
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -38,4 +38,7 @@ build_win/
|
|||
|
||||
# SDL2 windows
|
||||
!win32/SDL2/libSDL2.dll.a
|
||||
!win32/SDL2/SDL2.dll
|
||||
!win32/SDL2/SDL2.dll
|
||||
|
||||
# CLion
|
||||
.idea/
|
||||
|
|
|
@ -121,7 +121,7 @@ namespace openrayman
|
|||
|
||||
encoded_buf::int_type encoded_buf::underflow()
|
||||
{
|
||||
int value = m_in.peek();
|
||||
auto value = m_in.peek();
|
||||
if(value == traits_type::eof())
|
||||
return traits_type::eof();
|
||||
return traits_type::to_int_type(decode_char((char)value));
|
||||
|
@ -129,10 +129,10 @@ namespace openrayman
|
|||
|
||||
encoded_buf::int_type encoded_buf::uflow()
|
||||
{
|
||||
int value = m_in.get();
|
||||
auto value = m_in.get();
|
||||
if(value == traits_type::eof())
|
||||
return traits_type::eof();
|
||||
char c = decode_char((char)value);
|
||||
auto c = decode_char((char)value);
|
||||
advance_virtual_position(1, true);
|
||||
return traits_type::to_int_type(c);
|
||||
}
|
||||
|
|
|
@ -59,34 +59,34 @@ namespace openrayman
|
|||
config_stream >> config_json;
|
||||
if(config_json.count("game") > 0)
|
||||
game = config_json["game"];
|
||||
if(config_json.count("gfx") > 0)
|
||||
if(config_json.count("renderer") > 0)
|
||||
{
|
||||
vsync = config_json["gfx"]["vsync"];
|
||||
fullscreen = config_json["gfx"]["fullscreen"];
|
||||
max_fps = config_json["gfx"]["max_fps"];
|
||||
vsync = config_json["renderer"]["vsync"];
|
||||
fullscreen = config_json["renderer"]["fullscreen"];
|
||||
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["keyboard_map"]["stick(x, +)"];
|
||||
keyboard_map["stick(y, -)"] = config_json["keyboard_map"]["stick(y, -)"];
|
||||
keyboard_map["stick(y, +)"] = config_json["keyboard_map"]["stick(y, +)"];
|
||||
keyboard_map["stick(x, -)"] = config_json["input"]["keyboard"]["stick(x, -)"];
|
||||
keyboard_map["stick(x, +)"] = config_json["input"]["keyboard"]["stick(x, +)"];
|
||||
keyboard_map["stick(y, -)"] = config_json["input"]["keyboard"]["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["b"] = config_json["keyboard_map"]["b"];
|
||||
keyboard_map["a"] = config_json["input"]["keyboard"]["a"];
|
||||
keyboard_map["b"] = config_json["input"]["keyboard"]["b"];
|
||||
|
||||
keyboard_map["l"] = config_json["keyboard_map"]["l"];
|
||||
keyboard_map["r"] = config_json["keyboard_map"]["r"];
|
||||
keyboard_map["z"] = config_json["keyboard_map"]["z"];
|
||||
keyboard_map["l"] = config_json["input"]["keyboard"]["l"];
|
||||
keyboard_map["r"] = config_json["input"]["keyboard"]["r"];
|
||||
keyboard_map["z"] = config_json["input"]["keyboard"]["z"];
|
||||
|
||||
keyboard_map["cbtn(x, -)"] = config_json["keyboard_map"]["cbtn(x, -)"];
|
||||
keyboard_map["cbtn(x, +)"] = config_json["keyboard_map"]["cbtn(x, +)"];
|
||||
keyboard_map["cbtn(y, -)"] = config_json["keyboard_map"]["cbtn(y, -)"];
|
||||
keyboard_map["cbtn(y, +)"] = config_json["keyboard_map"]["cbtn(y, +)"];
|
||||
keyboard_map["cbtn(x, -)"] = config_json["input"]["keyboard"]["cbtn(x, -)"];
|
||||
keyboard_map["cbtn(x, +)"] = config_json["input"]["keyboard"]["cbtn(x, +)"];
|
||||
keyboard_map["cbtn(y, -)"] = config_json["input"]["keyboard"]["cbtn(y, -)"];
|
||||
keyboard_map["cbtn(y, +)"] = config_json["input"]["keyboard"]["cbtn(y, +)"];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -101,34 +101,35 @@ namespace openrayman
|
|||
nlohmann::json config_json;
|
||||
config_json["game"] = game;
|
||||
|
||||
config_json["gfx"] = nlohmann::json::object();
|
||||
config_json["renderer"] = nlohmann::json::object();
|
||||
|
||||
config_json["gfx"]["vsync"] = vsync;
|
||||
config_json["gfx"]["fullscreen"] = fullscreen;
|
||||
config_json["gfx"]["max_fps"] = max_fps;
|
||||
config_json["renderer"]["vsync"] = vsync;
|
||||
config_json["renderer"]["fullscreen"] = fullscreen;
|
||||
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["keyboard_map"]["stick(x, +)"] = keyboard_map["stick(x, +)"];
|
||||
config_json["keyboard_map"]["stick(y, -)"] = keyboard_map["stick(y, -)"];
|
||||
config_json["keyboard_map"]["stick(y, +)"] = keyboard_map["stick(y, +)"];
|
||||
config_json["input"]["keyboard"]["stick(x, -)"] = keyboard_map["stick(x, -)"];
|
||||
config_json["input"]["keyboard"]["stick(x, +)"] = keyboard_map["stick(x, +)"];
|
||||
config_json["input"]["keyboard"]["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["keyboard_map"]["b"] = keyboard_map["b"];
|
||||
config_json["input"]["keyboard"]["a"] = keyboard_map["a"];
|
||||
config_json["input"]["keyboard"]["b"] = keyboard_map["b"];
|
||||
|
||||
config_json["keyboard_map"]["l"] = keyboard_map["l"];
|
||||
config_json["keyboard_map"]["r"] = keyboard_map["r"];
|
||||
config_json["keyboard_map"]["z"] = keyboard_map["z"];
|
||||
config_json["input"]["keyboard"]["l"] = keyboard_map["l"];
|
||||
config_json["input"]["keyboard"]["r"] = keyboard_map["r"];
|
||||
config_json["input"]["keyboard"]["z"] = keyboard_map["z"];
|
||||
|
||||
config_json["keyboard_map"]["cbtn(x, -)"] = keyboard_map["cbtn(x, -)"];
|
||||
config_json["keyboard_map"]["cbtn(x, +)"] = keyboard_map["cbtn(x, +)"];
|
||||
config_json["keyboard_map"]["cbtn(y, -)"] = keyboard_map["cbtn(y, -)"];
|
||||
config_json["keyboard_map"]["cbtn(y, +)"] = keyboard_map["cbtn(y, +)"];
|
||||
config_json["input"]["keyboard"]["cbtn(x, -)"] = keyboard_map["cbtn(x, -)"];
|
||||
config_json["input"]["keyboard"]["cbtn(x, +)"] = keyboard_map["cbtn(x, +)"];
|
||||
config_json["input"]["keyboard"]["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);
|
||||
if(config_stream.is_open())
|
||||
|
|
|
@ -72,7 +72,7 @@ namespace openrayman
|
|||
// Provide more complete info for the user.
|
||||
std::stringstream title;
|
||||
title << "OpenRayman " << openrayman::version << " "
|
||||
<< (this_platform == platform::windows ? "Win32" : "Linux")
|
||||
<< map_platform(this_platform)
|
||||
<< " (OpenGL " << gl_major << "." << gl_minor << ")"
|
||||
<< " (Game \"" << load_game << "\")";
|
||||
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")))
|
||||
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();
|
||||
input_provider& provider = m_window.create_input_provider();
|
||||
while(!m_exit_requested)
|
||||
{
|
||||
double current_timer_value = m_backend_specifics.time();
|
||||
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_total_time += m_current_delta_time;
|
||||
m_last_timer_value = current_timer_value;
|
||||
|
@ -99,6 +101,7 @@ namespace openrayman
|
|||
|
||||
m_window.poll_events();
|
||||
m_last_input = m_current_input;
|
||||
|
||||
const input_state& st = provider.poll();
|
||||
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))
|
||||
|
@ -112,10 +115,10 @@ namespace openrayman
|
|||
m_fps = m_accumulated_frames_fps;
|
||||
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_accumulated_time_fixed -= 1 / 60.0;
|
||||
m_total_timed_updates++;
|
||||
m_accumulated_time_timed -= 1 / 60.0;
|
||||
}
|
||||
|
||||
m_window.present();
|
||||
|
|
38
src/engine.h
38
src/engine.h
|
@ -13,13 +13,16 @@
|
|||
#include <GL/gl3w.h>
|
||||
#include <config/config.h>
|
||||
#include <game.h>
|
||||
#include <game_controller.h>
|
||||
#include <renderer/renderer.h>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
namespace openrayman
|
||||
{
|
||||
// Core of OpenRayman. Handles timing, input and GL contexts.
|
||||
// The engine controls game initialization, timing and renderer/input/window management.
|
||||
class engine
|
||||
{
|
||||
public:
|
||||
|
@ -36,12 +39,14 @@ public:
|
|||
m_last_timer_value(0),
|
||||
m_current_delta_time(0),
|
||||
m_total_time(0),
|
||||
m_accumulated_time_fixed(0),
|
||||
m_accumulated_time_timed(0),
|
||||
m_accumulated_time_fps(0),
|
||||
m_total_frames(0),
|
||||
m_total_fixed_updates(0),
|
||||
m_total_timed_updates(0),
|
||||
m_accumulated_frames_fps(0),
|
||||
m_fps(0),
|
||||
m_game(nullptr),
|
||||
m_game_controller(nullptr),
|
||||
#ifdef LIBRETRO_CORE
|
||||
m_window(*(new libretro_window()))
|
||||
#else
|
||||
|
@ -70,13 +75,13 @@ public:
|
|||
}
|
||||
|
||||
// Returns a reference to the engine window.
|
||||
inline window& current_window() const
|
||||
inline window& active_window() const
|
||||
{
|
||||
return m_window;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
@ -111,40 +116,49 @@ public:
|
|||
return m_total_frames;
|
||||
}
|
||||
|
||||
// Returns the total amount of fixed updates that have passed since the start of the game.
|
||||
inline std::uint64_t total_fixed_updates() const
|
||||
// Returns the total amount of timed updates that have passed since the start of the game.
|
||||
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.
|
||||
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
|
||||
return (m_fps + (1 / m_current_delta_time)) / 2;
|
||||
}
|
||||
|
||||
// Returns a reference to the active config.
|
||||
inline const config& current_config() const
|
||||
inline const config& active_config() const
|
||||
{
|
||||
return m_config;
|
||||
}
|
||||
|
||||
// Returns a reference to the active game.
|
||||
inline const game& current_game() const
|
||||
inline const game& active_game() const
|
||||
{
|
||||
return *m_game;
|
||||
}
|
||||
|
||||
// Returns a reference to the active game controller.
|
||||
inline game_controller& active_game_controller()
|
||||
{
|
||||
return *m_game_controller;
|
||||
}
|
||||
private:
|
||||
window& m_window;
|
||||
backend_specifics& m_backend_specifics;
|
||||
input_state m_current_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;
|
||||
std::uint64_t m_total_frames, m_total_fixed_updates, m_accumulated_frames_fps, m_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_timed_updates, m_accumulated_frames_fps, m_fps;
|
||||
config m_config;
|
||||
std::unique_ptr<game> m_game;
|
||||
renderer m_renderer;
|
||||
std::unique_ptr<game_controller> m_game_controller;
|
||||
bool m_exit_requested;
|
||||
};
|
||||
}
|
||||
|
|
30
src/game_controller.cc
Normal file
30
src/game_controller.cc
Normal 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
46
src/game_controller.h
Normal 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
|
31
src/info.h
31
src/info.h
|
@ -2,6 +2,7 @@
|
|||
#define INFO_H
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
namespace openrayman
|
||||
{
|
||||
|
@ -12,18 +13,32 @@ namespace openrayman
|
|||
const int version_code = 10;
|
||||
|
||||
// Platform definitions.
|
||||
#ifdef linux
|
||||
#undef linux
|
||||
#endif
|
||||
enum class platform { windows, linux };
|
||||
enum class platform { win32, linux_desktop, libretro, android };
|
||||
|
||||
inline std::string map_platform(platform p)
|
||||
{
|
||||
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).
|
||||
// Assume we're on linux unless _WIN32 is specified, as those are the only two platforms we support.
|
||||
// TODO: platform for libretro?!?
|
||||
// Assume we're on linux unless _WIN32 is specified, as those are the only two platforms we support right now.
|
||||
#ifdef _WIN32
|
||||
const platform this_platform = platform::windows;
|
||||
const platform this_platform = platform::win32;
|
||||
#else
|
||||
const platform this_platform = platform::linux;
|
||||
const platform this_platform = platform::linux_desktop;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -80,7 +80,7 @@ int main(int argc, char** argv)
|
|||
return openrayman::gf_tools::print_info(path);
|
||||
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::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 << " \"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 << " --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;
|
||||
}
|
||||
if(str == "--version")
|
||||
|
@ -136,7 +136,7 @@ int main(int argc, char** argv)
|
|||
}
|
||||
}
|
||||
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;
|
||||
openrayman::engine engine;
|
||||
return engine.run(selected_game, selected_install_folder);
|
||||
|
|
48
src/math/math.h
Normal file
48
src/math/math.h
Normal 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
30
src/renderer/bind_guard.h
Normal 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
102
src/renderer/buf.cc
Normal 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
93
src/renderer/buf.h
Normal 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
12
src/renderer/renderer.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
#ifndef RENDERER_H
|
||||
#define RENDERER_H
|
||||
|
||||
namespace openrayman
|
||||
{
|
||||
class renderer
|
||||
{
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
136
src/renderer/texture.cc
Normal file
136
src/renderer/texture.cc
Normal 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
89
src/renderer/texture.h
Normal 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
|
|
@ -8,7 +8,7 @@ namespace openrayman
|
|||
|
||||
sdl_window::sdl_window(config& config) :
|
||||
m_window(nullptr),
|
||||
m_current_fullscreen(false),
|
||||
m_is_fullscreen(false),
|
||||
m_vsync_enabled(false),
|
||||
m_wants_close(false),
|
||||
m_input_provider(config)
|
||||
|
@ -64,7 +64,7 @@ namespace openrayman
|
|||
SDL_GL_SetSwapInterval(0);
|
||||
SDL_ShowCursor(SDL_DISABLE);
|
||||
|
||||
m_current_fullscreen = initial_fullscreen;
|
||||
m_is_fullscreen = initial_fullscreen;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -166,7 +166,7 @@ namespace openrayman
|
|||
|
||||
void sdl_window::set_size(int w, int h)
|
||||
{
|
||||
if(!m_current_fullscreen)
|
||||
if(!m_is_fullscreen)
|
||||
SDL_SetWindowSize(m_window, w, h);
|
||||
}
|
||||
|
||||
|
@ -184,10 +184,10 @@ namespace openrayman
|
|||
|
||||
void sdl_window::set_fullscreen(bool fullscreen)
|
||||
{
|
||||
if(fullscreen == m_current_fullscreen)
|
||||
if(fullscreen == m_is_fullscreen)
|
||||
return;
|
||||
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)
|
||||
|
|
|
@ -42,7 +42,7 @@ public:
|
|||
private:
|
||||
SDL_Window* m_window;
|
||||
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;
|
||||
|
||||
static int m_sdl_ref_count;
|
||||
|
|
Loading…
Reference in a new issue