OpenGL graphics acceleration support!

master
Vectozavr 2021-10-22 23:42:32 +07:00
parent 22c18968f6
commit e471795290
24 changed files with 307 additions and 74 deletions

View File

@ -113,3 +113,19 @@ if (SFML_FOUND)
endif() endif()
target_link_libraries(shooter sfml-audio sfml-network sfml-graphics sfml-window sfml-system) target_link_libraries(shooter sfml-audio sfml-network sfml-graphics sfml-window sfml-system)
# OpenGL part
if (APPLE OR UNIX)
set(GLEW_H /usr/local/Cellar/glew/2.1.0/include/GL)
set(GLFW_H /usr/local/Cellar/glfw/3.2.1/include/GLFW)
include_directories(${GLEW_H} ${GLFW_H})
set(GLEW_LINK /usr/local/Cellar/glew/2.1.0/lib/libGLEW.2.1.dylib)
set(GLFW_LINK /usr/local/Cellar/glfw/3.2.1/lib/libglfw.3.dylib)
link_libraries(${OPENGL} ${GLEW_LINK} ${GLFW_LINK})
target_link_libraries(shooter "-framework OpenGL")
target_link_libraries(shooter "-framework GLUT")
else()
endif()

View File

@ -8,7 +8,7 @@
#include "engine/utils/Log.h" #include "engine/utils/Log.h"
Player::Player() { Player::Player() {
loadObj(ShooterConsts::CUBE_OBJ, Vec3D{0.5, 1.9, 0.5}); loadObj(ShooterConsts::CUBE_OBJ, Vec3D{1.5, 1.9, 1.5});
setAcceleration(Vec3D{0, -ShooterConsts::GRAVITY, 0}); setAcceleration(Vec3D{0, -ShooterConsts::GRAVITY, 0});
setCollision(true); setCollision(true);
setVisible(false); setVisible(false);
@ -109,15 +109,17 @@ void Player::previousWeapon() {
} }
} }
void Player::fire() { bool Player::fire() {
auto camera = attached(ObjectNameTag("Camera")); auto camera = attached(ObjectNameTag("Camera"));
if(camera != nullptr) { if(camera != nullptr) {
auto damagedPlayers = _weapons[_selectedWeapon]->fire(_rayCastFunction, camera->position(), camera->lookAt()); auto fireInfo = _weapons[_selectedWeapon]->fire(_rayCastFunction, camera->position(), camera->lookAt());
for(auto& [damagedPlayerName, damage] : damagedPlayers) { for(auto& [damagedPlayerName, damage] : fireInfo.damagedPlayers) {
sf::Uint16 targetId = std::stoi(damagedPlayerName.str().substr(6)); sf::Uint16 targetId = std::stoi(damagedPlayerName.str().substr(6));
_damagePlayerCallBack(targetId, damage); _damagePlayerCallBack(targetId, damage);
} }
return fireInfo.shot;
} }
return false;
} }
void Player::reload() { void Player::reload() {

View File

@ -37,7 +37,7 @@ private:
std::function<void(std::shared_ptr<Weapon>)> _addWeaponCallBack; std::function<void(std::shared_ptr<Weapon>)> _addWeaponCallBack;
std::function<void(std::shared_ptr<Weapon>)> _removeWeaponCallBack; std::function<void(std::shared_ptr<Weapon>)> _removeWeaponCallBack;
std::function<std::pair<Vec3D, ObjectNameTag>(const Vec3D&, const Vec3D&)> _rayCastFunction; std::function<IntersectionInformation(const Vec3D&, const Vec3D&)> _rayCastFunction;
public: public:
Player(); Player();
@ -57,8 +57,10 @@ public:
void nextWeapon(); void nextWeapon();
void previousWeapon(); void previousWeapon();
void fire(); bool fire();
void reload(); void reload();
[[nodiscard]] ObjectNameTag weaponName() const { return _weapons[_selectedWeapon]->name(); }
std::shared_ptr<Weapon> weapon() { return _weapons[_selectedWeapon]; }
void rotateWeaponsRelativePoint(const Vec3D& point, const Vec3D& v, double val); void rotateWeaponsRelativePoint(const Vec3D& point, const Vec3D& v, double val);
@ -83,7 +85,7 @@ public:
void setRemoveWeaponCallBack(std::function<void(std::shared_ptr<Weapon>)> removeWeapon) { void setRemoveWeaponCallBack(std::function<void(std::shared_ptr<Weapon>)> removeWeapon) {
_removeWeaponCallBack = std::move(removeWeapon); _removeWeaponCallBack = std::move(removeWeapon);
} }
void setRayCastFunction(std::function<std::pair<Vec3D, ObjectNameTag>(const Vec3D&, const Vec3D&)> rayCastFunction) { void setRayCastFunction(std::function<IntersectionInformation(const Vec3D&, const Vec3D&)> rayCastFunction) {
_rayCastFunction = std::move(rayCastFunction); _rayCastFunction = std::move(rayCastFunction);
} }

View File

@ -9,6 +9,7 @@
#include "engine/animation/ATranslate.h" #include "engine/animation/ATranslate.h"
#include "engine/animation/ATranslateToPoint.h" #include "engine/animation/ATranslateToPoint.h"
#include "engine/animation/Timeline.h" #include "engine/animation/Timeline.h"
#include "engine/animation/ARotate.h"
#include "ShooterConsts.h" #include "ShooterConsts.h"
PlayerController::PlayerController(std::shared_ptr<Player> player, PlayerController::PlayerController(std::shared_ptr<Player> player,
@ -107,15 +108,24 @@ void PlayerController::update() {
SoundController::playSound(SoundTag("unSlowMo"), ShooterConsts::UN_SLOW_MO_SOUND); SoundController::playSound(SoundTag("unSlowMo"), ShooterConsts::UN_SLOW_MO_SOUND);
} }
if (Mouse::isButtonPressed(sf::Mouse::Button::Left)) {
bool shot = _player->fire();
if(shot) {
if(!_player->inCollision() && (_player->weaponName() == ObjectNameTag("shotgun")))
_player->addVelocity(-camera->lookAt() * 30 * coeff);
}
}
if (Keyboard::isKeyPressed(sf::Keyboard::Space) && _player->inCollision()) { if (Keyboard::isKeyPressed(sf::Keyboard::Space) && _player->inCollision()) {
// if we just want to jump, we have to add particular speed // if we just want to jump, we have to add particular speed
if (!_isSliding) if (!_isSliding)
_player->addVelocity(Vec3D{ 0, std::abs(_player->collisionNormal().y()) * sqrt(2 * -_player->acceleration().y() * ShooterConsts::JUMP_HEIGHT) * coeff, 0 }); _player->addVelocity(Vec3D{ 0, std::abs(_player->collisionNormal().y()) * sqrt(2 * -_player->acceleration().y() * ShooterConsts::JUMP_HEIGHT) * coeff, 0 });
// if we want to slide, we have to add speed * 60/fps to make it independent on frame rate // if we want to slide, we have to add speed * 60/fps to make it independent on frame rate
else else
_player->addVelocity(Vec3D{ 0, std::abs(_player->collisionNormal().y()) * sqrt(2 * -_player->acceleration().y() * ShooterConsts::JUMP_HEIGHT) * coeff * 60.0 / Time::fps(), 0 }); _player->addVelocity(Vec3D{ 0, std::abs(_player->collisionNormal().y()) * sqrt(2 * -_player->acceleration().y() * ShooterConsts::JUMP_HEIGHT) * coeff * Time::deltaTime() * 60, 0 });
_player->translate(Vec3D{ 0, Time::deltaTime() * ShooterConsts::WALK_SPEED * 2 * coeff * 60.0 / Time::fps(), 0 }); _player->translate(Vec3D{ 0, Time::deltaTime() * ShooterConsts::WALK_SPEED * 2 * coeff, 0 });
_isSliding = true; _isSliding = true;
} else { } else {
_isSliding = false; _isSliding = false;
@ -149,10 +159,6 @@ void PlayerController::update() {
_player->previousWeapon(); _player->previousWeapon();
} }
if (Mouse::isButtonPressed(sf::Mouse::Button::Left)) {
_player->fire();
}
if(Keyboard::isKeyPressed(sf::Keyboard::R)) { if(Keyboard::isKeyPressed(sf::Keyboard::R)) {
_player->reload(); _player->reload();
} }

View File

@ -59,3 +59,4 @@ GamePlay:
![Project demonstration](img/gamePlay5.png) ![Project demonstration](img/gamePlay5.png)
![Project demonstration](img/gamePlay6.png) ![Project demonstration](img/gamePlay6.png)
![Project demonstration](img/gamePlay7.png) ![Project demonstration](img/gamePlay7.png)
![Project demonstration](img/opengl.png)

View File

@ -81,7 +81,7 @@ void Shooter::start() {
// TODO: encapsulate call backs inside Player // TODO: encapsulate call backs inside Player
player->setAddTraceCallBack([this](const Vec3D& from, const Vec3D& to){ client->addTrace(from, to); addFireTrace(from, to); }); player->setAddTraceCallBack([this](const Vec3D& from, const Vec3D& to){ client->addTrace(from, to); addFireTrace(from, to); });
player->setDamagePlayerCallBack([this] (sf::Uint16 targetId, double damage) { client->damagePlayer(targetId, damage); }); player->setDamagePlayerCallBack([this] (sf::Uint16 targetId, double damage) { client->damagePlayer(targetId, damage); });
player->setRayCastFunction([this](const Vec3D& from, const Vec3D& to) { return world->rayCast(from, to, "Enemy"); }); player->setRayCastFunction([this](const Vec3D& from, const Vec3D& to) { return world->rayCast(from, to, "weapon Player"); });
player->setTakeBonusCallBack([this] (const string& bonusName) { client->takeBonus(bonusName); }); player->setTakeBonusCallBack([this] (const string& bonusName) { client->takeBonus(bonusName); });
player->setAddWeaponCallBack([this](std::shared_ptr<Weapon> weapon){ addWeapon(std::move(weapon)); }); player->setAddWeaponCallBack([this](std::shared_ptr<Weapon> weapon){ addWeapon(std::move(weapon)); });
player->setRemoveWeaponCallBack([this](std::shared_ptr<Weapon> weapon){ removeWeapon(std::move(weapon)); }); player->setRemoveWeaponCallBack([this](std::shared_ptr<Weapon> weapon){ removeWeapon(std::move(weapon)); });
@ -246,7 +246,7 @@ void Shooter::addFireTrace(const Vec3D &from, const Vec3D &to) {
world->addBody(std::make_shared<RigidBody>(Mesh::LineTo(from, to, 0.05)), ObjectNameTag(traceName)); world->addBody(std::make_shared<RigidBody>(Mesh::LineTo(from, to, 0.05)), ObjectNameTag(traceName));
world->body(ObjectNameTag(traceName))->setCollider(false); world->body(ObjectNameTag(traceName))->setCollider(false);
Timeline::animate(AnimationListTag(traceName + "_fadeOut"), new AColor(world->body(ObjectNameTag(traceName)), {255, 255, 255, 0})); Timeline::animate(AnimationListTag(traceName + "_fadeOut"), new AColor(world->body(ObjectNameTag(traceName)), {150, 150, 150, 0}));
Timeline::animate(AnimationListTag(traceName + "_delete"), new AFunction([this, traceName](){ removeFireTrace(ObjectNameTag(traceName)); }, 1, 2)); Timeline::animate(AnimationListTag(traceName + "_delete"), new AFunction([this, traceName](){ removeFireTrace(ObjectNameTag(traceName)); }, 1, 2));
} }
@ -266,7 +266,7 @@ void Shooter::removeBonus(const ObjectNameTag &bonusName) {
} }
void Shooter::addWeapon(std::shared_ptr<Weapon> weapon) { void Shooter::addWeapon(std::shared_ptr<Weapon> weapon) {
world->addBody(weapon, weapon->name()); world->addBody(weapon, ObjectNameTag("weapon_" + weapon->name().str()));
if(client != nullptr) if(client != nullptr)
client->changeWeapon(weapon->name().str()); client->changeWeapon(weapon->name().str());
@ -294,5 +294,5 @@ void Shooter::changeEnemyWeapon(const std::string& weaponName, sf::Uint16 enemyI
} }
void Shooter::removeWeapon(std::shared_ptr<Weapon> weapon) { void Shooter::removeWeapon(std::shared_ptr<Weapon> weapon) {
world->removeBody(ObjectNameTag(weapon->name())); world->removeBody(ObjectNameTag("weapon_" + weapon->name().str()));
} }

View File

@ -6,7 +6,6 @@
#include "utils/Log.h" #include "utils/Log.h"
#include <cmath> #include <cmath>
#include "Consts.h"
std::vector<std::shared_ptr<Triangle>> Camera::project(std::shared_ptr<Mesh> mesh) { std::vector<std::shared_ptr<Triangle>> Camera::project(std::shared_ptr<Mesh> mesh) {
@ -128,3 +127,30 @@ void Camera::clear() {
_triangles.clear(); _triangles.clear();
_V = Matrix4x4::View(left(), up(), lookAt(), position()); _V = Matrix4x4::View(left(), up(), lookAt(), position());
} }
// OpenGL function
GLfloat *Camera::view() const {
auto* v = (GLfloat*)malloc(4*4*sizeof(GLfloat));
v[0] = -(GLfloat)left().x();
v[4] = -(GLfloat)left().y();
v[8] = -(GLfloat)left().z();
v[12] = (GLfloat)position().dot(left());
v[1] = (GLfloat)up().x();
v[5] = (GLfloat)up().y();
v[9] = (GLfloat)up().z();
v[13] = -(GLfloat)position().dot(up());
v[2] = -(GLfloat)lookAt().x();
v[6] = -(GLfloat)lookAt().y();
v[10] = -(GLfloat)lookAt().z();
v[14] = (GLfloat)position().dot(lookAt());
v[3] = (GLfloat)0.0f;
v[7] = (GLfloat)0.0f;
v[11] = (GLfloat)0.0f;
v[15] = (GLfloat)1.0f;
return v;
}

View File

@ -6,9 +6,9 @@
#define ENGINE_CAMERA_H #define ENGINE_CAMERA_H
#include <vector> #include <vector>
#include "Screen.h"
#include "Plane.h" #include "Plane.h"
#include "Mesh.h" #include "Mesh.h"
#include <SFML/OpenGL.hpp>
class Camera final : public Object{ class Camera final : public Object{
private: private:
@ -24,6 +24,7 @@ private:
bool _ready = false; bool _ready = false;
double _aspect = 0; double _aspect = 0;
public: public:
Camera() = default; Camera() = default;
Camera(const Camera& camera) = delete; Camera(const Camera& camera) = delete;
@ -36,6 +37,9 @@ public:
[[nodiscard]] int buffSize() const { return _triangles.size(); } [[nodiscard]] int buffSize() const { return _triangles.size(); }
std::vector<std::shared_ptr<Triangle>> sorted(); std::vector<std::shared_ptr<Triangle>> sorted();
// OpenGL function
[[nodiscard]] GLfloat* view() const;
}; };

View File

@ -13,6 +13,7 @@ namespace Consts {
const sf::Color BACKGROUND_COLOR = sf::Color(255, 255, 255); const sf::Color BACKGROUND_COLOR = sf::Color(255, 255, 255);
const std::string PROJECT_NAME = "engine"; const std::string PROJECT_NAME = "engine";
const bool USE_LOG_FILE = true; const bool USE_LOG_FILE = true;
const bool USE_OPEN_GL = true;
const double PI = 3.14159265358979323846264338327950288; const double PI = 3.14159265358979323846264338327950288;
const double EPS = 0.000001; const double EPS = 0.000001;

View File

@ -42,11 +42,6 @@ void Engine::create(int screenWidth, int screenHeight, const std::string &name,
Time::update(); Time::update();
update(); update();
/* Project all mesh
* Here we project all _tris for each body from world._objects.
* When we call camera.project(m.second),
*/
// sometimes we dont need to update physics world // sometimes we dont need to update physics world
// (for example in menu or while pause) // (for example in menu or while pause)
// hence we can set '_updateWorld' equal to false in setUpdateWorld(bool): // hence we can set '_updateWorld' equal to false in setUpdateWorld(bool):
@ -54,15 +49,30 @@ void Engine::create(int screenWidth, int screenHeight, const std::string &name,
Timeline::update(); Timeline::update();
camera->clear();
world->update(); world->update();
world->projectObjectsInCamera(camera);
// draw projected body if(_useOpenGL) {
GLfloat* view = camera->view();
for(auto & it : *world) {
if (it.second->isVisible()) {
GLfloat* geometry = Screen::glMeshToGLfloatArray(it.second, camera->position());
screen->glDrawMesh(geometry, view, 3 * it.second->triangles().size());
free(geometry);
}
}
free(view);
} else {
// clear triangles from previous frame
camera->clear();
// project triangles to the camera plane
for(auto & it : *world)
camera->project(it.second);
// draw triangles on the screen
for (auto &t : camera->sorted()) for (auto &t : camera->sorted())
screen->drawTriangle(*t); screen->drawTriangle(*t);
_triPerSec = camera->buffSize() * Time::fps(); _triPerSec = camera->buffSize() * Time::fps();
}
printDebugText(); printDebugText();
gui(); gui();
@ -87,13 +97,18 @@ void Engine::exit() {
void Engine::printDebugText() const { void Engine::printDebugText() const {
if (_debugText) { if (_debugText) {
screen->debugText(_name + "\n\n X: " + std::string str = _name + "\n\n X: " +
std::to_string((camera->position().x())) + "\n Y: " + std::to_string((camera->position().x())) + "\n Y: " +
std::to_string((camera->position().y())) + "\n Z: " + std::to_string((camera->position().y())) + "\n Z: " +
std::to_string((camera->position().z())) + "\n\n" + std::to_string((camera->position().z())) + "\n\n" +
std::to_string(screen->width()) + "x" + std::to_string(screen->width()) + "x" +
std::to_string(screen->height()) + "\n" + std::to_string(screen->height()) + "\t" +
std::to_string(Time::fps()) + std::to_string(Time::fps()) + " fps";
" fps \n" + std::to_string((int) _triPerSec) + " tris/s"); if(_useOpenGL) {
str += "\n Using OpenGL acceleration";
} else {
str += "\n" + std::to_string((int) _triPerSec) + " tris/s";
}
screen->debugText(str);
} }
} }

View File

@ -20,6 +20,7 @@ private:
double _triPerSec = 0; double _triPerSec = 0;
bool _debugText = true; bool _debugText = true;
bool _updateWorld = true; bool _updateWorld = true;
bool _useOpenGL = Consts::USE_OPEN_GL;
void printDebugText() const; void printDebugText() const;
protected: protected:
@ -35,6 +36,7 @@ protected:
void setDebugText(bool value) { _debugText = value; } void setDebugText(bool value) { _debugText = value; }
void setUpdateWorld(bool value) { _updateWorld = value; } void setUpdateWorld(bool value) { _updateWorld = value; }
void setGlEnable(bool value) { _useOpenGL = value; }
virtual void gui(){} virtual void gui(){}
public: public:

View File

@ -52,7 +52,7 @@ public:
~Mesh() override; ~Mesh() override;
Mesh static Obj(const std::string& filename); Mesh static Obj(const std::string& filename);
Mesh static LineTo(const Vec3D& from, const Vec3D& to, double line_width = 0.1, const sf::Color& color = {150, 150, 150, 255}); Mesh static LineTo(const Vec3D& from, const Vec3D& to, double line_width = 0.1, const sf::Color& color = {150, 150, 150, 100});
}; };
#endif //INC_3DZAVR_MESH_H #endif //INC_3DZAVR_MESH_H

View File

@ -7,6 +7,9 @@
#include <utility> #include <utility>
#include "utils/Log.h" #include "utils/Log.h"
#include "ResourceManager.h" #include "ResourceManager.h"
#include <SFML/OpenGL.hpp>
#include <cmath>
void Screen::open(int screenWidth, int screenHeight, const std::string &name, bool verticalSync, sf::Color background, sf::Uint32 style) { void Screen::open(int screenWidth, int screenHeight, const std::string &name, bool verticalSync, sf::Color background, sf::Uint32 style) {
_title = name; _title = name;
@ -15,6 +18,7 @@ void Screen::open(int screenWidth, int screenHeight, const std::string &name, bo
_background = background; _background = background;
sf::ContextSettings settings; sf::ContextSettings settings;
settings.depthBits = 24;
settings.antialiasingLevel = 8; settings.antialiasingLevel = 8;
_window = std::make_shared<sf::RenderWindow>(); _window = std::make_shared<sf::RenderWindow>();
@ -37,6 +41,9 @@ void Screen::display() {
} }
void Screen::clear() { void Screen::clear() {
// Clear the depth buffer
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
_window->clear(_background); _window->clear(_background);
} }
@ -48,7 +55,10 @@ void Screen::drawTriangle(const Triangle& triangle)
sf::Vertex(sf::Vector2f((float)triangle[1].x(), (float)triangle[1].y()), triangle.color()), sf::Vertex(sf::Vector2f((float)triangle[1].x(), (float)triangle[1].y()), triangle.color()),
sf::Vertex(sf::Vector2f((float)triangle[2].x(), (float)triangle[2].y()), triangle.color()) sf::Vertex(sf::Vector2f((float)triangle[2].x(), (float)triangle[2].y()), triangle.color())
}; };
_window->pushGLStates();
_window->draw(tris, 3, sf::Triangles); _window->draw(tris, 3, sf::Triangles);
_window->popGLStates();
} }
void Screen::setTitle(const std::string& title) { void Screen::setTitle(const std::string& title) {
@ -71,9 +81,11 @@ void Screen::debugText(const std::string& text) {
t.setString(text); t.setString(text);
t.setCharacterSize(30); t.setCharacterSize(30);
t.setFillColor(sf::Color::Black); t.setFillColor(sf::Color::Black);
t.setPosition(10, 10); t.setPosition(10, 50);
_window->pushGLStates();
_window->draw(t); _window->draw(t);
_window->popGLStates();
} }
void Screen::drawTetragon(const Vec2D &p1, const Vec2D &p2, const Vec2D &p3, const Vec2D &p4, sf::Color color) { void Screen::drawTetragon(const Vec2D &p1, const Vec2D &p2, const Vec2D &p3, const Vec2D &p4, sf::Color color) {
@ -84,7 +96,10 @@ void Screen::drawTetragon(const Vec2D &p1, const Vec2D &p2, const Vec2D &p3, con
polygon.setPoint(2, sf::Vector2f((float)p3.x(), (float)p3.y())); polygon.setPoint(2, sf::Vector2f((float)p3.x(), (float)p3.y()));
polygon.setPoint(3, sf::Vector2f((float)p4.x(), (float)p4.y())); polygon.setPoint(3, sf::Vector2f((float)p4.x(), (float)p4.y()));
polygon.setFillColor(color); polygon.setFillColor(color);
_window->pushGLStates();
_window->draw(polygon); _window->draw(polygon);
_window->popGLStates();
} }
void Screen::drawText(const std::string& string, const Vec2D &position, int size, sf::Color color) { void Screen::drawText(const std::string& string, const Vec2D &position, int size, sf::Color color) {
@ -98,17 +113,125 @@ void Screen::drawText(const std::string& string, const Vec2D &position, int size
text.setPosition((float)position.x(), (float)position.y()); text.setPosition((float)position.x(), (float)position.y());
text.setString(string); text.setString(string);
_window->pushGLStates();
_window->draw(text); _window->draw(text);
_window->popGLStates();
} }
void Screen::drawSprite(const sf::Sprite &sprite) { void Screen::drawSprite(const sf::Sprite &sprite) {
_window->pushGLStates();
_window->draw(sprite); _window->draw(sprite);
_window->popGLStates();
} }
void Screen::drawText(const sf::Text &text) { void Screen::drawText(const sf::Text &text) {
_window->pushGLStates();
_window->draw(text); _window->draw(text);
_window->popGLStates();
} }
void Screen::attachMouse(std::shared_ptr<Mouse> mouse) { void Screen::attachMouse(std::shared_ptr<Mouse> mouse) {
mouse->setWindow(_window); mouse->setWindow(_window);
} }
// OpenGL functions
void Screen::glDrawMesh(GLfloat* geometry, GLfloat* view, size_t count) {
// OpenGL:
// Make the window the active window for OpenGL calls
_window->setActive(true);
glEnable(GL_CULL_FACE); // enable culling face
glCullFace(GL_BACK); // cull faces from back
glFrontFace(GL_CCW); // vertex order (counter clock wise)
// Enable Z-buffer read and write
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
glClearDepth(1.f);
// Disable lighting
glDisable(GL_LIGHTING);
// enable alpha channel:
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// Configure the viewport (the same size as the window)
glViewport(0, 0, _window->getSize().x, _window->getSize().y);
// Setup a perspective projection
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
GLfloat ratio = static_cast<float>(_window->getSize().x) / _window->getSize().y;
glFrustum(-ratio, ratio, -1.f, 1.f, 1.0f, 500.f);
// Enable position and texture coordinates vertex components
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glVertexPointer(3, GL_FLOAT, 7 * sizeof(GLfloat), geometry);
glColorPointer(4, GL_FLOAT, 7 * sizeof(GLfloat), geometry + 3);
// Disable normal and color vertex components
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
// Apply some transformations
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glLoadMatrixf(view);
// Draw the mesh
glDrawArrays(GL_TRIANGLES, 0, count);
// Make the window no longer the active window for OpenGL calls
_window->setActive(false);
}
GLfloat* Screen::glMeshToGLfloatArray(std::shared_ptr<Mesh> mesh, const Vec3D& cameraPosition) {
Vec3D pos = mesh->position();
std::vector<Triangle>& triangles = mesh->triangles();
auto* geometry = (GLfloat*)malloc(7*3*triangles.size()*sizeof(GLfloat));
for(int i = 0; i < triangles.size(); i++) {
int stride = 21*i;
double dot = triangles[i].norm().dot((pos + Vec3D(triangles[i][0]) - cameraPosition).normalized());
sf::Color color = triangles[i].color();
sf::Color ambientColor = sf::Color((sf::Uint8)(color.r * (0.3 * std::abs(dot) + 0.7)),
(sf::Uint8)(color.g * (0.3 * std::abs(dot) + 0.7)),
(sf::Uint8)(color.b * (0.3 * std::abs(dot) + 0.7)),
(sf::Uint8)color.a);
geometry[stride + 0] = (GLfloat)triangles[i][0].x() + pos.x();
geometry[stride + 1] = (GLfloat)triangles[i][0].y() + pos.y();
geometry[stride + 2] = (GLfloat)triangles[i][0].z() + pos.z();
geometry[stride + 3] = (GLfloat)ambientColor.r/255.0f;
geometry[stride + 4] = (GLfloat)ambientColor.g/255.0f;
geometry[stride + 5] = (GLfloat)ambientColor.b/255.0f;
geometry[stride + 6] = (GLfloat)ambientColor.a/255.0f;
geometry[stride + 7] = (GLfloat)triangles[i][1].x() + pos.x();
geometry[stride + 8] = (GLfloat)triangles[i][1].y() + pos.y();
geometry[stride + 9] = (GLfloat)triangles[i][1].z() + pos.z();
geometry[stride + 10] = (GLfloat)ambientColor.r/255.0f;
geometry[stride + 11] = (GLfloat)ambientColor.g/255.0f;
geometry[stride + 12] = (GLfloat)ambientColor.b/255.0f;
geometry[stride + 13] = (GLfloat)ambientColor.a/255.0f;
geometry[stride + 14] = (GLfloat)triangles[i][2].x() + pos.x();
geometry[stride + 15] = (GLfloat)triangles[i][2].y() + pos.y();
geometry[stride + 16] = (GLfloat)triangles[i][2].z() + pos.z();
geometry[stride + 17] = (GLfloat)ambientColor.r/255.0f;
geometry[stride + 18] = (GLfloat)ambientColor.g/255.0f;
geometry[stride + 19] = (GLfloat)ambientColor.b/255.0f;
geometry[stride + 20] = (GLfloat)ambientColor.a/255.0f;
}
return geometry;
}

View File

@ -13,6 +13,8 @@
#include "utils/Time.h" #include "utils/Time.h"
#include "Mouse.h" #include "Mouse.h"
#include "Consts.h" #include "Consts.h"
#include "Mesh.h"
#include "Camera.h"
class Screen final { class Screen final {
private: private:
@ -29,7 +31,7 @@ public:
void display(); void display();
void clear(); void clear();
bool hasFocus() const { return _window->hasFocus(); } [[nodiscard]] bool hasFocus() const { return _window->hasFocus(); }
void drawTriangle(const Triangle& triangle); void drawTriangle(const Triangle& triangle);
void drawTetragon(const Vec2D& p1, const Vec2D& p2, const Vec2D& p3, const Vec2D& p4, sf::Color color); void drawTetragon(const Vec2D& p1, const Vec2D& p2, const Vec2D& p3, const Vec2D& p4, sf::Color color);
@ -50,6 +52,10 @@ public:
void debugText(const std::string& text); void debugText(const std::string& text);
void attachMouse(std::shared_ptr<Mouse> mouse); void attachMouse(std::shared_ptr<Mouse> mouse);
// OpenGL functions
void glDrawMesh(GLfloat* geometry, GLfloat* view, size_t count);
static GLfloat* glMeshToGLfloatArray(std::shared_ptr<Mesh> mesh, const Vec3D& cameraPosition);
}; };

View File

@ -6,6 +6,8 @@
#include "utils/Log.h" #include "utils/Log.h"
#include "Plane.h" #include "Plane.h"
#include "ResourceManager.h" #include "ResourceManager.h"
#include <sstream>
#include <cmath>
using namespace std; using namespace std;
@ -19,15 +21,28 @@ void World::loadBody(const ObjectNameTag& tag, const string &filename, const Vec
Log::log("World::loadBody(): inserted body from " + filename + " with title '" + tag.str() + "' with " + std::to_string(_objects[tag]->triangles().size()) + " tris."); Log::log("World::loadBody(): inserted body from " + filename + " with title '" + tag.str() + "' with " + std::to_string(_objects[tag]->triangles().size()) + " tris.");
} }
std::pair<Vec3D, ObjectNameTag> World::rayCast(const Vec3D& from, const Vec3D& to, const std::string& tag) { IntersectionInformation World::rayCast(const Vec3D& from, const Vec3D& to, const std::string& skipTags) {
std::pair<Vec3D, string> result; // make vector of tags, that we are going to escape
vector <string> tagsToSkip;
stringstream s(skipTags);
std::string t;
while (s >> t) tagsToSkip.push_back(t);
bool intersected = false;
std::unique_ptr<Vec3D> point = std::make_unique<Vec3D>(); std::unique_ptr<Vec3D> point = std::make_unique<Vec3D>();
std::unique_ptr<Triangle> triangle = std::make_unique<Triangle>();
std::string bodyName; std::string bodyName;
double minDistance = Consts::RAY_CAST_MAX_DISTANCE; double minDistance = Consts::RAY_CAST_MAX_DISTANCE;
std::shared_ptr<RigidBody> intersectedBody = nullptr;
for(auto& [name, body] : _objects) { for(auto& [name, body] : _objects) {
if(!tag.empty() && name.str().find(tag) == std::string::npos)
//for (auto& escapeTag : tagsToSkip)
// if(name.str().find(escapeTag) != std::string::npos)
// continue;
if(name.str().find("Player") != std::string::npos || name.str().find("weapon") != std::string::npos)
continue; continue;
for(auto& tri : body->triangles()) { for(auto& tri : body->triangles()) {
@ -39,11 +54,14 @@ std::pair<Vec3D, ObjectNameTag> World::rayCast(const Vec3D& from, const Vec3D& t
if(intersection.second > 0 && distance < minDistance && tri_translated.isPointInside(intersection.first)) { if(intersection.second > 0 && distance < minDistance && tri_translated.isPointInside(intersection.first)) {
minDistance = distance; minDistance = distance;
point = std::make_unique<Vec3D>(intersection.first); point = std::make_unique<Vec3D>(intersection.first);
triangle = std::make_unique<Triangle>(tri_translated);
bodyName = name.str(); bodyName = name.str();
intersected = true;
intersectedBody = body;
} }
} }
} }
return {*point, ObjectNameTag(bodyName)}; return IntersectionInformation{*point, sqrt(minDistance), *triangle, ObjectNameTag(bodyName), intersectedBody, intersected};
} }
void World::loadMap(const std::string& filename, const Vec3D& scale) { void World::loadMap(const std::string& filename, const Vec3D& scale) {
@ -53,6 +71,8 @@ void World::loadMap(const std::string& filename, const Vec3D& scale) {
addBody(std::make_shared<RigidBody>(*objs[i]), meshName); addBody(std::make_shared<RigidBody>(*objs[i]), meshName);
body(meshName)->scale(scale); body(meshName)->scale(scale);
} }
auto it = _objects.begin();
} }
void World::removeBody(const ObjectNameTag& tag) { void World::removeBody(const ObjectNameTag& tag) {
@ -95,11 +115,6 @@ void World::update() {
} }
} }
void World::projectObjectsInCamera(std::shared_ptr<Camera> camera) {
for (auto &[name, body] : _objects)
camera->project(body);
}
std::shared_ptr<RigidBody> World::body(const ObjectNameTag& tag) { std::shared_ptr<RigidBody> World::body(const ObjectNameTag& tag) {
if(_objects.count(tag) == 0) if(_objects.count(tag) == 0)
return nullptr; return nullptr;

View File

@ -7,8 +7,18 @@
#include <map> #include <map>
#include "Camera.h" #include "Camera.h"
#include "Screen.h"
#include "physics/RigidBody.h" #include "physics/RigidBody.h"
struct IntersectionInformation final {
const Vec3D pointOfIntersection;
const double distanceToObject;
const Triangle intersectedTriangle;
const ObjectNameTag objectName;
const std::shared_ptr<RigidBody> obj;
const bool intersected;
};
class World final { class World final {
private: private:
std::map<ObjectNameTag, std::shared_ptr<RigidBody>> _objects; std::map<ObjectNameTag, std::shared_ptr<RigidBody>> _objects;
@ -17,19 +27,19 @@ public:
void checkCollision(const ObjectNameTag& tag); void checkCollision(const ObjectNameTag& tag);
void update(); void update();
void projectObjectsInCamera(std::shared_ptr<Camera> camera);
void addBody(std::shared_ptr<RigidBody> mesh, const ObjectNameTag& tag); void addBody(std::shared_ptr<RigidBody> mesh, const ObjectNameTag& tag);
std::shared_ptr<RigidBody> body(const ObjectNameTag& tag); std::shared_ptr<RigidBody> body(const ObjectNameTag& tag);
void removeBody(const ObjectNameTag& tag); void removeBody(const ObjectNameTag& tag);
void loadBody(const ObjectNameTag& tag, const std::string &filename, const Vec3D& scale = Vec3D{1, 1, 1}); void loadBody(const ObjectNameTag& tag, const std::string &filename, const Vec3D& scale = Vec3D{1, 1, 1});
// rayCast returns pair of Point4D and std::string: // std::string skipTags is a string that consist of all objects we want to skip in ray casting
// 1) Point4D is point of collision IntersectionInformation rayCast(const Vec3D& from, const Vec3D& to, const std::string& skipTags = "");
// 2) std::string - title of the object
std::pair<Vec3D, ObjectNameTag> rayCast(const Vec3D& from, const Vec3D& to, const std::string& tag = "");
void loadMap(const std::string& filename, const Vec3D & scale = Vec3D{1, 1, 1}); void loadMap(const std::string& filename, const Vec3D & scale = Vec3D{1, 1, 1});
std::map<ObjectNameTag, std::shared_ptr<RigidBody>>::iterator begin() { return _objects.begin(); }
std::map<ObjectNameTag, std::shared_ptr<RigidBody>>::iterator end() { return _objects.end(); }
}; };

View File

@ -8,7 +8,7 @@
using namespace std; using namespace std;
Ak47::Ak47(int ammo, const std::string& weaponName) : Weapon(weaponName, ShooterConsts::AK47_OBJ, Vec3D{3, 3, 3}, Vec3D{-0.8, 1.3, 0.3}, Vec3D{0, Consts::PI, 0}) { Ak47::Ak47(int ammo, const std::string& weaponName) : Weapon(weaponName, ShooterConsts::AK47_OBJ, Vec3D{3, 3, 3}, Vec3D{-2.2, 1.0, 1.3}, Vec3D{0, Consts::PI, 0}) {
fireSound = ShooterConsts::AK47_FIRE_SOUND; fireSound = ShooterConsts::AK47_FIRE_SOUND;
reloadSound = ShooterConsts::AK47_RELOAD_SOUND; reloadSound = ShooterConsts::AK47_RELOAD_SOUND;

View File

@ -10,7 +10,7 @@
class Gold_Ak47 final : public Weapon { class Gold_Ak47 final : public Weapon {
public: public:
explicit Gold_Ak47(int ammo = 200, const std::string& weaponName = "gold_ak47") : Weapon(weaponName, ShooterConsts::GOLD_AK47_OBJ, Vec3D{3, 3, 3}, Vec3D{-0.8, 1.3, 0.3}, Vec3D{0, Consts::PI, 0}) { explicit Gold_Ak47(int ammo = 200, const std::string& weaponName = "gold_ak47") : Weapon(weaponName, ShooterConsts::GOLD_AK47_OBJ, Vec3D{3, 3, 3}, Vec3D{-2.2, 1.0, 1.3}, Vec3D{0, Consts::PI, 0}) {
fireSound = ShooterConsts::GOLD_AK47_FIRE_SOUND; fireSound = ShooterConsts::GOLD_AK47_FIRE_SOUND;
reloadSound = ShooterConsts::GOLD_AK47_RELOAD_SOUND; reloadSound = ShooterConsts::GOLD_AK47_RELOAD_SOUND;

View File

@ -8,7 +8,7 @@
using namespace std; using namespace std;
Gun::Gun(int ammo, const std::string& weaponName) : Weapon(weaponName, ShooterConsts::GUN_OBJ, Vec3D{3, 3, 3}, Vec3D{-0.8, 1.3, 0.3}, Vec3D{0, Consts::PI, 0}) { Gun::Gun(int ammo, const std::string& weaponName) : Weapon(weaponName, ShooterConsts::GUN_OBJ, Vec3D{3, 3, 3}, Vec3D{-1.8, 1.3, 1.8}, Vec3D{0, Consts::PI, 0}) {
fireSound = ShooterConsts::GUN_FIRE_SOUND; fireSound = ShooterConsts::GUN_FIRE_SOUND;
reloadSound = ShooterConsts::GUN_RELOAD_SOUND; reloadSound = ShooterConsts::GUN_RELOAD_SOUND;

View File

@ -6,7 +6,7 @@
#include "Rifle.h" #include "Rifle.h"
#include "../ShooterConsts.h" #include "../ShooterConsts.h"
Rifle::Rifle(int ammo, const std::string &weaponName) : Weapon(weaponName, ShooterConsts::RIFLE_OBJ, Vec3D{3, 3, 3}, Vec3D{-1.2, 1, 0.3}, Vec3D{0, Consts::PI, 0}) { Rifle::Rifle(int ammo, const std::string &weaponName) : Weapon(weaponName, ShooterConsts::RIFLE_OBJ, Vec3D{3, 3, 3}, Vec3D{-2.3, 1, 1.3}, Vec3D{0, Consts::PI, 0}) {
fireSound = ShooterConsts::RIFLE_FIRE_SOUND; fireSound = ShooterConsts::RIFLE_FIRE_SOUND;
reloadSound = ShooterConsts::RIFLE_RELOAD_SOUND; reloadSound = ShooterConsts::RIFLE_RELOAD_SOUND;

View File

@ -9,7 +9,7 @@
using namespace std; using namespace std;
Shotgun::Shotgun(int ammo, const std::string& weaponName) : Weapon(weaponName, ShooterConsts::SHOTGUN_OBJ, Vec3D{3, 3, 3}, Vec3D{-0.95, 1.3, -0.6}, Vec3D{0, Consts::PI, 0}) { Shotgun::Shotgun(int ammo, const std::string& weaponName) : Weapon(weaponName, ShooterConsts::SHOTGUN_OBJ, Vec3D{3, 3, 3}, Vec3D{-1.95, 0.8, 1.5}, Vec3D{0, Consts::PI, 0}) {
fireSound = ShooterConsts::SHOTGUN_FIRE_SOUND; fireSound = ShooterConsts::SHOTGUN_FIRE_SOUND;
reloadSound = ShooterConsts::SHOTGUN_RELOAD_SOUND; reloadSound = ShooterConsts::SHOTGUN_RELOAD_SOUND;
@ -25,7 +25,7 @@ Shotgun::Shotgun(int ammo, const std::string& weaponName) : Weapon(weaponName, S
} }
std::map<ObjectNameTag, double> std::map<ObjectNameTag, double>
Shotgun::processFire(std::function<std::pair<Vec3D, ObjectNameTag>(const Vec3D&, const Vec3D&)> rayCastFunction, const Vec3D& position, const Vec3D& direction) { Shotgun::processFire(std::function<IntersectionInformation(const Vec3D&, const Vec3D&)> rayCastFunction, const Vec3D& position, const Vec3D& direction) {
std::map<ObjectNameTag, double> damagedPlayers; std::map<ObjectNameTag, double> damagedPlayers;
for(int i = 0; i < 15; i++) { for(int i = 0; i < 15; i++) {

View File

@ -10,7 +10,7 @@
class Shotgun final : public Weapon { class Shotgun final : public Weapon {
public: public:
explicit Shotgun(int ammo = 15, const std::string& weaponName = "shotgun"); explicit Shotgun(int ammo = 15, const std::string& weaponName = "shotgun");
std::map<ObjectNameTag, double> processFire(std::function<std::pair<Vec3D, ObjectNameTag>(const Vec3D&, const Vec3D&)> rayCastFunction, const Vec3D& position, const Vec3D& direction) override; std::map<ObjectNameTag, double> processFire(std::function<IntersectionInformation(const Vec3D&, const Vec3D&)> rayCastFunction, const Vec3D& position, const Vec3D& direction) override;
}; };

View File

@ -21,7 +21,7 @@ Weapon::Weapon(const std::string& weaponName, const std::string& objFileName, co
translate(t); translate(t);
} }
std::map<ObjectNameTag, double> Weapon::fire(std::function<std::pair<Vec3D, ObjectNameTag>(const Vec3D&, const Vec3D&)> rayCastFunction, const Vec3D& position, const Vec3D& direction) { FireInformation Weapon::fire(std::function<IntersectionInformation(const Vec3D&, const Vec3D&)> rayCastFunction, const Vec3D& position, const Vec3D& direction) {
if(_clipAmmo == 0) { if(_clipAmmo == 0) {
reload(); reload();
if(_clipAmmo == 0) if(_clipAmmo == 0)
@ -29,7 +29,7 @@ std::map<ObjectNameTag, double> Weapon::fire(std::function<std::pair<Vec3D, Obje
} }
if(_clipAmmo <= 0 || std::abs(Time::time() - _lastFireTime) < _fireDelay || std::abs(Time::time() - _lastReloadTime) < _reloadTime) if(_clipAmmo <= 0 || std::abs(Time::time() - _lastFireTime) < _fireDelay || std::abs(Time::time() - _lastReloadTime) < _reloadTime)
return std::map<ObjectNameTag, double>(); return FireInformation{std::map<ObjectNameTag, double>(), false};
_lastFireTime = Time::time(); _lastFireTime = Time::time();
_clipAmmo--; _clipAmmo--;
@ -37,7 +37,7 @@ std::map<ObjectNameTag, double> Weapon::fire(std::function<std::pair<Vec3D, Obje
SoundController::playSound(SoundTag("fire"), fireSound); SoundController::playSound(SoundTag("fire"), fireSound);
Log::log("Weapon::fire (" + std::to_string(_stockAmmo) + " : " + std::to_string(_clipAmmo) + ")"); Log::log("Weapon::fire (" + std::to_string(_stockAmmo) + " : " + std::to_string(_clipAmmo) + ")");
return processFire(std::move(rayCastFunction), position, direction); return FireInformation{processFire(std::move(rayCastFunction), position, direction), true};
} }
void Weapon::reload() { void Weapon::reload() {
@ -56,11 +56,11 @@ void Weapon::reload() {
_lastReloadTime = Time::time(); _lastReloadTime = Time::time();
} }
std::map<ObjectNameTag, double> Weapon::processFire(std::function<std::pair<Vec3D, ObjectNameTag>(const Vec3D&, const Vec3D&)> rayCastFunction, const Vec3D& position, const Vec3D& direction) { std::map<ObjectNameTag, double> Weapon::processFire(std::function<IntersectionInformation(const Vec3D&, const Vec3D&)> rayCastFunction, const Vec3D& position, const Vec3D& direction) {
return addTrace(std::move(rayCastFunction), position, direction); return addTrace(std::move(rayCastFunction), position, direction);
} }
std::map<ObjectNameTag, double> Weapon::addTrace(std::function<std::pair<Vec3D, ObjectNameTag>(const Vec3D&, const Vec3D&)> rayCastFunction, const Vec3D& from, const Vec3D& directionTo) { std::map<ObjectNameTag, double> Weapon::addTrace(std::function<IntersectionInformation(const Vec3D&, const Vec3D&)> rayCastFunction, const Vec3D& from, const Vec3D& directionTo) {
std::map<ObjectNameTag, double> damagedPlayers; std::map<ObjectNameTag, double> damagedPlayers;
double spreading = _spreading*ShooterConsts::FIRE_DISTANCE/100; double spreading = _spreading*ShooterConsts::FIRE_DISTANCE/100;
@ -70,13 +70,12 @@ std::map<ObjectNameTag, double> Weapon::addTrace(std::function<std::pair<Vec3D,
// damage player // damage player
auto rayCast = rayCastFunction(from, from + directionTo * ShooterConsts::FIRE_DISTANCE + randV); auto rayCast = rayCastFunction(from, from + directionTo * ShooterConsts::FIRE_DISTANCE + randV);
if(rayCast.second.str().find("Enemy") != std::string::npos) { if(rayCast.objectName.str().find("Enemy") != std::string::npos)
damagedPlayers[rayCast.second] += _damage/(1.0 + (from - rayCast.first).abs()); damagedPlayers[rayCast.objectName] += _damage/(1.0 + rayCast.distanceToObject);
}
// add trace line // add trace line
Vec3D lineFrom = position() + Vec3D(triangles().back()[0]); Vec3D lineFrom = position() + Vec3D(triangles().back()[0]);
Vec3D lineTo = rayCast.first == Vec3D(0) ? position() + -lookAt() * ShooterConsts::FIRE_DISTANCE + randV: rayCast.first; Vec3D lineTo = rayCast.intersected ? rayCast.pointOfIntersection : position() + -lookAt() * ShooterConsts::FIRE_DISTANCE + randV;
_addTraceCallBack(lineFrom, lineTo); _addTraceCallBack(lineFrom, lineTo);
return damagedPlayers; return damagedPlayers;

View File

@ -15,6 +15,11 @@
#include "../engine/SoundController.h" #include "../engine/SoundController.h"
#include "../engine/Consts.h" #include "../engine/Consts.h"
struct FireInformation {
const std::map<ObjectNameTag, double> damagedPlayers;
const bool shot;
};
class Weapon : public RigidBody { class Weapon : public RigidBody {
protected: protected:
int _initialPack = 100; // how much ammo do you have when you find the weapon int _initialPack = 100; // how much ammo do you have when you find the weapon
@ -39,14 +44,14 @@ protected:
std::function<void(const Vec3D&, const Vec3D&)> _addTraceCallBack; std::function<void(const Vec3D&, const Vec3D&)> _addTraceCallBack;
std::map<ObjectNameTag, double> addTrace(std::function<std::pair<Vec3D, ObjectNameTag>(const Vec3D&, const Vec3D&)> rayCastFunction, const Vec3D& position, const Vec3D& direction); std::map<ObjectNameTag, double> addTrace(std::function<IntersectionInformation(const Vec3D&, const Vec3D&)> rayCastFunction, const Vec3D& position, const Vec3D& direction);
virtual std::map<ObjectNameTag, double> processFire(std::function<std::pair<Vec3D, ObjectNameTag>(const Vec3D&, const Vec3D&)> rayCastFunction, const Vec3D& position, const Vec3D& direction); virtual std::map<ObjectNameTag, double> processFire(std::function<IntersectionInformation(const Vec3D&, const Vec3D&)> rayCastFunction, const Vec3D& position, const Vec3D& direction);
public: public:
Weapon(const std::string& weaponName, const std::string& objFileName, const Vec3D& scale, const Vec3D& translate, const Vec3D& rotate); Weapon(const std::string& weaponName, const std::string& objFileName, const Vec3D& scale, const Vec3D& translate, const Vec3D& rotate);
std::map<ObjectNameTag, double> fire(std::function<std::pair<Vec3D, ObjectNameTag>(const Vec3D&, const Vec3D&)> rayCastFunction, const Vec3D& position, const Vec3D& direction); FireInformation fire(std::function<IntersectionInformation(const Vec3D&, const Vec3D&)> rayCastFunction, const Vec3D& position, const Vec3D& direction);
void reload(); void reload();
[[nodiscard]] std::pair<double, double> balance() const{ return std::make_pair(_clipAmmo, _stockAmmo); } [[nodiscard]] std::pair<double, double> balance() const{ return std::make_pair(_clipAmmo, _stockAmmo); }