diff --git a/data/mesh/small_atomic_bomb.scad b/data/mesh/small_atomic_bomb.scad new file mode 100644 index 0000000..3b86498 --- /dev/null +++ b/data/mesh/small_atomic_bomb.scad @@ -0,0 +1,45 @@ +$fn = 12; + +offset = 1.0; + +r_inner = 0.2; +r_outer = 0.5; + +rotate([0,0,180]) { + +hull() { + // hull around the small base + translate([offset, 0, 0]) sphere(r_inner); + + // and the thick head + sphere(r_outer); +} + +pipe_len=0.3; +translate([offset, 0, 0]) { + rotate([0,90,0]) cylinder(r=r_inner, pipe_len); +} + +// fins +w = 0.5; +h = 0.8 * r_outer; + +difference() { + // fin + for (i=[0:4]) { + rotate([i*90,0,0]) { + translate([offset-w/2+pipe_len,0,-h/2]) { + hull() { + cube([w,0.05,0.01], center=true); + rotate([0,-5,0]) { + cube([w-0.2,0.05,h+0.15], center=true); + } + } + } + } + } + + // cutout part near end of pipe + //translate([pipe_len+offset,0,0]) sphere(0.25); +} +} diff --git a/game/CMakeLists.txt b/game/CMakeLists.txt index e775454..bf6965b 100644 --- a/game/CMakeLists.txt +++ b/game/CMakeLists.txt @@ -3,6 +3,7 @@ set(CMAKE_INCLUDE_CURRENT_DIRS ON) find_package(OpenGL REQUIRED) find_package(epoxy REQUIRED) find_package(X11 REQUIRED) +find_package(assimp REQUIRED) set(GAME_SRC main.cpp @@ -34,7 +35,9 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}) include_directories(${OPENGL_INCLUDE_DIR}) include_directories(${CMAKE_SOURCE_DIR}/libs/glm/) include_directories(${CMAKE_SOURCE_DIR}/libs/asio/asio/include/) +include_directories(${assimp_INCLUDE_DIRS}) add_executable(game ${GAME_SRC}) setup_target(game) -target_link_libraries(game X11 epoxy pthread) + +target_link_libraries(game X11 epoxy pthread ${assimp_LIBRARIES} assimp) diff --git a/game/game_window.hpp b/game/game_window.hpp index 940a6e2..f48147a 100644 --- a/game/game_window.hpp +++ b/game/game_window.hpp @@ -20,16 +20,20 @@ class GameWindow : public endofthejedi::GLWindow { private: Game* m_game; - //endofthejedi::RendererPolygon2d m_renderer; + endofthejedi::RendererPolygon2d m_renderer; //endofthejedi::RendererPolygon3d m_renderer; - endofthejedi::RendererRayTracer m_renderer; + //endofthejedi::RendererRayTracer m_renderer; protected: void init() override { + std::cout<<"init" << std::endl; + glClearColor(0.5f, 0.6f, 0.7f, 1.0f); resize(); glEnable(GL_DEPTH_TEST); + + m_renderer.setup(); } void render(double time) override { diff --git a/game/glclasses.cpp b/game/glclasses.cpp index 0733c47..453d03e 100644 --- a/game/glclasses.cpp +++ b/game/glclasses.cpp @@ -3,75 +3,87 @@ #include #include -endofthejedi::VAO::VAO() { glGenVertexArrays(1, &m_name); } +namespace endofthejedi { + VAO::VAO() { glGenVertexArrays(1, &m_name); } -endofthejedi::VAO::~VAO() { glDeleteVertexArrays(1, &m_name); } + VAO::~VAO() { glDeleteVertexArrays(1, &m_name); } -void endofthejedi::VAO::bind() { glBindVertexArray(m_name); } + void VAO::bind() { glBindVertexArray(m_name); } -void endofthejedi::VAO::fill(GLuint index, GLint size, GLenum type, - GLboolean normalized, GLsizei stride, - const GLvoid *pointer) { - glEnableVertexAttribArray(index); - glVertexAttribPointer(index, size, GL_FLOAT, normalized, stride, pointer); -} + void VAO::fill(GLuint index, GLint size, GLenum type, + GLboolean normalized, GLsizei stride, + const GLvoid *pointer) + { + (void) type; -endofthejedi::Shader::Shader() { m_program = glCreateProgram(); } - -endofthejedi::Shader::~Shader() {} - -bool endofthejedi::Shader::check() { - GLint len = 0; - GLint result = 0; - - glGetProgramiv(m_program, GL_LINK_STATUS, &result); - glGetProgramiv(m_program, GL_INFO_LOG_LENGTH, &len); - if (len > 1) { - char *error = (char *)malloc(len); - glGetProgramInfoLog(m_program, len, NULL, error); - std::string str(error); - std::cout << str << "\n"; + glEnableVertexAttribArray(index); + glVertexAttribPointer(index, size, GL_FLOAT, normalized, stride, pointer); } - std::cout << "checked program" - << "\n"; - return (bool)result; -} + Shader::Shader() : m_program(0) { } -bool endofthejedi::Shader::checkShader(GLuint shader) { - GLint len = 0; - GLint result = 0; + Shader::~Shader() {} - glGetShaderiv(shader, GL_COMPILE_STATUS, &result); - glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len); - if (len > 1) { - char *error = (char *)malloc(len+1); - glGetShaderInfoLog(shader, 0, &len, error); - std::string str(error, error + len); - std::cout << str << "\n"; + void Shader::init() { m_program = glCreateProgram(); } + + bool Shader::check() { + GLint len = 0; + GLint result = 0; + + glGetProgramiv(m_program, GL_LINK_STATUS, &result); + glGetProgramiv(m_program, GL_INFO_LOG_LENGTH, &len); + if (len > 1) { + char *error = (char *)malloc(len); + glGetProgramInfoLog(m_program, len, NULL, error); + std::string str(error); + std::cout << str << std::endl; + } + std::cout << "checked program" << std::endl; + + return (bool)result; } - std::cout << "checked shader" - << "\n"; - return (bool)result; -} - -void endofthejedi::Shader::bind() { glUseProgram(m_program); } - -void endofthejedi::Shader::unbind() { glUseProgram(0); } - -void endofthejedi::Shader::load(std::string path, GLenum shadertype) { - GLuint cheddar = glCreateShader(shadertype); - const char *cheddardata = path.c_str(); - glShaderSource(cheddar, 1, &cheddardata, NULL); - glCompileShader(cheddar); - checkShader(cheddar); - glAttachShader(m_program, cheddar); - glLinkProgram(m_program); - check(); - glDeleteShader(cheddar); -} - -GLuint endofthejedi::Shader::location(std::string name) { - return glGetUniformLocation(m_program, name.c_str()); + bool Shader::checkShader(GLuint shader) { + GLint len = 0; + GLint result = 0; + + glGetShaderiv(shader, GL_COMPILE_STATUS, &result); + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len); + if (len > 1) { + char *error = (char *)malloc(len+1); + glGetShaderInfoLog(shader, 0, &len, error); + std::string str(error, error + len); + std::cout << str << std::endl; + } + + std::cout << "checked shader" << std::endl; + + return (bool)result; + } + + void Shader::bind() { glUseProgram(m_program); } + + void Shader::unbind() { glUseProgram(0); } + + void Shader::load(const std::string &path, GLenum shadertype) { + if (m_program == 0) { + std::cout<<"[shader] error: shader program is invalid (0)!" << std::endl; + exit(-1); + return; + } + + GLuint cheddar = glCreateShader(shadertype); + const char *cheddardata = path.c_str(); + glShaderSource(cheddar, 1, &cheddardata, NULL); + glCompileShader(cheddar); + checkShader(cheddar); + glAttachShader(m_program, cheddar); + glLinkProgram(m_program); + check(); + glDeleteShader(cheddar); + } + + GLuint Shader::location(const std::string &name) { + return glGetUniformLocation(m_program, name.c_str()); + } } diff --git a/game/glclasses.hpp b/game/glclasses.hpp index 7280425..9f9d693 100644 --- a/game/glclasses.hpp +++ b/game/glclasses.hpp @@ -43,6 +43,7 @@ class VAO { class Shader { private: GLuint m_program; + bool check(); bool checkShader(GLuint shader); @@ -50,10 +51,12 @@ class Shader { public: Shader(); ~Shader(); + void init(); // call to init when opengl context exists + void bind(); void unbind(); - void load(std::string data, GLenum shadertype); - GLuint location(std::string name); + void load(const std::string &data, GLenum shadertype); + GLuint location(const std::string &name); }; } diff --git a/game/main.cpp b/game/main.cpp index ba0264d..707c21a 100644 --- a/game/main.cpp +++ b/game/main.cpp @@ -71,6 +71,7 @@ int main(int argc, char *argv[]) { GameWindow window(500, 500, &game); window.set_maxfps(60.0); + window.open(); while(window.running()){ window.poll(); diff --git a/game/opengl.cpp b/game/opengl.cpp index ee32a5c..5d370d6 100644 --- a/game/opengl.cpp +++ b/game/opengl.cpp @@ -19,117 +19,126 @@ timespec diff(timespec start, timespec end) { return result; } -endofthejedi::GLWindow::GLWindow(unsigned int width, unsigned int height) - : m_width(width), m_height(height) { - m_display = XOpenDisplay(NULL); +namespace endofthejedi { - if (m_display == NULL) { - throw std::runtime_error("XOpenDisplay NULL"); + GLWindow::GLWindow(unsigned int width, unsigned int height) : m_width(width), m_height(height) + { } - m_rootwnd = DefaultRootWindow(m_display); + void GLWindow::open() + { + m_display = XOpenDisplay(NULL); - m_visualinfo = glXChooseVisual(m_display, 0, m_attributes); + if (m_display == NULL) { + throw std::runtime_error("XOpenDisplay NULL"); + } - if (m_visualinfo == NULL) { - throw std::runtime_error("glXChooseVisual NULL"); + m_rootwnd = DefaultRootWindow(m_display); + + m_visualinfo = glXChooseVisual(m_display, 0, m_attributes); + + if (m_visualinfo == NULL) { + throw std::runtime_error("glXChooseVisual NULL"); + } + + m_colormap = + XCreateColormap(m_display, m_rootwnd, m_visualinfo->visual, AllocNone); + + m_swa.colormap = m_colormap; + m_swa.event_mask = ExposureMask | KeyPressMask; + + m_window = XCreateWindow( + m_display, m_rootwnd, 0, 0, m_width, m_height, 0, m_visualinfo->depth, + InputOutput, m_visualinfo->visual, CWColormap | CWEventMask, &m_swa); + + m_atomWmDeleteWindow = XInternAtom(m_display, "WM_DELETE_WINDOW", False); + XSetWMProtocols(m_display, m_window, &m_atomWmDeleteWindow, 1); + + XMapWindow(m_display, m_window); + XStoreName(m_display, m_window, "NAME"); + + m_glcontext = glXCreateContext(m_display, m_visualinfo, NULL, GL_TRUE); + + XSync(m_display, False); + + glXMakeCurrent(m_display, m_window, m_glcontext); + + std::cout << "GL Renderer: " << glGetString(GL_RENDERER) << "\n"; + std::cout << "GL Version: " << glGetString(GL_VERSION) << "\n"; + std::cout << "GLSL Version: " << glGetString(GL_SHADING_LANGUAGE_VERSION) + << "\n"; + + m_running = true; + + init(); + + XClearWindow(m_display, m_window); + XMapRaised(m_display, m_window); } - m_colormap = - XCreateColormap(m_display, m_rootwnd, m_visualinfo->visual, AllocNone); - - m_swa.colormap = m_colormap; - m_swa.event_mask = ExposureMask | KeyPressMask; - - m_window = XCreateWindow( - m_display, m_rootwnd, 0, 0, width, height, 0, m_visualinfo->depth, - InputOutput, m_visualinfo->visual, CWColormap | CWEventMask, &m_swa); - - m_atomWmDeleteWindow = XInternAtom(m_display, "WM_DELETE_WINDOW", False); - XSetWMProtocols(m_display, m_window, &m_atomWmDeleteWindow, 1); - - XMapWindow(m_display, m_window); - XStoreName(m_display, m_window, "NAME"); - - m_glcontext = glXCreateContext(m_display, m_visualinfo, NULL, GL_TRUE); - - XSync(m_display, False); - - glXMakeCurrent(m_display, m_window, m_glcontext); - - std::cout << "GL Renderer: " << glGetString(GL_RENDERER) << "\n"; - std::cout << "GL Version: " << glGetString(GL_VERSION) << "\n"; - std::cout << "GLSL Version: " << glGetString(GL_SHADING_LANGUAGE_VERSION) - << "\n"; - - init(); - - XClearWindow(m_display, m_window); - XMapRaised(m_display, m_window); -} - -endofthejedi::GLWindow::~GLWindow() { - glXMakeCurrent(m_display, None, NULL); - glXDestroyContext(m_display, m_glcontext); - XFree(m_visualinfo); - XDestroyWindow(m_display, m_window); - XCloseDisplay(m_display); -} - -void endofthejedi::GLWindow::handleevents() { - if (XPending(m_display) > 0) { - XEvent xev; - XNextEvent(m_display, &xev); - handle(xev); + GLWindow::~GLWindow() { + glXMakeCurrent(m_display, None, NULL); + glXDestroyContext(m_display, m_glcontext); + XFree(m_visualinfo); + XDestroyWindow(m_display, m_window); + XCloseDisplay(m_display); } -} -void endofthejedi::GLWindow::handle(XEvent event) { - if (event.type == Expose) { - XWindowAttributes attribs; - XGetWindowAttributes(m_display, m_window, &attribs); - m_width = attribs.width; - m_height = attribs.height; - resize(); - } else if (event.type == ClientMessage) { - if (event.xclient.data.l[0] == m_atomWmDeleteWindow) { + void GLWindow::handleevents() { + if (XPending(m_display) > 0) { + XEvent xev; + XNextEvent(m_display, &xev); + handle(xev); + } + } + + void GLWindow::handle(XEvent event) { + if (event.type == Expose) { + XWindowAttributes attribs; + XGetWindowAttributes(m_display, m_window, &attribs); + m_width = attribs.width; + m_height = attribs.height; + resize(); + } else if (event.type == ClientMessage) { + if (event.xclient.data.l[0] == m_atomWmDeleteWindow) { + stop(); + } + } else if (event.type == DestroyNotify) { stop(); } - } else if (event.type == DestroyNotify) { - stop(); } + + void GLWindow::swap() { glXSwapBuffers(m_display, m_window); } + + void GLWindow::poll() { + + clock_gettime(CLOCK_MONOTONIC_RAW, &prev); + clock_gettime(CLOCK_MONOTONIC_RAW, ¤t); + + handleevents(); + render(delta); + swap(); + + if (m_maxfps > 0) { + sleeptime = ((1000000000.0/m_maxfps) - delta) + sleeptime; + if (sleeptime > 0.0) { + usleep((unsigned int)(sleeptime/1000.0)); + sleeptime = sleeptime - (unsigned int)(sleeptime/1000.0); + } + } + + clock_gettime(CLOCK_MONOTONIC_RAW, ¤t); + delta = diff(prev, current).tv_nsec; + prev = current; + if(delta > 0.0) { + m_fps = (1000000000.0/delta); + //std::cout << m_fps << "\n"; + } + } + + void GLWindow::stop() { m_running = false; } + bool GLWindow::running() { return m_running; } + void GLWindow::init() { } + void GLWindow::render(double time) { UNUSED(time) } + void GLWindow::resize() {} } - -void endofthejedi::GLWindow::swap() { glXSwapBuffers(m_display, m_window); } - -void endofthejedi::GLWindow::poll() { - - clock_gettime(CLOCK_MONOTONIC_RAW, &prev); - clock_gettime(CLOCK_MONOTONIC_RAW, ¤t); - - handleevents(); - render(delta); - swap(); - - if (m_maxfps > 0) { - sleeptime = ((1000000000.0/m_maxfps) - delta) + sleeptime; - if (sleeptime > 0.0) { - usleep((unsigned int)(sleeptime/1000.0)); - sleeptime = sleeptime - (unsigned int)(sleeptime/1000.0); - } - } - - clock_gettime(CLOCK_MONOTONIC_RAW, ¤t); - delta = diff(prev, current).tv_nsec; - prev = current; - if(delta > 0.0) { - m_fps = (1000000000.0/delta); - //std::cout << m_fps << "\n"; - } -} - -void endofthejedi::GLWindow::stop() { m_running = false; } -bool endofthejedi::GLWindow::running() { return m_running; } -void endofthejedi::GLWindow::init() { m_running = true; } -void endofthejedi::GLWindow::render(double time) { UNUSED(time) } -void endofthejedi::GLWindow::resize() {} diff --git a/game/opengl.hpp b/game/opengl.hpp index 7a8888f..420b9b9 100644 --- a/game/opengl.hpp +++ b/game/opengl.hpp @@ -67,10 +67,14 @@ namespace endofthejedi { virtual void handleevents(); public: - //initializes the X Window & creates an OpenGL context + // create the class. call open() afterwards GLWindow(unsigned int width, unsigned int height); + ~GLWindow(); + //initializes the X Window & creates an OpenGL context + void open(); + //mainloop does event handling & calls render/swap void poll(); void swap(); diff --git a/game/renderer_polygon_2d/renderer_polygon_2d.cpp b/game/renderer_polygon_2d/renderer_polygon_2d.cpp index 69ef800..af57648 100644 --- a/game/renderer_polygon_2d/renderer_polygon_2d.cpp +++ b/game/renderer_polygon_2d/renderer_polygon_2d.cpp @@ -1,6 +1,15 @@ #include "renderer_polygon_2d.hpp" namespace endofthejedi { + void RendererPolygon2d::setup() + { + // (dark grey) bg + float r = 0.1; + float g = 0.1; + float b = 0.1; + glClearColor(r, g, b, 1.0); + } + void RendererPolygon2d::drawCircle(float x, float y, float radius, float r, float g, float b, int numSides) { diff --git a/game/renderer_polygon_2d/renderer_polygon_2d.hpp b/game/renderer_polygon_2d/renderer_polygon_2d.hpp index f1c227c..3376ec3 100644 --- a/game/renderer_polygon_2d/renderer_polygon_2d.hpp +++ b/game/renderer_polygon_2d/renderer_polygon_2d.hpp @@ -15,7 +15,7 @@ namespace endofthejedi { - class RendererPolygon2d : Renderer { + class RendererPolygon2d : public Renderer { private: void drawCircle(float x, float y, float radius, float r, float g, float b, int numSides = 12); @@ -27,7 +27,9 @@ namespace endofthejedi { void drawExplosion(const game::Explosion *explosion); protected: + public: + void setup() override; void render(const game::State *state) override; }; diff --git a/game/renderer_polygon_3d/polygon_model.cpp b/game/renderer_polygon_3d/polygon_model.cpp new file mode 100644 index 0000000..e69de29 diff --git a/game/renderer_polygon_3d/polygon_model.hpp b/game/renderer_polygon_3d/polygon_model.hpp new file mode 100644 index 0000000..2b1d155 --- /dev/null +++ b/game/renderer_polygon_3d/polygon_model.hpp @@ -0,0 +1,116 @@ +#pragma once + +#include +#include + +#include // C++ importer interface +#include // Output data structure +#include // Post processing flags + +#include +#include + +class PolygonModel { + public: + PolygonModel(const std::string &filename) : m_filename(filename) + { + if (!import()) { + m_loaded = false; + + } else { + m_loaded = true; + } + } + + bool import() + { + // Create an instance of the Importer class + Assimp::Importer importer; + // And have it read the given file with some example postprocessing + // Usually - if speed is not the most important aspect for you - you'll + // propably to request more postprocessing than we do in this example. + const aiScene* scene = importer.ReadFile(m_filename, + aiProcess_CalcTangentSpace | + aiProcess_Triangulate | + aiProcess_JoinIdenticalVertices | + aiProcess_SortByPType); + + // If the import failed, report it + if (!scene) { + std::cout<<"[polygonmodel] loading file " + << m_filename << " failed with: " + << importer.GetErrorString() << std::endl; + + return false; + } + + // Now we can access the file's contents. + copyVertices(scene); + + // We're done. Everything will be cleaned up by the importer destructor + return true; + } + + private: + bool copyVertices(const aiScene *scene) + { + if (scene->mMeshes == 0) { + std::cout << "[polygonmodel : no meshes loaded for " << m_filename << std::endl; + return false; + } + + aiNode *node = scene->mRootNode; + const aiMesh* mesh = scene->mMeshes[node->mMeshes[0]]; + + // 3 vertices per face, 3 floats per vertex + m_numVertices = mesh->mNumFaces*3; + + m_data_position.reserve(m_numVertices); + m_data_normal.reserve(m_numVertices); + + size_t t, i; + for (t=0; tmNumFaces; ++t) { + const aiFace* face = &mesh->mFaces[t]; + if (face->mNumIndices != 3) { + std::cout << "[polygonmodel] need triangles, got something different with: " + << face->mNumIndices << " vertices" << std::endl; + + return false; + } + + for (i=0; imNumIndices; i++) { + const size_t index = face->mIndices[i]; + m_data_position.push_back(mesh->mVertices[index].x); + m_data_position.push_back(mesh->mVertices[index].y); + m_data_position.push_back(mesh->mVertices[index].z); + + m_data_normal.push_back(mesh->mNormals[index].x); + m_data_normal.push_back(mesh->mNormals[index].y); + m_data_normal.push_back(mesh->mNormals[index].z); + } + } + + size_t totalBytes = 3*m_numVertices*sizeof(float); + std::cout<<"[polygonmodel] loaded " << m_numVertices << " vertices (" + << totalBytes << " bytes)" << std::endl; + + return true; + } + + const std::string &filename() const { return m_filename; } + bool ready() const { return m_loaded; } + + private: + std::string m_filename; + bool m_loaded; + + size_t m_numVertices; + + // both will hold 3 * numVertices floats + std::vector m_data_position; + std::vector m_data_normal; + + GLuint m_vbo_id_position; + GLuint m_vbo_id_normal; +}; + diff --git a/game/renderer_polygon_3d/renderer_polygon_3d.cpp b/game/renderer_polygon_3d/renderer_polygon_3d.cpp index 8d5590c..c2791d7 100644 --- a/game/renderer_polygon_3d/renderer_polygon_3d.cpp +++ b/game/renderer_polygon_3d/renderer_polygon_3d.cpp @@ -2,16 +2,21 @@ #include +#include "polygon_model.hpp" + namespace endofthejedi { void RendererPolygon3d::setup() { std::cout<<"setup 3d" << std::endl; + + PolygonModel atomicBomb("../data/mesh/small_atomic_bomb.stl"); + exit(-1); } void RendererPolygon3d::render(const game::State *state) { (void) state; - //std::cout<<"render 3d" << std::endl; + std::cout<<"render 3d" << std::endl; } } diff --git a/game/renderer_polygon_3d/renderer_polygon_3d.hpp b/game/renderer_polygon_3d/renderer_polygon_3d.hpp index c959fcf..c0685f1 100644 --- a/game/renderer_polygon_3d/renderer_polygon_3d.hpp +++ b/game/renderer_polygon_3d/renderer_polygon_3d.hpp @@ -12,14 +12,18 @@ #include "state/ship.hpp" #include "state/explosion.hpp" +class PolygonModel; + namespace endofthejedi { - class RendererPolygon3d : Renderer { + class RendererPolygon3d : public Renderer { private: protected: public: void setup(); void render(const game::State *state) override; - }; + private: + std::vector m_models; + }; } diff --git a/game/renderer_ray_tracer/renderer_ray_tracer.cpp b/game/renderer_ray_tracer/renderer_ray_tracer.cpp index 8987259..ed89c70 100644 --- a/game/renderer_ray_tracer/renderer_ray_tracer.cpp +++ b/game/renderer_ray_tracer/renderer_ray_tracer.cpp @@ -16,7 +16,13 @@ static const char *fss = namespace endofthejedi { - RendererRayTracer::RendererRayTracer() { + RendererRayTracer::RendererRayTracer() + { + } + + void RendererRayTracer::setup() + { + m_shader.init(); m_shader.load(vss, GL_VERTEX_SHADER); m_shader.load(fss, GL_FRAGMENT_SHADER); } diff --git a/game/renderer_ray_tracer/renderer_ray_tracer.hpp b/game/renderer_ray_tracer/renderer_ray_tracer.hpp index 243cd21..13bc95d 100644 --- a/game/renderer_ray_tracer/renderer_ray_tracer.hpp +++ b/game/renderer_ray_tracer/renderer_ray_tracer.hpp @@ -11,12 +11,14 @@ namespace endofthejedi { - class RendererRayTracer: Renderer { + class RendererRayTracer : public Renderer { private: Shader m_shader; protected: public: RendererRayTracer(); + + void setup() override; void render(const game::State *state) override; }; diff --git a/game/state/commands.cpp b/game/state/commands.cpp index 5e09db3..5f83dfd 100644 --- a/game/state/commands.cpp +++ b/game/state/commands.cpp @@ -13,7 +13,9 @@ namespace game { // TODO: idea // shoot multiple rockets at once or from different positions after // level up / upgrade ... - Missile *missile = new Missile(player, player->ship->position, util::deg2rad(m_angle), player->speed); + + // angles are supplied in degrees and are CCW + Missile *missile = new Missile(player, player->ship->position, -util::deg2rad(m_angle), player->speed); Trace *trace = new Trace(missile); missile->trace = trace;