OpenGL graphics acceleration support!
parent
22c18968f6
commit
e471795290
|
@ -112,4 +112,20 @@ if (SFML_FOUND)
|
|||
include_directories(${SFML_INCLUDE_DIR})
|
||||
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()
|
10
Player.cpp
10
Player.cpp
|
@ -8,7 +8,7 @@
|
|||
#include "engine/utils/Log.h"
|
||||
|
||||
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});
|
||||
setCollision(true);
|
||||
setVisible(false);
|
||||
|
@ -109,15 +109,17 @@ void Player::previousWeapon() {
|
|||
}
|
||||
}
|
||||
|
||||
void Player::fire() {
|
||||
bool Player::fire() {
|
||||
auto camera = attached(ObjectNameTag("Camera"));
|
||||
if(camera != nullptr) {
|
||||
auto damagedPlayers = _weapons[_selectedWeapon]->fire(_rayCastFunction, camera->position(), camera->lookAt());
|
||||
for(auto& [damagedPlayerName, damage] : damagedPlayers) {
|
||||
auto fireInfo = _weapons[_selectedWeapon]->fire(_rayCastFunction, camera->position(), camera->lookAt());
|
||||
for(auto& [damagedPlayerName, damage] : fireInfo.damagedPlayers) {
|
||||
sf::Uint16 targetId = std::stoi(damagedPlayerName.str().substr(6));
|
||||
_damagePlayerCallBack(targetId, damage);
|
||||
}
|
||||
return fireInfo.shot;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Player::reload() {
|
||||
|
|
8
Player.h
8
Player.h
|
@ -37,7 +37,7 @@ private:
|
|||
std::function<void(std::shared_ptr<Weapon>)> _addWeaponCallBack;
|
||||
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:
|
||||
Player();
|
||||
|
||||
|
@ -57,8 +57,10 @@ public:
|
|||
|
||||
void nextWeapon();
|
||||
void previousWeapon();
|
||||
void fire();
|
||||
bool fire();
|
||||
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);
|
||||
|
||||
|
@ -83,7 +85,7 @@ public:
|
|||
void setRemoveWeaponCallBack(std::function<void(std::shared_ptr<Weapon>)> 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);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "engine/animation/ATranslate.h"
|
||||
#include "engine/animation/ATranslateToPoint.h"
|
||||
#include "engine/animation/Timeline.h"
|
||||
#include "engine/animation/ARotate.h"
|
||||
#include "ShooterConsts.h"
|
||||
|
||||
PlayerController::PlayerController(std::shared_ptr<Player> player,
|
||||
|
@ -107,15 +108,24 @@ void PlayerController::update() {
|
|||
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 we just want to jump, we have to add particular speed
|
||||
if (!_isSliding)
|
||||
_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
|
||||
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;
|
||||
} else {
|
||||
_isSliding = false;
|
||||
|
@ -149,10 +159,6 @@ void PlayerController::update() {
|
|||
_player->previousWeapon();
|
||||
}
|
||||
|
||||
if (Mouse::isButtonPressed(sf::Mouse::Button::Left)) {
|
||||
_player->fire();
|
||||
}
|
||||
|
||||
if(Keyboard::isKeyPressed(sf::Keyboard::R)) {
|
||||
_player->reload();
|
||||
}
|
||||
|
|
|
@ -59,3 +59,4 @@ GamePlay:
|
|||
![Project demonstration](img/gamePlay5.png)
|
||||
![Project demonstration](img/gamePlay6.png)
|
||||
![Project demonstration](img/gamePlay7.png)
|
||||
![Project demonstration](img/opengl.png)
|
||||
|
|
|
@ -81,7 +81,7 @@ void Shooter::start() {
|
|||
// TODO: encapsulate call backs inside Player
|
||||
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->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->setAddWeaponCallBack([this](std::shared_ptr<Weapon> weapon){ addWeapon(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->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));
|
||||
}
|
||||
|
||||
|
@ -266,7 +266,7 @@ void Shooter::removeBonus(const ObjectNameTag &bonusName) {
|
|||
}
|
||||
|
||||
void Shooter::addWeapon(std::shared_ptr<Weapon> weapon) {
|
||||
world->addBody(weapon, weapon->name());
|
||||
world->addBody(weapon, ObjectNameTag("weapon_" + weapon->name().str()));
|
||||
|
||||
if(client != nullptr)
|
||||
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) {
|
||||
world->removeBody(ObjectNameTag(weapon->name()));
|
||||
world->removeBody(ObjectNameTag("weapon_" + weapon->name().str()));
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
#include "utils/Log.h"
|
||||
|
||||
#include <cmath>
|
||||
#include "Consts.h"
|
||||
|
||||
std::vector<std::shared_ptr<Triangle>> Camera::project(std::shared_ptr<Mesh> mesh) {
|
||||
|
||||
|
@ -64,9 +63,9 @@ std::vector<std::shared_ptr<Triangle>> Camera::project(std::shared_ptr<Mesh> mes
|
|||
Triangle clippedProjected = clipped * _SP;
|
||||
|
||||
Triangle clippedProjectedNormalized = Triangle(clippedProjected[0] / clippedProjected[0].w(),
|
||||
clippedProjected[1] / clippedProjected[1].w(),
|
||||
clippedProjected[2] / clippedProjected[2].w(),
|
||||
ambientColor);
|
||||
clippedProjected[1] / clippedProjected[1].w(),
|
||||
clippedProjected[2] / clippedProjected[2].w(),
|
||||
ambientColor);
|
||||
|
||||
_triangles.emplace_back(std::make_shared<Triangle>(clippedProjectedNormalized));
|
||||
}
|
||||
|
@ -128,3 +127,30 @@ void Camera::clear() {
|
|||
_triangles.clear();
|
||||
_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;
|
||||
}
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
#define ENGINE_CAMERA_H
|
||||
|
||||
#include <vector>
|
||||
#include "Screen.h"
|
||||
#include "Plane.h"
|
||||
#include "Mesh.h"
|
||||
#include <SFML/OpenGL.hpp>
|
||||
|
||||
class Camera final : public Object{
|
||||
private:
|
||||
|
@ -24,6 +24,7 @@ private:
|
|||
|
||||
bool _ready = false;
|
||||
double _aspect = 0;
|
||||
|
||||
public:
|
||||
Camera() = default;
|
||||
Camera(const Camera& camera) = delete;
|
||||
|
@ -36,6 +37,9 @@ public:
|
|||
|
||||
[[nodiscard]] int buffSize() const { return _triangles.size(); }
|
||||
std::vector<std::shared_ptr<Triangle>> sorted();
|
||||
|
||||
// OpenGL function
|
||||
[[nodiscard]] GLfloat* view() const;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ namespace Consts {
|
|||
const sf::Color BACKGROUND_COLOR = sf::Color(255, 255, 255);
|
||||
const std::string PROJECT_NAME = "engine";
|
||||
const bool USE_LOG_FILE = true;
|
||||
const bool USE_OPEN_GL = true;
|
||||
|
||||
const double PI = 3.14159265358979323846264338327950288;
|
||||
const double EPS = 0.000001;
|
||||
|
|
|
@ -42,11 +42,6 @@ void Engine::create(int screenWidth, int screenHeight, const std::string &name,
|
|||
Time::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
|
||||
// (for example in menu or while pause)
|
||||
// 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();
|
||||
|
||||
camera->clear();
|
||||
world->update();
|
||||
world->projectObjectsInCamera(camera);
|
||||
|
||||
// draw projected body
|
||||
for (auto &t : camera->sorted())
|
||||
screen->drawTriangle(*t);
|
||||
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())
|
||||
screen->drawTriangle(*t);
|
||||
|
||||
_triPerSec = camera->buffSize() * Time::fps();
|
||||
_triPerSec = camera->buffSize() * Time::fps();
|
||||
}
|
||||
|
||||
printDebugText();
|
||||
gui();
|
||||
|
@ -87,13 +97,18 @@ void Engine::exit() {
|
|||
|
||||
void Engine::printDebugText() const {
|
||||
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().y())) + "\n Z: " +
|
||||
std::to_string((camera->position().z())) + "\n\n" +
|
||||
std::to_string(screen->width()) + "x" +
|
||||
std::to_string(screen->height()) + "\n" +
|
||||
std::to_string(Time::fps()) +
|
||||
" fps \n" + std::to_string((int) _triPerSec) + " tris/s");
|
||||
std::to_string(screen->height()) + "\t" +
|
||||
std::to_string(Time::fps()) + " fps";
|
||||
if(_useOpenGL) {
|
||||
str += "\n Using OpenGL acceleration";
|
||||
} else {
|
||||
str += "\n" + std::to_string((int) _triPerSec) + " tris/s";
|
||||
}
|
||||
screen->debugText(str);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ private:
|
|||
double _triPerSec = 0;
|
||||
bool _debugText = true;
|
||||
bool _updateWorld = true;
|
||||
bool _useOpenGL = Consts::USE_OPEN_GL;
|
||||
|
||||
void printDebugText() const;
|
||||
protected:
|
||||
|
@ -35,6 +36,7 @@ protected:
|
|||
|
||||
void setDebugText(bool value) { _debugText = value; }
|
||||
void setUpdateWorld(bool value) { _updateWorld = value; }
|
||||
void setGlEnable(bool value) { _useOpenGL = value; }
|
||||
|
||||
virtual void gui(){}
|
||||
public:
|
||||
|
|
|
@ -52,7 +52,7 @@ public:
|
|||
~Mesh() override;
|
||||
|
||||
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
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
#include <utility>
|
||||
#include "utils/Log.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) {
|
||||
_title = name;
|
||||
|
@ -15,6 +18,7 @@ void Screen::open(int screenWidth, int screenHeight, const std::string &name, bo
|
|||
_background = background;
|
||||
|
||||
sf::ContextSettings settings;
|
||||
settings.depthBits = 24;
|
||||
settings.antialiasingLevel = 8;
|
||||
|
||||
_window = std::make_shared<sf::RenderWindow>();
|
||||
|
@ -37,6 +41,9 @@ void Screen::display() {
|
|||
}
|
||||
|
||||
void Screen::clear() {
|
||||
// Clear the depth buffer
|
||||
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
|
||||
|
||||
_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[2].x(), (float)triangle[2].y()), triangle.color())
|
||||
};
|
||||
|
||||
_window->pushGLStates();
|
||||
_window->draw(tris, 3, sf::Triangles);
|
||||
_window->popGLStates();
|
||||
}
|
||||
|
||||
void Screen::setTitle(const std::string& title) {
|
||||
|
@ -71,9 +81,11 @@ void Screen::debugText(const std::string& text) {
|
|||
t.setString(text);
|
||||
t.setCharacterSize(30);
|
||||
t.setFillColor(sf::Color::Black);
|
||||
t.setPosition(10, 10);
|
||||
t.setPosition(10, 50);
|
||||
|
||||
_window->pushGLStates();
|
||||
_window->draw(t);
|
||||
_window->popGLStates();
|
||||
}
|
||||
|
||||
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(3, sf::Vector2f((float)p4.x(), (float)p4.y()));
|
||||
polygon.setFillColor(color);
|
||||
|
||||
_window->pushGLStates();
|
||||
_window->draw(polygon);
|
||||
_window->popGLStates();
|
||||
}
|
||||
|
||||
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.setString(string);
|
||||
|
||||
_window->pushGLStates();
|
||||
_window->draw(text);
|
||||
_window->popGLStates();
|
||||
}
|
||||
|
||||
void Screen::drawSprite(const sf::Sprite &sprite) {
|
||||
_window->pushGLStates();
|
||||
_window->draw(sprite);
|
||||
_window->popGLStates();
|
||||
}
|
||||
|
||||
void Screen::drawText(const sf::Text &text) {
|
||||
_window->pushGLStates();
|
||||
_window->draw(text);
|
||||
_window->popGLStates();
|
||||
}
|
||||
|
||||
void Screen::attachMouse(std::shared_ptr<Mouse> mouse) {
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
#include "utils/Time.h"
|
||||
#include "Mouse.h"
|
||||
#include "Consts.h"
|
||||
#include "Mesh.h"
|
||||
#include "Camera.h"
|
||||
|
||||
class Screen final {
|
||||
private:
|
||||
|
@ -29,7 +31,7 @@ public:
|
|||
|
||||
void display();
|
||||
void clear();
|
||||
bool hasFocus() const { return _window->hasFocus(); }
|
||||
[[nodiscard]] bool hasFocus() const { return _window->hasFocus(); }
|
||||
|
||||
void drawTriangle(const Triangle& triangle);
|
||||
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 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);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
#include "utils/Log.h"
|
||||
#include "Plane.h"
|
||||
#include "ResourceManager.h"
|
||||
#include <sstream>
|
||||
#include <cmath>
|
||||
|
||||
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.");
|
||||
}
|
||||
|
||||
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<Triangle> triangle = std::make_unique<Triangle>();
|
||||
std::string bodyName;
|
||||
double minDistance = Consts::RAY_CAST_MAX_DISTANCE;
|
||||
std::shared_ptr<RigidBody> intersectedBody = nullptr;
|
||||
|
||||
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;
|
||||
|
||||
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)) {
|
||||
minDistance = distance;
|
||||
point = std::make_unique<Vec3D>(intersection.first);
|
||||
triangle = std::make_unique<Triangle>(tri_translated);
|
||||
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) {
|
||||
|
@ -53,6 +71,8 @@ void World::loadMap(const std::string& filename, const Vec3D& scale) {
|
|||
addBody(std::make_shared<RigidBody>(*objs[i]), meshName);
|
||||
body(meshName)->scale(scale);
|
||||
}
|
||||
|
||||
auto it = _objects.begin();
|
||||
}
|
||||
|
||||
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) {
|
||||
if(_objects.count(tag) == 0)
|
||||
return nullptr;
|
||||
|
|
|
@ -7,8 +7,18 @@
|
|||
|
||||
#include <map>
|
||||
#include "Camera.h"
|
||||
#include "Screen.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 {
|
||||
private:
|
||||
std::map<ObjectNameTag, std::shared_ptr<RigidBody>> _objects;
|
||||
|
@ -17,19 +27,19 @@ public:
|
|||
|
||||
void checkCollision(const ObjectNameTag& tag);
|
||||
void update();
|
||||
void projectObjectsInCamera(std::shared_ptr<Camera> camera);
|
||||
|
||||
void addBody(std::shared_ptr<RigidBody> mesh, const ObjectNameTag& tag);
|
||||
std::shared_ptr<RigidBody> body(const ObjectNameTag& tag);
|
||||
void removeBody(const ObjectNameTag& tag);
|
||||
void loadBody(const ObjectNameTag& tag, const std::string &filename, const Vec3D& scale = Vec3D{1, 1, 1});
|
||||
|
||||
// rayCast returns pair of Point4D and std::string:
|
||||
// 1) Point4D is point of collision
|
||||
// 2) std::string - title of the object
|
||||
std::pair<Vec3D, ObjectNameTag> rayCast(const Vec3D& from, const Vec3D& to, const std::string& tag = "");
|
||||
// std::string skipTags is a string that consist of all objects we want to skip in ray casting
|
||||
IntersectionInformation rayCast(const Vec3D& from, const Vec3D& to, const std::string& skipTags = "");
|
||||
|
||||
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(); }
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
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;
|
||||
reloadSound = ShooterConsts::AK47_RELOAD_SOUND;
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
class Gold_Ak47 final : public Weapon {
|
||||
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;
|
||||
reloadSound = ShooterConsts::GOLD_AK47_RELOAD_SOUND;
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
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;
|
||||
reloadSound = ShooterConsts::GUN_RELOAD_SOUND;
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
#include "Rifle.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;
|
||||
reloadSound = ShooterConsts::RIFLE_RELOAD_SOUND;
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
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;
|
||||
reloadSound = ShooterConsts::SHOTGUN_RELOAD_SOUND;
|
||||
|
||||
|
@ -25,7 +25,7 @@ Shotgun::Shotgun(int ammo, const std::string& weaponName) : Weapon(weaponName, S
|
|||
}
|
||||
|
||||
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;
|
||||
|
||||
for(int i = 0; i < 15; i++) {
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
class Shotgun final : public Weapon {
|
||||
public:
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ Weapon::Weapon(const std::string& weaponName, const std::string& objFileName, co
|
|||
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) {
|
||||
reload();
|
||||
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)
|
||||
return std::map<ObjectNameTag, double>();
|
||||
return FireInformation{std::map<ObjectNameTag, double>(), false};
|
||||
|
||||
_lastFireTime = Time::time();
|
||||
_clipAmmo--;
|
||||
|
@ -37,7 +37,7 @@ std::map<ObjectNameTag, double> Weapon::fire(std::function<std::pair<Vec3D, Obje
|
|||
SoundController::playSound(SoundTag("fire"), fireSound);
|
||||
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() {
|
||||
|
@ -56,11 +56,11 @@ void Weapon::reload() {
|
|||
_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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
double spreading = _spreading*ShooterConsts::FIRE_DISTANCE/100;
|
||||
|
@ -70,13 +70,12 @@ std::map<ObjectNameTag, double> Weapon::addTrace(std::function<std::pair<Vec3D,
|
|||
|
||||
// damage player
|
||||
auto rayCast = rayCastFunction(from, from + directionTo * ShooterConsts::FIRE_DISTANCE + randV);
|
||||
if(rayCast.second.str().find("Enemy") != std::string::npos) {
|
||||
damagedPlayers[rayCast.second] += _damage/(1.0 + (from - rayCast.first).abs());
|
||||
}
|
||||
if(rayCast.objectName.str().find("Enemy") != std::string::npos)
|
||||
damagedPlayers[rayCast.objectName] += _damage/(1.0 + rayCast.distanceToObject);
|
||||
|
||||
// add trace line
|
||||
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);
|
||||
|
||||
return damagedPlayers;
|
||||
|
|
|
@ -15,6 +15,11 @@
|
|||
#include "../engine/SoundController.h"
|
||||
#include "../engine/Consts.h"
|
||||
|
||||
struct FireInformation {
|
||||
const std::map<ObjectNameTag, double> damagedPlayers;
|
||||
const bool shot;
|
||||
};
|
||||
|
||||
class Weapon : public RigidBody {
|
||||
protected:
|
||||
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::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:
|
||||
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();
|
||||
|
||||
[[nodiscard]] std::pair<double, double> balance() const{ return std::make_pair(_clipAmmo, _stockAmmo); }
|
||||
|
|
Loading…
Reference in New Issue