Initial commit

master
Vectozavr 2021-09-13 19:53:43 +07:00
commit 7e5c96c123
138 changed files with 12511 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
# Project exclude paths
/cmake-build-debug/
/cmake-build-release/

17
Ak47.cpp Executable file
View File

@ -0,0 +1,17 @@
//
// Created by Иван Ильин on 02.06.2021.
//
#include <ResourceManager.h>
#include "Ak47.h"
using namespace std;
Ak47::Ak47(int ammo, const std::string& weaponName) : Weapon(weaponName, "../obj/ak47.obj", "../obj/ak47_mat.txt", Point4D{3, 3, 3}, Point4D{-0.8, 1.3, 0.3}, Point4D{0, M_PI, 0}) {
fireSound.setBuffer(*ResourceManager::loadSoundBuffer("../sound/weapons/ak47.ogg"));
reloadSound.setBuffer(*ResourceManager::loadSoundBuffer("../sound/weapons/reload_ak47.ogg"));
_stockAmmo = ammo - _clipCapacity;
_fireDelay = 0.1;
}

16
Ak47.h Executable file
View File

@ -0,0 +1,16 @@
//
// Created by Иван Ильин on 02.06.2021.
//
#ifndef SHOOTER_AK47_H
#define SHOOTER_AK47_H
#include "Weapon.h"
class Ak47 : public Weapon {
public:
explicit Ak47(int ammo = 100, const std::string& weaponName = "ak47");
};
#endif //SHOOTER_3DZAVR_AK47_H

13
Bonus.cpp Executable file
View File

@ -0,0 +1,13 @@
//
// Created by Иван Ильин on 05.06.2021.
//
#include "Bonus.h"
Bonus::Bonus(const std::string &bonusName, const std::string &filename, const std::string &materials, const Point4D &scale) {
_name = bonusName;
loadObj(filename, materials, scale);
setCollider(false);
a_rotate("a_rotation", Point4D{0, 2*M_PI, 0}, 4, Animation::Continue, Animation::linear);
}

21
Bonus.h Executable file
View File

@ -0,0 +1,21 @@
//
// Created by Иван Ильин on 05.06.2021.
//
#ifndef SHOOTER_BONUS_H
#define SHOOTER_BONUS_H
#include "World.h"
#include "Player.h"
class Bonus : public Mesh {
protected:
std::string _name;
public:
explicit Bonus(const std::string &bonusName, const std::string& filename, const std::string &materials = "", const Point4D& scale = Point4D{1, 1, 1});
[[nodiscard]] std::string name() const { return _name; }
};
#endif //SHOOTER_3DZAVR_BONUS_H

105
CMakeLists.txt Executable file
View File

@ -0,0 +1,105 @@
cmake_minimum_required(VERSION 3.17)
project(shooter)
set(CMAKE_CXX_STANDARD 20)
include_directories(engine)
add_executable(shooter
# game:
main.cpp
Player.cpp
Player.h
Client.cpp
Client.h
Server.cpp
Server.h
Weapon.cpp
Weapon.h
Ak47.cpp
Ak47.h
Shotgun.cpp
Shotgun.h
Gun.cpp
Gun.h
Bonus.cpp
Bonus.h
Gold_Ak47.h
Rifle.cpp
Rifle.h
# 3d engine:
engine/utils/Time.h
engine/utils/Time.cpp
engine/utils/Point4D.h
engine/utils/Point4D.cpp
engine/utils/Matrix4x4.h
engine/utils/Matrix4x4.cpp
engine/Triangle.h
engine/Triangle.cpp
engine/Mesh.h
engine/Mesh.cpp
engine/utils/Log.h
engine/utils/Log.cpp
engine/ResourceManager.h
engine/ResourceManager.cpp
engine/World.h
engine/World.cpp
engine/Camera.h
engine/Camera.cpp
engine/Screen.h
engine/Screen.cpp
engine/Engine.h
engine/Engine.cpp
engine/Plane.h
engine/Plane.cpp
engine/CameraController.h
engine/CameraController.cpp
engine/animation/Animatable.h
engine/animation/Animation.h
engine/animation/Interpolation.h
engine/animation/Animatable.cpp
engine/animation/Animation.cpp
engine/animation/ATranslate.h
engine/animation/AScale.h
engine/animation/ARotate.h
engine/animation/AWait.h
engine/physics/RigidBody.cpp
engine/physics/RigidBody.h
engine/physics/Simplex.h
engine/physics/Solver.cpp
engine/physics/Solver.h
engine/Object.h
engine/gui/Button.cpp
engine/gui/Button.h
engine/gui/Window.cpp
engine/gui/Window.h
engine/network/ClientUDP.cpp
engine/network/ClientUDP.h
engine/network/MsgType.cpp
engine/network/MsgType.h
engine/network/ReliableMsg.cpp
engine/network/ReliableMsg.h
engine/network/ServerUDP.cpp
engine/network/ServerUDP.h
engine/network/UDPConnection.cpp
engine/network/UDPConnection.h
engine/network/UDPSocket.cpp
engine/network/UDPSocket.h
engine/network/config.h
engine/animation/AFunction.h
)
if(${APPLE})
include_directories(/usr/local/include)
else()
set(SFML_DIR "C:/Libraries/SFML/lib/cmake/SFML")
set(SFML_STATIC_LIBRARIES TRUE)
endif()
find_package(SFML 2.5.1 COMPONENTS graphics audio REQUIRED)
if (SFML_FOUND)
include_directories(${SFML_INCLUDE_DIR})
endif()
target_link_libraries(shooter sfml-audio sfml-network sfml-graphics sfml-window sfml-system)

217
Client.cpp Executable file
View File

@ -0,0 +1,217 @@
//
// Created by Иван Ильин on 25.05.2021.
//
#include "Client.h"
#include "utils/Log.h"
#include "Bonus.h"
void Client::updatePacket() {
sf::Packet packet;
packet << MsgType::ClientUpdate << _player->position().x() << _player->position().y() << _player->position().z() << _player->angle().y() << _player->camera()->angleLeftUpLookAt().x();
_socket.send(packet, _socket.serverId());
}
void Client::processInit(sf::Packet& packet) {
sf::Uint16 targetId;
double buf[4];
while (packet >> targetId >> buf[0] >> buf[1] >> buf[2] >> buf[3])
{
if(targetId != _socket.ownId()) {
std::string name = "Player_" + std::to_string(targetId);
_players.insert({ targetId, std::make_shared<Player>() });
(*_world).addMesh(_players[targetId], name);
_players[targetId]->setVisible(true);
_players[targetId]->setAcceleration(Point4D{0, 0, 0});
// add head and other stuff:
_world->loadObj(name + "_head", "../obj/cube.obj", "",Point4D{0.7, 0.7, 0.7});
(*_world)[name + "_head"]->translate(Point4D{0, 2, 0});
(*_world)[name + "_head"]->setCollider(false);
_players[targetId]->attach((*_world)[name + "_head"]);
_world->loadObj(name + "_eye1", "../obj/cube.obj", "",Point4D{0.2, 0.2, 0.05});
(*_world)[name + "_eye1"]->translate(Point4D{0.3, 2.1, 0.7});
(*_world)[name + "_eye1"]->setCollider(false);
(*_world)[name + "_eye1"]->setColor({147, 159, 255});
(*_world)[name + "_head"]->attach((*_world)[name + "_eye1"]);
_world->loadObj(name + "_eye2", "../obj/cube.obj", "",Point4D{0.2, 0.2, 0.05});
(*_world)[name + "_eye2"]->translate(Point4D{-0.3, 2.1, 0.7});
(*_world)[name + "_eye2"]->setCollider(false);
(*_world)[name + "_eye2"]->setColor({147, 159, 255});
(*_world)[name + "_head"]->attach((*_world)[name + "_eye2"]);
_players[targetId]->translateToPoint(Point4D{ buf[0], buf[1], buf[2]});
_players[targetId]->setHealth(buf[3]);
}
}
}
void Client::processUpdate(sf::Packet& packet) {
sf::Uint16 targetId;
double buf[6];
while (packet >> targetId >> buf[0] >> buf[1] >> buf[2] >> buf[3] >> buf[4] >> buf[5]) {
if (_players.count(targetId)) {
std::string name = "Player_" + std::to_string(targetId);
_players[targetId]->translateToPoint(Point4D{buf[0], buf[1], buf[2]});
_players[targetId]->setHealth(buf[3]);
_players[targetId]->rotateToAngle(Point4D{0, buf[4], 0});
//(*_world)[name + "_head"]->rotateToAngle({buf[5], 2*buf[4], 0});
(*_world)[name + "_head"]->rotate(Matrix4x4::RotationY(buf[4]) * Point4D{1, 0, 0},
buf[5] - _players[targetId]->headAngle());
_players[targetId]->setHeadAngle(buf[5]);
} else if (targetId == _socket.ownId()) {
_player->setHealth(buf[3]);
}
}
}
void Client::processNewClient(sf::Packet& packet) {
sf::Uint16 targetId;
packet >> targetId;
std::string name = "Player_" + std::to_string(targetId);
_players.insert({ targetId, std::make_shared<Player>() });
_world->addMesh(_players[targetId], name);
_players[targetId]->setVisible(true);
_players[targetId]->setAcceleration(Point4D{0, 0, 0});
// add head and other stuff:
_world->loadObj(name + "_head", "../obj/cube.obj","",Point4D{0.7, 0.7, 0.7});
(*_world)[name + "_head"]->translate(Point4D{0, 2, 0});
(*_world)[name + "_head"]->setCollider(false);
_players[targetId]->attach((*_world)[name + "_head"]);
_world->loadObj(name + "_eye1", "../obj/cube.obj","",Point4D{0.2, 0.2, 0.05});
(*_world)[name + "_eye1"]->translate(Point4D{0.3, 2.1, 0.7});
(*_world)[name + "_eye1"]->setCollider(false);
(*_world)[name + "_eye1"]->setColor({147, 159, 255});
(*_world)[name + "_head"]->attach((*_world)[name + "_eye1"]);
_world->loadObj(name + "_eye2", "../obj/cube.obj", "",Point4D{0.2, 0.2, 0.05});
(*_world)[name + "_eye2"]->translate(Point4D{-0.3, 2.1, 0.7});
(*_world)[name + "_eye2"]->setCollider(false);
(*_world)[name + "_eye2"]->setColor({147, 159, 255});
(*_world)[name + "_head"]->attach((*_world)[name + "_eye2"]);
}
void Client::processDisconnect(sf::Uint16 targetId) {
if (targetId != _socket.ownId() && _players.count(targetId)) {
std::string name = "Player_" + std::to_string(targetId);
_world->removeMesh(name);
_world->removeMesh(name + "_head");
_world->removeMesh(name + "_eye1");
_world->removeMesh(name + "_eye2");
_players.erase(targetId);
}
}
void Client::processCustomPacket(MsgType type, sf::Packet& packet) {
int buff[3];
sf::Uint16 buffId[2];
double dbuff[10];
std::string tmp, tmp2;
Point4D p1, p2;
switch (type) {
case MsgType::Kill:
packet >> buffId[0] >> buffId[1];
if(buffId[0] == _socket.ownId()) {
_player->addDeath();
// respawn
_player->translateToPoint(Point4D{50.0*(-1 + 2.0*(double)rand()/RAND_MAX),30.0*(double)rand()/RAND_MAX,50.0*(-1 + 2.0*(double)rand()/RAND_MAX)});
_player->playDeath();
_player->initWeapons();
_player->setFullAbility();
}
else
_players[buffId[0]]->addDeath();
if(buffId[1] == _socket.ownId()) {
_player->addKill();
_player->playKill();
}
else
_players[buffId[1]]->addKill();
break;
case MsgType::FireTrace:
packet >> dbuff[0] >> dbuff[1] >> dbuff[2] >> dbuff[3] >> dbuff[4] >> dbuff[5];
p1 = Point4D(dbuff[0], dbuff[1], dbuff[2]);
p2 = Point4D(dbuff[3], dbuff[4], dbuff[5]);
tmp = "Client_fireTraces_" + std::to_string(fireTraces++);
_world->addMesh(std::make_shared<Mesh>(Mesh::LineTo(p1, p2, 0.05)), tmp);
(*_world)[tmp]->setCollider(false);
(*_world)[tmp]->a_color(tmp + "_fadeOut", {255, 255, 255, 0}, 1, Animation::None, Animation::linear);
(*_world)["Player_im"]->a_function(tmp + "delete", [this, tmp](){ deleteTrace(_world, tmp); }, 1, 2);
break;
case MsgType::InitBonuses:
while (packet >> tmp >> dbuff[0] >> dbuff[1] >> dbuff[2]) {
tmp2 = tmp.substr(6, tmp.size()-3-5);
_world->addMesh(std::make_shared<Bonus>(tmp, "../obj/" + tmp2 + ".obj", "../obj/" + tmp2 + "_mat.txt", Point4D{3, 3, 3}), tmp);
(*_world)[tmp]->translateToPoint(Point4D(dbuff[0], dbuff[1], dbuff[2]));
}
break;
case MsgType::AddBonus:
packet >> tmp >> dbuff[0] >> dbuff[1] >> dbuff[2];
tmp2 = tmp.substr(6, tmp.size()-3-5);
_world->addMesh(std::make_shared<Bonus>(tmp, "../obj/" + tmp2 + ".obj", "../obj/" + tmp2 + "_mat.txt", Point4D{3, 3, 3}), tmp);
(*_world)[tmp]->translateToPoint(Point4D(dbuff[0], dbuff[1], dbuff[2]));
break;
case MsgType::RemoveBonus:
packet >> tmp;
_world->removeMesh(tmp);
break;
}
}
void Client::processDisconnected() {
for (auto it = _players.begin(); it != _players.end();) {
std::string name = "Player_" + std::to_string(it->first);
_world->removeMesh(name);
_world->removeMesh(name + "_head");
_world->removeMesh(name + "_eye1");
_world->removeMesh(name + "_eye2");
_players.erase(it++);
}
}
void Client::damagePlayer(sf::Uint16 targetId, double damage) {
sf::Packet packet;
packet << MsgType::Damage << targetId << damage;
_socket.send(packet, _socket.serverId());
Log::log("Client: damagePlayer " + std::to_string(targetId) + " ( -" + std::to_string(damage) + "hp )");
}
void Client::addTrace(const Point4D& from, const Point4D& to) {
sf::Packet packet;
packet << MsgType::FireTrace << from.x() << from.y() << from.z() << to.x() << to.y() << to.z();
_socket.send(packet, _socket.serverId());
}
void Client::deleteTrace(const std::shared_ptr<World> &world, const std::string &traceName) {
world->removeMesh(traceName);
}
void Client::takeBonus(const std::string& bonusName) {
sf::Packet packet;
packet << MsgType::RemoveBonus << bonusName;
_socket.send(packet, _socket.serverId());
}

45
Client.h Executable file
View File

@ -0,0 +1,45 @@
//
// Created by Иван Ильин on 25.05.2021.
//
#ifndef SHOOTER_CLIENT_H
#define SHOOTER_CLIENT_H
#include <utility>
#include "network/ClientUDP.h"
#include "Player.h"
class Client : public ClientUDP {
private:
std::shared_ptr<Player> _player;
std::shared_ptr<World> _world;
std::map<sf::Uint16, std::shared_ptr<Player>> _players{};
int fireTraces = 0;
public:
Client(std::shared_ptr<Player> player, std::shared_ptr<World> world) : _player(std::move(player)), _world(std::move(world)) {};
void updatePacket() override;
void processInit(sf::Packet& packet) override;
void processUpdate(sf::Packet& packet) override;
void processNewClient(sf::Packet& packet) override;
void processDisconnect(sf::Uint16 targetId) override;
void processCustomPacket(MsgType type, sf::Packet& packet) override;
void processDisconnected() override;
void damagePlayer(sf::Uint16 targetId, double damage);
void takeBonus(const std::string& bonusName);
void addTrace(const Point4D& from, const Point4D& to);
void deleteTrace(const std::shared_ptr<World> &world, const std::string& traceName);
};
#endif //MINECRAFT_3DZAVR_CLIENT_H

27
Gold_Ak47.h Executable file
View File

@ -0,0 +1,27 @@
//
// Created by Иван Ильин on 05.06.2021.
//
#ifndef SHOOTER_GOLD_AK47_H
#define SHOOTER_GOLD_AK47_H
#include "Weapon.h"
class Gold_Ak47 : public Weapon {
public:
explicit Gold_Ak47(int ammo = 200, const std::string& weaponName = "gold_ak47") : Weapon(weaponName, "../obj/ak47.obj", "../obj/gold_ak47_mat.txt", Point4D{3, 3, 3}, Point4D{-0.8, 1.3, 0.3}, Point4D{0, M_PI, 0}) {
fireSound.setBuffer(*ResourceManager::loadSoundBuffer("../sound/weapons/ak47.ogg"));
reloadSound.setBuffer(*ResourceManager::loadSoundBuffer("../sound/weapons/reload_ak47.ogg"));
_initialPack = 200;
_spreading = 1.0;
_reloadTime = 1.5;
_clipCapacity = 60;
_stockAmmo = ammo - _clipCapacity;
_fireDelay = 0.05;
_damage = 600;
_clipAmmo = _clipCapacity;
}
};
#endif //SHOOTER_3DZAVR_GOLD_AK47_H

22
Gun.cpp Executable file
View File

@ -0,0 +1,22 @@
//
// Created by Иван Ильин on 03.06.2021.
//
#include <ResourceManager.h>
#include "Gun.h"
using namespace std;
Gun::Gun(int ammo, const std::string& weaponName) : Weapon(weaponName, "../obj/gun.obj", "../obj/gun_mat.txt", Point4D{3, 3, 3}, Point4D{-0.8, 1.3, 0.3}, Point4D{0, M_PI, 0}) {
fireSound.setBuffer(*ResourceManager::loadSoundBuffer("../sound/weapons/gun.ogg"));
reloadSound.setBuffer(*ResourceManager::loadSoundBuffer("../sound/weapons/reload_gun.ogg"));
_initialPack = 30;
_clipCapacity = 6; // how much ammo can be stored in one clip
_stockAmmo = ammo - _clipCapacity; // how much ammo do you have in stock
_clipAmmo = _clipCapacity; // how much ammo do you have in current clip
_reloadTime = 2;
_fireDelay = 0.3; // time delay between fires
_damage = 800;
_spreading = 3.0;
}

16
Gun.h Executable file
View File

@ -0,0 +1,16 @@
//
// Created by Иван Ильин on 03.06.2021.
//
#ifndef SHOOTER_GUN_H
#define SHOOTER_GUN_H
#include "Weapon.h"
class Gun : public Weapon {
public:
explicit Gun(int ammo = 30, const std::string& weaponName = "gun");
};
#endif //SHOOTER_3DZAVR_GUN_H

343
Player.cpp Executable file
View File

@ -0,0 +1,343 @@
//
// Created by Иван Ильин on 14.03.2021.
//
#include "Player.h"
#include "Screen.h"
#include "ResourceManager.h"
#include "utils/Log.h"
void Player::update() {
if(inCollision())
p_velocity -= p_velocity*Time::deltaTime()*2;
if(isInSlowMo) {
if(_ability > 0)
_ability -= Time::deltaTime();
else {
_ability = 0;
isInSlowMo = false;
setVelocity(velocity()*slowMoCoefficient);
setAcceleration(p_acceleration*slowMoCoefficient*slowMoCoefficient);
slowMoSound.stop();
unSlowMoSound.play();
}
}
double coeff = isInSlowMo ? 1.0/slowMoCoefficient : 1.0;
bool inRunning_old = inRunning;
inRunning = _screen != nullptr && (Screen::isKeyPressed(sf::Keyboard::A) || Screen::isKeyPressed(sf::Keyboard::D) ||Screen::isKeyPressed(sf::Keyboard::W) || Screen::isKeyPressed(sf::Keyboard::S));
// in case when the camera is attached we make some animation during running
if(_camera != nullptr && inRunning && !_camera->isInAnim()) {
_camera->a_translate("hor_oscil", -_camera->left()/12, 0.3/coeff, Animation::LoopOut::None, Animation::cos);
_camera->a_wait("hor_oscil", 0);
_camera->a_translate("hor_oscil", _camera->left()/12, 0.3/coeff, Animation::LoopOut::None, Animation::cos);
_camera->a_translate("vert_oscil", -Point4D{0, 1, 0}/24, 0.15/coeff, Animation::LoopOut::None, Animation::cos);
_camera->a_wait("vert_oscil", 0);
_camera->a_translate("vert_oscil", Point4D{0, 1, 0}/24, 0.15/coeff, Animation::LoopOut::None, Animation::cos);
_camera->a_wait("vert_oscil", 0);
_camera->a_translate("vert_oscil", -Point4D{0, 1, 0}/24, 0.15/coeff, Animation::LoopOut::None, Animation::cos);
_camera->a_wait("vert_oscil", 0);
_camera->a_translate("vert_oscil", Point4D{0, 1, 0}/24, 0.15/coeff, Animation::LoopOut::None, Animation::cos);
_camera->a_translateToPoint("init", position() + Point4D{0, 1.8, 0}, 0.3/coeff, Animation::None, Animation::cos);
} else if(_camera != nullptr && inRunning_old && !inRunning) {
_camera->a_stopAllAnimations();
_camera->a_translateToPoint("init", position() + Point4D{0, 1.8, 0}, 0.15/coeff, Animation::None, Animation::cos);
}
auto rayToFloor = (*_world).rayCast(position(), position() + Point4D{0, -5, 0});
if(_world != nullptr && _screen != nullptr && _camera != nullptr) {
// Left and right
if (Screen::isKeyPressed(sf::Keyboard::A)) {
translate(_camera->left() * Time::deltaTime() * walkSpeed * coeff);
if(inCollision())
setVelocity(Point4D{0,0,0});
}
if (Screen::isKeyPressed(sf::Keyboard::D)) {
translate(-_camera->left() * Time::deltaTime() * walkSpeed * coeff);
if(inCollision())
setVelocity(Point4D{0,0,0});
}
// Forward and backward
if (Screen::isKeyPressed(sf::Keyboard::W)) {
translate(_camera->left().cross3D(Point4D{0, 1, 0}) * Time::deltaTime() * walkSpeed * coeff);
if(inCollision())
setVelocity(Point4D{0,0,0});
}
if (Screen::isKeyPressed(sf::Keyboard::S)) {
translate(-_camera->left().cross3D(Point4D{0, 1, 0}) * Time::deltaTime() * walkSpeed * coeff);
if(inCollision())
setVelocity(Point4D{0,0,0});
}
if (_ability > 0 && !isInSlowMo && Screen::isKeyPressed(sf::Keyboard::LShift)) {
// slow mo
isInSlowMo = true;
setVelocity(velocity()/slowMoCoefficient);
setAcceleration(p_acceleration/(slowMoCoefficient*slowMoCoefficient));
unSlowMoSound.stop();
slowMoSound.play();
} else if (isInSlowMo && !Screen::isKeyPressed(sf::Keyboard::LShift)) {
isInSlowMo = false;
setVelocity(velocity()*slowMoCoefficient);
setAcceleration(p_acceleration*slowMoCoefficient*slowMoCoefficient);
slowMoSound.stop();
unSlowMoSound.play();
}
if (Screen::isKeyPressed(sf::Keyboard::Space) && inCollision()) {
addVelocity(Point4D{0, std::abs(_collisionNormal.y())*sqrt(2 * g * jumpHeight)*coeff, 0});
translate(Point4D{0, Time::deltaTime() * walkSpeed * 2 * coeff, 0});
}
// Mouse movement
Point4D disp = _screen->getMouseDisplacement();
rotate(Point4D{0, -disp.x() / 1000.0, 0});
p_velocity = Matrix4x4::RotationY(-disp.x() / 1000.0)*p_velocity;
double rotationLeft = disp.y() / 1000.0;
// You can only see in range [-90 : 90] grad
if (_camera->angleLeftUpLookAt().x() + rotationLeft > M_PI / 2)
rotationLeft = M_PI / 2 - _camera->angleLeftUpLookAt().x();
if (_camera->angleLeftUpLookAt().x() + rotationLeft < -M_PI / 2)
rotationLeft = -M_PI / 2 - _camera->angleLeftUpLookAt().x();
_camera->rotateLeft(rotationLeft);
rotateWeaponsRelativePoint(position() + Point4D{0, 1.8, 0}, _camera->left(), rotationLeft);
if (_screen->isKeyTapped(sf::Keyboard::Right) || _screen->isKeyTapped(sf::Keyboard::E)) {
if(_weapons.size() > 1) {
// change '_selectedWeapon'
_weapons[_selectedWeapon]->removeFromWorld(_world);
_selectedWeapon = (_selectedWeapon + 1) % _weapons.size();
_weapons[_selectedWeapon]->addToWorld(_world);
Log::log("selected _selectedWeapon " + std::to_string(_selectedWeapon));
changeWeaponSound.play();
}
}
if (_screen->isKeyTapped(sf::Keyboard::Left) || _screen->isKeyTapped(sf::Keyboard::Q)) {
if(_weapons.size() > 1) {
// change '_selectedWeapon'
_weapons[_selectedWeapon]->removeFromWorld(_world);
if (_selectedWeapon > 0)
_selectedWeapon = (_selectedWeapon - 1) % _weapons.size();
else
_selectedWeapon = _weapons.size() - 1;
_weapons[_selectedWeapon]->addToWorld(_world);
Log::log("selected _selectedWeapon " + std::to_string(_selectedWeapon));
changeWeaponSound.play();
}
}
if (_screen->isButtonPressed(sf::Mouse::Button::Left)) {
auto damagedPlayers = _weapons[_selectedWeapon]->fire(_world, _camera);
for(auto& damagedPlayer : damagedPlayers) {
sf::Uint16 targetId = std::stoi(damagedPlayer.first.substr(7));
damagePlayerCallBack(targetId, damagedPlayer.second);
}
}
if(Screen::isKeyPressed(sf::Keyboard::R)) {
_weapons[_selectedWeapon]->reload();
}
if (inRunning && inCollision() && walkSound.getStatus() != sf::Sound::Status::Playing) {
if ((position() - rayToFloor.first).abs() < 2) {
int soundNum = round((double) rand() / RAND_MAX * 5) + 1;
walkSound.setBuffer(*ResourceManager::loadSoundBuffer("../sound/stonestep" + std::to_string(soundNum) + ".ogg"));
//walkSound.setVolume(30);
walkSound.play();
}
}
}
oldVelocity = velocity();
}
void Player::rotateWeaponsRelativePoint(const Point4D& point4D, const Point4D& v, double val) {
for(auto& weapon : _weapons)
weapon->rotateRelativePoint(point4D, v, val);
}
void Player::drawStats() {
if(_screen == nullptr)
return;
// health bar
int xPos = 10;
int yPos = _screen->height() - 10 - 10;
int width = _screen->width()/2 - 20;
int height = 10;
sf::ConvexShape polygon1;
polygon1.setPointCount(4);
sf::ConvexShape polygon2;
polygon2.setPointCount(4);
polygon1.setPoint(0, sf::Vector2f((float)xPos, (float)yPos));
polygon1.setPoint(1, sf::Vector2f((float)(xPos + width), (float)yPos));
polygon1.setPoint(2, sf::Vector2f((float)(xPos + width), (float)(yPos + height)));
polygon1.setPoint(3, sf::Vector2f((float)xPos, (float)(yPos + height)));
polygon2.setPoint(0, sf::Vector2f((float)xPos, (float)yPos));
polygon2.setPoint(1, sf::Vector2f((float)xPos + width * _health / _healthMax, (float)yPos));
polygon2.setPoint(2, sf::Vector2f((float)xPos + width * _health / _healthMax, (float)(yPos + height)));
polygon2.setPoint(3, sf::Vector2f((float)xPos, (float)(yPos + height)));
polygon1.setFillColor({ 255, 174, 174, 100 });
polygon2.setFillColor({ static_cast<sf::Uint8>((_healthMax - _health)/_healthMax * 255), static_cast<sf::Uint8>(_health * 255 / _healthMax), 0, 100 });
polygon1.setOutlineThickness(3);
_screen->window.draw(polygon2);
// ability bar
sf::ConvexShape polygon3;
polygon3.setPointCount(4);
polygon3.setPoint(0, sf::Vector2f((float)xPos, (float)yPos - 15));
polygon3.setPoint(1, sf::Vector2f((float)xPos + width * _ability / _abilityMax, (float)yPos - 15));
polygon3.setPoint(2, sf::Vector2f((float)xPos + width * _ability / _abilityMax, (float)(yPos - 15 + height)));
polygon3.setPoint(3, sf::Vector2f((float)xPos, (float)(yPos - 15 + height)));
polygon3.setFillColor({ 255, 168, 168, 100 });
_screen->window.draw(polygon3);
// ammo
sf::Text text_health;
text_health.setFont(*ResourceManager::loadFont("../engine/fonts/Roboto-Medium.ttf"));
// text health
text_health.setCharacterSize(20);
text_health.setFillColor(sf::Color::White);
text_health.setStyle(sf::Text::Italic);
text_health.setPosition(0, 0);
text_health.setString(std::to_string((int)_health));
// text ammo
sf::Text text_ammo1(text_health);
sf::Text text_ammo2(text_health);
int ammo1Size = 100;
int ammo2Size = 50;
auto balance = _weapons[_selectedWeapon]->balance();
text_ammo1.setCharacterSize(ammo1Size);
text_ammo1.setString(std::to_string((int)balance.first));
text_ammo1.setPosition(150, _screen->height() - 50 - ammo1Size);
text_ammo1.setFillColor(sf::Color(0, 0, 0, 100));
_screen->window.draw(text_ammo1);
text_ammo2.setCharacterSize(ammo2Size);
text_ammo2.setString(std::to_string((int)balance.second));
text_ammo2.setPosition(50, _screen->height() - 50 - ammo2Size);
text_ammo2.setFillColor(sf::Color(0, 0, 0, 70));
_screen->window.draw(text_ammo2);
// text killSound/deathSound stats
sf::Text text_kills(text_health);
text_kills.setStyle(sf::Text::Bold);
text_kills.setString("KILLS: " + std::to_string((int)_kills) + " | " + "DEATHS: " + std::to_string((int)_deaths));
text_kills.setFillColor(sf::Color(0, 0, 0, 100));
text_kills.setCharacterSize(ammo2Size/2);
text_kills.setPosition(10, 10);
_screen->window.draw(text_kills);
sf::Text text_deaths(text_health);
text_deaths.setString(std::to_string((int)_deaths));
text_deaths.setFillColor(sf::Color(100, 0, 0, 100));
text_deaths.setCharacterSize(ammo2Size);
text_deaths.setPosition(10, ammo2Size + 10);
}
void Player::playDeath() {
deathSound.setBuffer(*ResourceManager::loadSoundBuffer("../sound/classic_hurt.ogg"));
deathSound.play();
}
void Player::playKill() {
killSound.setBuffer(*ResourceManager::loadSoundBuffer("../sound/kill.ogg"));
killSound.play();
}
void Player::collisionWithObject(const std::string &objName, const std::shared_ptr<Mesh> &obj) {
if(objName.find("Bonus_gun") != std::string::npos)
addWeapon(std::make_shared<Gun>());
if(objName.find("Bonus_shotgun") != std::string::npos)
addWeapon(std::make_shared<Shotgun>());
if(objName.find("Bonus_ak47") != std::string::npos)
addWeapon(std::make_shared<Ak47>());
if(objName.find("Bonus_gold_ak47") != std::string::npos)
addWeapon(std::make_shared<Gold_Ak47>());
if(objName.find("Bonus_rifle") != std::string::npos)
addWeapon(std::make_shared<Rifle>());
if(objName.find("Bonus_hill") != std::string::npos)
setFullHealth();
if(objName.find("Bonus_ability") != std::string::npos)
setFullAbility();
if(objName.find("Bonus") != std::string::npos) {
_world->removeMesh(objName);
takeBonusCallBack(objName);
}
}
void Player::addWeapon(const std::shared_ptr<Weapon> &weapon) {
changeWeaponSound.play();
if(!_weapons.empty()) {
for(auto& w : _weapons) {
if (w->name() == weapon->name()) {
w->addAmmo(w->initialPack());
return;
}
}
}
_weapons.push_back(weapon);
_weapons.back()->attachToPlayer(*this);
_weapons.back()->translate(position());
_weapons.back()->rotateRelativePoint(position() + Point4D{0, 1.8, 0}, Point4D{0, 1, 0}, p_angle.y());
_weapons.back()->rotateRelativePoint(position() + Point4D{0, 1.8, 0}, _camera->left(), _camera->angleLeftUpLookAt().x());
_weapons.back()->setAddTraceCallBack(addTraceCallBack);
changeWeaponSound.play();
}
void Player::initWeapons() {
if(!_weapons.empty()) {
_weapons[_selectedWeapon]->removeFromWorld(_world);
_weapons.clear();
}
_selectedWeapon = 0;
addWeapon(std::make_shared<Gun>());
_weapons[_selectedWeapon]->addToWorld(_world);
}

165
Player.h Executable file
View File

@ -0,0 +1,165 @@
//
// Created by Иван Ильин on 14.03.2021.
//
#ifndef SHOOTER_PLAYER_H
#define SHOOTER_PLAYER_H
#include <SFML/Audio/Sound.hpp>
#include <utility>
#include <ResourceManager.h>
#include "Mesh.h"
#include "Camera.h"
#include "World.h"
#include "Ak47.h"
#include "Shotgun.h"
#include "Gun.h"
#include "Gold_Ak47.h"
#include "Rifle.h"
class Player : public Mesh{
private:
double _healthMax = 100;
double _health = _healthMax;
double _abilityMax = 10;
double _ability = _abilityMax;
double jumpHeight = 3;
double walkSpeed = 10;
double _headAngle = 0;
unsigned _kills = 0;
unsigned _deaths = 0;
double g = 35;
double slowMoCoefficient = 5;
bool isInSlowMo = false;
std::shared_ptr<Camera> _camera;
std::shared_ptr<Screen> _screen;
std::shared_ptr<World> _world;
bool inRunning = false;
// sounds
sf::Sound killSound;
sf::Sound deathSound;
sf::Sound walkSound;
sf::Sound changeWeaponSound;
sf::Sound slowMoSound;
sf::Sound unSlowMoSound;
sf::Sound fullHealthSound;
sf::Sound fullAbilitySound;
Point4D oldVelocity;
std::string _name = "im";
std::vector<std::shared_ptr<Weapon>> _weapons;
uint8_t _selectedWeapon = 0;
std::function<void(sf::Uint16 targetId, double)> damagePlayerCallBack;
std::function<void(const Point4D&, const Point4D&)> addTraceCallBack;
std::function<void(const std::string&)> takeBonusCallBack;
public:
Player() {
loadObj("../obj/cube.obj", "", Point4D{0.5, 1.9, 0.5});
setAcceleration(Point4D{0, -g, 0});
setCollision(true);
setVisible(false);
setColor({240, 168, 168});
};
void update();
void attachCamera(std::shared_ptr<Camera>& camera, std::shared_ptr<Screen> screen) {
_camera = camera;
_screen = std::move(screen);
camera->translateToPoint(position() + Point4D{0, 1.8, 0});
this->attach(camera);
}
void attachWorld(const std::shared_ptr<World>& world, const Point4D& pos = Point4D{0, 30, 0}) {
_world = world;
translate(pos);
initWeapons();
changeWeaponSound.setBuffer(*ResourceManager::loadSoundBuffer("../sound/weapons/change_weapon.ogg"));
slowMoSound.setBuffer(*ResourceManager::loadSoundBuffer("../sound/slow_mo.ogg"));
unSlowMoSound.setBuffer(*ResourceManager::loadSoundBuffer("../sound/unslow_mo.ogg"));
fullHealthSound.setBuffer(*ResourceManager::loadSoundBuffer("../sound/fullHealth.ogg"));
fullAbilitySound.setBuffer(*ResourceManager::loadSoundBuffer("../sound/fullAbility.ogg"));
setCollisionCallBack([this](const std::string& objName, const std::shared_ptr<Mesh>& obj) {collisionWithObject(objName, obj);});
}
void setHealth(double h) {
_health = h;
}
void setAbility(double a) {
_ability = a;
}
void setFullHealth() {
_health = _healthMax;
fullHealthSound.play();
}
void setFullAbility() {
_ability = _abilityMax;
fullAbilitySound.play();
}
void setDamagePlayerCallBack(std::function<void(sf::Uint16 targetId, double)> hit) {
damagePlayerCallBack = std::move(hit);
}
void setAddTraceCallBack(std::function<void(const Point4D&, const Point4D&)> add) {
addTraceCallBack = std::move(add);
}
void setTakeBonusCallBack(std::function<void(const std::string&)> take) {
takeBonusCallBack = std::move(take);
}
[[nodiscard]] double health() const { return _health; }
[[nodiscard]] std::string name() const { return "Player_" + _name; }
std::shared_ptr<Camera> camera() { return _camera; }
// This is for situation when you want to store the position of the head but you dont have attached camera
void setHeadAngle(double a) { _headAngle = a; }
[[nodiscard]] double headAngle() const { return _headAngle; };
void rotateWeaponsRelativePoint(const Point4D& point4D, const Point4D& v, double val);
void drawStats();
void addKill() { _kills++; }
void addDeath() { _deaths++; }
void playDeath();
void playKill();
void collisionWithObject(const std::string& objName, const std::shared_ptr<Mesh>& obj);
void addWeapon(const std::shared_ptr<Weapon>& weapon);
void initWeapons();
};
#endif //MINECRAFT_3DZAVR_PLAYER_H

71
README.md Executable file
View File

@ -0,0 +1,71 @@
# Shooter on self-written 3D engine
<h4>About:</h4>
Source code of simple shooter on [3Dzavr game engine](https://github.com/vectozavr/3dzavr)
![Project demonstration](img/gamePlay2.png)
<h4>Installation (Windows):</h4>
1) Download and install .exe file
download game: https://drive.google.com/file/d/1_8zUCwxyC-eLbqswM7OBXoNIAdzpUrkI/view
2) Write <b>ip</b> and <b>port</b> of server in <b>bin/connect.txt</b> file.
3) Write <b>port</b> of the server in <b>bin/server.txt</b> file (only for computer where the server will be running).
3) Enjoy gaming!
<h4>Control:</h4>
<b>SHIFT</b> - slow motion (this ability is not infinite: its bar is next to hp)
<b>E & Q </b> or keys <b> <- -> </b> - change weapon
<b>R</b> - recharge
Player control is standard.
<h4>Research source code:</h4>
1) Download and install OpenAL library for SFML sound support (in current version you can't setup this engine without OpenAL)
openal: https://openal.org/downloads/
2) Install SFML on your computer (<b>The compiler versions have to match 100%</b>):
sfml: https://www.sfml-dev.org/download.php
2) Open CLion or Visual Studio
CLion (recommended): https://www.jetbrains.com/clion/
Visual Studio: https://visualstudio.microsoft.com/ru/
3) Clone this repository
rep url: https://github.com/vectozavr/shooter
4) Built project
For any issues, please, create new issue in this repository.
Demos:
Online:
![Project demonstration](img/gamePlay4.png)
GamePlay:
![Project demonstration](img/gamePlay3.png)
![Project demonstration](img/gamePlay5.png)
![Project demonstration](img/gamePlay6.png)
![Project demonstration](img/gamePlay7.png)

20
Rifle.cpp Executable file
View File

@ -0,0 +1,20 @@
//
// Created by Иван Ильин on 06.06.2021.
//
#include <ResourceManager.h>
#include "Rifle.h"
Rifle::Rifle(int ammo, const std::string &weaponName) : Weapon(weaponName, "../obj/rifle.obj", "../obj/rifle_mat.txt", Point4D{3, 3, 3}, Point4D{-1.2, 1, 0.3}, Point4D{0, M_PI, 0}) {
fireSound.setBuffer(*ResourceManager::loadSoundBuffer("../sound/weapons/shotgun.ogg"));
reloadSound.setBuffer(*ResourceManager::loadSoundBuffer("../sound/weapons/reload_ak47.ogg"));
_initialPack = 5;
_clipCapacity = 1; // how much ammo can be stored in one clip
_stockAmmo = ammo - _clipCapacity; // how much ammo do you have in stock
_clipAmmo = _clipCapacity; // how much ammo do you have in current clip
_reloadTime = 1;
_fireDelay = 1; // time delay between fires
_damage = 30000;
_spreading = 0.5;
}

16
Rifle.h Executable file
View File

@ -0,0 +1,16 @@
//
// Created by Иван Ильин on 06.06.2021.
//
#ifndef SHOOTER_RIFLE_H
#define SHOOTER_RIFLE_H
#include "Weapon.h"
class Rifle : public Weapon {
public:
explicit Rifle(int ammo = 5, const std::string& weaponName = "rifle");
};
#endif //SHOOTER_3DZAVR_RIFLE_H

166
Server.cpp Executable file
View File

@ -0,0 +1,166 @@
//
// Created by Иван Ильин on 25.05.2021.
//
#include "Server.h"
#include "utils/Log.h"
void Server::broadcast() {
sf::Packet updatePacket;
updatePacket << MsgType::Update;
for (auto& player : _players)
{
//player.second->setHealth(player.second->health() + (Time::time() - _lastBroadcast)/100);
updatePacket << player.first << player.second->position().x() << player.second->position().y() << player.second->position().z() << player.second->health() << player.second->angle().y() << player.second->headAngle();
}
for (auto& player : _players)
{
_socket.send(updatePacket, player.first);
}
}
void Server::processConnect(sf::Uint16 targetId) {
sf::Packet sendPacket1, sendPacket2;
sf::Packet extraPacket;
// players init
extraPacket << MsgType::NewClient << targetId;
sendPacket1 << MsgType::Init << targetId;
_players.insert({ targetId, std::make_shared<Player>() });
for (const auto& player : _players)
{
sendPacket1 << player.first << player.second->position().x() << player.second->position().y() << player.second->position().z() << player.second->health();
if (player.first != targetId)
_socket.sendRely(extraPacket, player.first);
}
_socket.sendRely(sendPacket1, targetId);
// bonuses init
sendPacket2 << MsgType::InitBonuses;
for(auto& bonus : _bonuses) {
if(bonus.second.onTheMap)
sendPacket2 << bonus.first << bonus.second.position.x() << bonus.second.position.y() << bonus.second.position.z();
}
_socket.sendRely(sendPacket2, targetId);
}
void Server::processClientUpdate(sf::Uint16 senderId, sf::Packet& packet) {
double buf[5];
packet >> buf[0] >> buf[1] >> buf[2] >> buf[3] >> buf[4];
_players.at(senderId)->translateToPoint(Point4D{ buf[0], buf[1], buf[2] });
_players.at(senderId)->rotateToAngle(Point4D{0, buf[3], 0});
_players.at(senderId)->setHeadAngle(buf[4]);
}
void Server::processDisconnect(sf::Uint16 senderId) {
sf::Packet sendPacket;
sendPacket << MsgType::Disconnect << senderId;
_players.erase(senderId);
for (const auto& player : _players)
_socket.sendRely(sendPacket, player.first);
}
void Server::processCustomPacket(MsgType type, sf::Packet& packet, sf::Uint16 senderId) {
sf::Packet sendPacket;
int buff[3];
double dbuff[10];
sf::Uint16 targetId;
double damage;
std::string tmp;
double newHealth;
switch (type) {
case MsgType::Damage:
packet >> targetId >> damage;
newHealth = _players[targetId]->health() - damage;
if(newHealth > 0) {
_players[targetId]->setHealth(newHealth);
}
else {
_players[targetId]->setFullHealth();
_players[targetId]->setFullAbility();
_players[targetId]->addDeath();
_players[senderId]->addKill();
sendPacket << MsgType::Kill << targetId << senderId;
for (auto& player : _players)
_socket.send(sendPacket, player.first);
}
break;
case MsgType::FireTrace:
packet >> dbuff[0] >> dbuff[1] >> dbuff[2] >> dbuff[3] >> dbuff[4] >> dbuff[5];
sendPacket << MsgType::FireTrace << dbuff[0] << dbuff[1] << dbuff[2] << dbuff[3] << dbuff[4] << dbuff[5];
for (auto& player : _players) {
if(player.first != senderId)
_socket.send(sendPacket, player.first);
}
break;
case MsgType::RemoveBonus:
packet >> tmp;
if(tmp.find("Bonus_hill") != std::string::npos) {
_players[senderId]->setFullHealth();
}
if(tmp.find("Bonus_ability") != std::string::npos) {
_players[senderId]->setFullAbility();
}
_bonuses[tmp].onTheMap = false;
_bonuses[tmp].lastTake = Time::time();
sendPacket << MsgType::RemoveBonus << tmp;
for (auto& player : _players) {
if(player.first != senderId)
_socket.send(sendPacket, player.first);
}
break;
}
}
void Server::processStop() {
for (auto it = _players.begin(); it != _players.end();)
_players.erase(it++);
}
void Server::generateBonuses() {
_bonuses.insert({"Bonus_gun_1", {Point4D(-10, -2, -15), -2*_bonusRechargeTime, true}});
_bonuses.insert({"Bonus_gun_2", {Point4D(10, -2, 15), -2*_bonusRechargeTime, true}});
_bonuses.insert({"Bonus_shotgun_1", {Point4D(-10, 13, -24), -2*_bonusRechargeTime, true}});
_bonuses.insert({"Bonus_shotgun_2", {Point4D(10, 13, 24), -2*_bonusRechargeTime, true}});
_bonuses.insert({"Bonus_ak47_1", {Point4D(-25, 30, 50), -2*_bonusRechargeTime, true}});
_bonuses.insert({"Bonus_ak47_2", {Point4D(25, 30, -50), -2*_bonusRechargeTime, true}});
_bonuses.insert({"Bonus_gold_ak47_1", {Point4D(-35, 80, 25), -2*_bonusRechargeTime, true}});
_bonuses.insert({"Bonus_gold_ak47_2", {Point4D(35, 80, -25), -2*_bonusRechargeTime, true}});
_bonuses.insert({"Bonus_rifle_1", {Point4D(40, -2, 45), -2*_bonusRechargeTime, true}});
_bonuses.insert({"Bonus_rifle_2", {Point4D(-40, -2, -45), -2*_bonusRechargeTime, true}});
_bonuses.insert({"Bonus_hill_1", {Point4D(-40, -2, 45), -2*_bonusRechargeTime, true}});
_bonuses.insert({"Bonus_hill_2", {Point4D(40, -2, -45), -2*_bonusRechargeTime, true}});
_bonuses.insert({"Bonus_ability_1", {Point4D(25, 18, -33), -2*_bonusRechargeTime, true}});
_bonuses.insert({"Bonus_ability_2", {Point4D(-25, 18, 33), -2*_bonusRechargeTime, true}});
}
void Server::updateInfo() {
for(auto& bonus : _bonuses) {
if(!bonus.second.onTheMap && abs(Time::time() - bonus.second.lastTake) > _bonusRechargeTime) {
sf::Packet sendPacket;
sendPacket << MsgType::AddBonus << bonus.first << bonus.second.position.x() << bonus.second.position.y() << bonus.second.position.z();
for (const auto& player : _players)
_socket.sendRely(sendPacket, player.first);
bonus.second.onTheMap = true;
}
}
}

43
Server.h Executable file
View File

@ -0,0 +1,43 @@
//
// Created by Иван Ильин on 25.05.2021.
//
#ifndef SHOOTER_SERVER_H
#define SHOOTER_SERVER_H
#include "network/ServerUDP.h"
#include "Player.h"
#include "Bonus.h"
struct BonusInfo {
Point4D position;
double lastTake;
bool onTheMap;
};
class Server : public ServerUDP {
private:
std::map<sf::Uint16, std::shared_ptr<Player>> _players{};
std::map<std::string, BonusInfo> _bonuses{};
double _bonusRechargeTime = 60;
public:
Server() = default;
void broadcast() override;
void processConnect(sf::Uint16 senderId) override;
void processClientUpdate(sf::Uint16 senderId, sf::Packet& packet) override;
void processDisconnect(sf::Uint16 senderId) override;
void processCustomPacket(MsgType type, sf::Packet& packet, sf::Uint16 senderId) override;
void processStop() override;
void generateBonuses();
void updateInfo() override;
};
#endif //MINECRAFT_3DZAVR_SERVER_H

56
Shotgun.cpp Executable file
View File

@ -0,0 +1,56 @@
//
// Created by Иван Ильин on 02.06.2021.
//
#include <ResourceManager.h>
#include "Shotgun.h"
using namespace std;
Shotgun::Shotgun(int ammo, const std::string& weaponName) : Weapon(weaponName, "../obj/shotgun.obj", "../obj/shotgun_mat.txt", Point4D{3, 3, 3}, Point4D{-0.95, 1.3, -0.6}, Point4D{0, M_PI, 0}) {
fireSound.setBuffer(*ResourceManager::loadSoundBuffer("../sound/weapons/shotgun.ogg"));
reloadSound.setBuffer(*ResourceManager::loadSoundBuffer("../sound/weapons/reload_shotgun.ogg"));
//reloadSound.setVolume(30);
_initialPack = 15;
_clipCapacity = 1; // how much ammo can be stored in one clipx
_stockAmmo = ammo - _clipCapacity; // how much ammo do you have in stock
_clipAmmo = _clipCapacity; // how much ammo do you have in current clip
_reloadTime = 1;
_fireDelay = 1; // time delay between fires
_damage = 400;
_spreading = 5;
}
std::map<std::string, double>
Shotgun::processFire(const std::shared_ptr<World> &world, const std::shared_ptr<Camera> &camera) {
std::map<std::string, double> damagedPlayers;
for(int i = 0; i < 15; i++) {
//generate random vector
Point4D randV(10*_spreading*(1.0 - 2.0*(double)rand()/RAND_MAX), 10*_spreading*(1.0 - 2.0*(double)rand()/RAND_MAX), 10*_spreading*(1.0 - 2.0*(double)rand()/RAND_MAX));
// damage player
auto rayCast = world->rayCast(camera->position(), camera->position() + camera->lookAt() * 1000 + randV);
if (rayCast.second.find("Player") != std::string::npos) {
damagedPlayers[rayCast.second] += _damage / (1.0 + (camera->position() - rayCast.first).abs());
}
// add trace line
Point4D to = rayCast.first.w() == -1 ? camera->position() + camera->lookAt() * 1000 + randV: rayCast.first;
string traceName = _name + "_trace_nr_" + std::to_string(fireTraces++);
Point4D from = _objects[_name + "_" + to_string(9)]->position() +
_objects[_name + "_" + to_string(9)]->triangles()[0][0];
world->addMesh(make_shared<Mesh>(Mesh::LineTo(from, to, 0.05)), traceName);
(*world)[traceName]->setCollider(false);
// remove trace line after some time
(*world)[traceName]->a_color("color_trace", {255, 255, 255, 0}, 1, Animation::None, Animation::linear);
(*world)["map_0"]->a_function(traceName + "delete", [world, traceName]() { deleteTrace(world, traceName); },
1, 2);
addTraceCallBack(from, to);
}
return damagedPlayers;
}

17
Shotgun.h Executable file
View File

@ -0,0 +1,17 @@
//
// Created by Иван Ильин on 02.06.2021.
//
#ifndef SHOOTER_SHOTGUN_H
#define SHOOTER_SHOTGUN_H
#include "Weapon.h"
class Shotgun : public Weapon {
public:
explicit Shotgun(int ammo = 15, const std::string& weaponName = "shotgun");
std::map<std::string, double> processFire(const std::shared_ptr<World>& world, const std::shared_ptr<Camera>& camera) override;
};
#endif //SHOOTER_3DZAVR_SHOTGUN_H

127
Weapon.cpp Executable file
View File

@ -0,0 +1,127 @@
//
// Created by Иван Ильин on 01.06.2021.
//
#include <sstream>
#include "Weapon.h"
#include "ResourceManager.h"
#include "utils/Log.h"
using namespace std;
Weapon::Weapon(const std::string& weaponName, const std::string& objFileName, const std::string& matFileName, const Point4D& scale, const Point4D& translate, const Point4D& rotate) {
_name = weaponName;
auto objs = Mesh::LoadObjects(objFileName, matFileName, scale);
for(int i = 0; i < objs.size(); i++) {
string meshName = _name + "_" + to_string(i);
objs[i]->setCollider(false);
//transforms
objs[i]->rotate(rotate);
objs[i]->translate(translate);
_objects.insert({meshName, objs[i]});
}
noAmmoSound.setBuffer(*ResourceManager::loadSoundBuffer("../sound/weapons/no_ammo.ogg"));
}
std::map<std::string, double> Weapon::fire(const std::shared_ptr<World>& world, const std::shared_ptr<Camera>& camera) {
if(_clipAmmo == 0) {
reload();
if(_clipAmmo == 0)
noAmmoSound.play();
}
if(_clipAmmo <= 0 || abs(Time::time() - _lastFireTime) < _fireDelay || abs(Time::time() - _lastReloadTime) < _reloadTime)
return std::map<std::string, double>();
_lastFireTime = Time::time();
_clipAmmo--;
fireSound.play();
Log::log("Weapon::fire (" + std::to_string(_stockAmmo) + " : " + std::to_string(_clipAmmo) + ")");
return processFire(world, camera);
}
void Weapon::reload() {
if (_stockAmmo == 0 || abs(Time::time() - _lastReloadTime) < _reloadTime)
return;
if(_clipCapacity - _clipAmmo <= _stockAmmo) {
_stockAmmo -= _clipCapacity - _clipAmmo;
_clipAmmo = _clipCapacity;
} else {
_clipAmmo += _stockAmmo;
_stockAmmo = 0;
}
reloadSound.play();
Log::log("Weapon::reload (" + std::to_string(_stockAmmo) + " : " + std::to_string(_clipAmmo) + ")");
_lastReloadTime = Time::time();
}
void Weapon::addToWorld(const shared_ptr<World> &world) {
for(auto& obj : _objects) {
world->addMesh(obj.second, obj.first);
}
}
void Weapon::removeFromWorld(const shared_ptr<World> &world) {
for(auto& obj : _objects) {
world->removeMeshInstantly(obj.first);
}
}
void Weapon::attachToPlayer(Mesh &player) {
for(auto& obj : _objects) {
player.attach(obj.second);
}
}
void Weapon::rotate(const Point4D& point4D, double val) {
for(auto& mesh : _objects)
mesh.second->rotate(point4D, val);
}
void Weapon::translate(const Point4D &point4D) {
for(auto& mesh : _objects)
mesh.second->translate(point4D);
}
void Weapon::deleteTrace(const shared_ptr<World> &world, const std::string& traceName) {
world->removeMesh(traceName);
}
void Weapon::rotateRelativePoint(const Point4D &point4D, const Point4D &v, double val) {
for(auto& mesh : _objects)
mesh.second->rotateRelativePoint(point4D, v, val);
}
std::map<std::string, double> Weapon::processFire(const shared_ptr<World> &world, const shared_ptr<Camera> &camera) {
std::map<std::string, double> damagedPlayers;
//generate random vector
Point4D randV(10.0*_spreading*(1.0 - 2.0*(double)rand()/RAND_MAX), 10.0*_spreading*(1.0 - 2.0*(double)rand()/RAND_MAX), 10.0*_spreading*(1.0 - 2.0*(double)rand()/RAND_MAX));
// damage player
auto rayCast = world->rayCast(camera->position(), camera->position() + camera->lookAt() * 1000 + randV);
if(rayCast.second.find("Player") != std::string::npos) {
damagedPlayers[rayCast.second] += _damage/(1.0 + (camera->position() - rayCast.first).abs());
}
// add trace line
Point4D to = rayCast.first.w() == -1 ? camera->position() + camera->lookAt() * 1000 + randV: rayCast.first;
string traceName = _name + "_trace_nr_" + std::to_string(fireTraces++);
Point4D from = _objects[_name + "_" + to_string(_objects.size()-1)]->position() + _objects[_name + "_" + to_string(_objects.size()-1)]->triangles()[0][0];
world->addMesh(make_shared<Mesh>(Mesh::LineTo(from, to, 0.05)), traceName);
(*world)[traceName]->setCollider(false);
// remove trace line after some time
(*world)[traceName]->a_color("color_trace", {255, 255, 255, 0}, 1, Animation::None, Animation::linear);
(*world)["Player_im"]->a_function(traceName + "delete", [world, traceName](){deleteTrace(world, traceName); }, 1, 2);
addTraceCallBack(from, to);
return damagedPlayers;
}

80
Weapon.h Executable file
View File

@ -0,0 +1,80 @@
//
// Created by Иван Ильин on 01.06.2021.
//
#ifndef SHOOTER_WEAPON_H
#define SHOOTER_WEAPON_H
#include <string>
#include <World.h>
#include <Camera.h>
#include <SFML/Audio/Sound.hpp>
#include "Mesh.h"
#include "utils/Time.h"
class Weapon {
protected:
int _initialPack = 100; // how much ammo do you have when you find the weapon
int _clipCapacity = 30; // how much ammo can be stored in one clip
int _stockAmmo = _initialPack - _clipCapacity; // how much ammo do you have in stock
int _clipAmmo = _clipCapacity; // how much ammo do you have in current clip
double _reloadTime = 3;
double _fireDelay = 0.1; // time delay between fires
double _damage = 300;
double _spreading = 2.0;
[[maybe_unused]] double _firePower = M_PI/100;
std::string _name = "Weapon_name";
std::map<std::string, std::shared_ptr<Mesh>> _objects;
double _lastFireTime = -INFINITY;
double _lastReloadTime = -INFINITY;
sf::Sound fireSound;
sf::Sound reloadSound;
sf::Sound noAmmoSound;
static void deleteTrace(const std::shared_ptr<World> &world, const std::string& traceName);
int fireTraces = 0;
std::function<void(const Point4D&, const Point4D&)> addTraceCallBack;
public:
Weapon(const std::string& weaponName, const std::string& objFileName, const std::string& matFileName, const Point4D& scale, const Point4D& translate, const Point4D& rotate);
std::map<std::string, double> fire(const std::shared_ptr<World>& world, const std::shared_ptr<Camera>& camera);
void reload();
void addToWorld(const std::shared_ptr<World>& world);
void removeFromWorld(const std::shared_ptr<World>& world);
void attachToPlayer(Mesh& player);
[[nodiscard]] std::pair<double, double> balance() const{ return std::make_pair(_clipAmmo, _stockAmmo); }
void rotateRelativePoint(const Point4D& point4D, const Point4D& v, double val);
void rotate(const Point4D& point4D, double val);
void translate(const Point4D& point4D);
void setAddTraceCallBack(std::function<void(Point4D, Point4D)> add) {
addTraceCallBack = std::move(add);
}
[[nodiscard]] std::string name() const { return _name; }
void addAmmo(int ammoAdd) { _stockAmmo += ammoAdd; }
virtual std::map<std::string, double> processFire(const std::shared_ptr<World>& world, const std::shared_ptr<Camera>& camera);
[[nodiscard]] int initialPack() const {return _initialPack; }
};
#endif //SHOOTER_3DZAVR_WEAPON_H

256
engine/Camera.cpp Executable file
View File

@ -0,0 +1,256 @@
//
// Created by Иван Ильин on 14.01.2021.
//
#include "Camera.h"
#include "utils/Log.h"
std::vector<Triangle> &Camera::project(Mesh& mesh, Screen::ViewMode mode) {
if(!ready) {
Log::log("Camera::project(): cannot project tris without camera initialization ( Camera::init() ) ");
return this->triangles;
}
if(!mesh.isVisible())
return this->triangles;
// Model transform matrix: translate tris in the origin of mesh.
Matrix4x4 M = Matrix4x4::Translation(mesh.position());
VM = V * M;
// We don't want to waste time re-allocating memory every time
std::vector<Triangle> clippedTriangles, tempBuffer;
for(auto& t : mesh.triangles()) {
double dot = t.norm().dot((mesh.position() + t[0] - p_position).normalize());
if(dot > 0)
continue;
Triangle clipped[2];
// It needs to be cleared because it's reused through iterations. Usually it doesn't free memory.
clippedTriangles.clear();
// In the beginning we need to to translate triangle from world coordinate to our camera system:
// After that we apply clipping for all planes from clipPlanes
clippedTriangles.emplace_back(t * VM);
for(auto& plane : clipPlanes)
{
while(!clippedTriangles.empty())
{
clipped[0] = clippedTriangles.back();
clipped[1] = clipped[0];
clippedTriangles.pop_back();
int additional = plane.clip(clipped[0], clipped[1]);
for(int i = 0; i < additional; i++)
tempBuffer.emplace_back(clipped[i]);
}
clippedTriangles.swap(tempBuffer);
}
for(auto& clippedTriangle : clippedTriangles) {
if(mode != Screen::ViewMode::Clipped) {
clippedTriangle.color = sf::Color(clippedTriangle.color.r * (0.3 * std::abs(dot) + 0.7),
clippedTriangle.color.g * (0.3 * std::abs(dot) + 0.7),
clippedTriangle.color.b * (0.3 * std::abs(dot) + 0.7),
(mode == Screen::ViewMode::Transparency ||
mode == Screen::ViewMode::Normals) ? 100 : clippedTriangle.color.a);
}
// Finally its time to project our clipped colored triangle from 3D -> 2D
// and transform it's coordinate to screen space (in pixels):
clippedTriangle *= SP;
clippedTriangle[0] /= clippedTriangle[0].w();
clippedTriangle[1] /= clippedTriangle[1].w();
clippedTriangle[2] /= clippedTriangle[2].w();
triangles.emplace_back(clippedTriangle);
}
}
return this->triangles;
}
void Camera::init(int width, int height, double fov, double ZNear, double ZFar) {
// We need to init camera only after creation or changing width, height, fov, ZNear or ZFar.
// Because here we calculate matrix that does not change during the motion of _objects or camera
w = width; h = height;
aspect = (double)width / (double)height;
P = Matrix4x4::Projection(fov, aspect, ZNear, ZFar);
S = Matrix4x4::ScreenSpace(width, height);
SP = S * P; // screen-space-projections matrix
// This is planes for clipping tris.
// Motivation: we are not interest in tris that we cannot see.
clipPlanes.emplace_back(Plane(Point4D{0, 0, 1}, Point4D{0, 0, ZNear})); // near plane
clipPlanes.emplace_back(Plane(Point4D{0, 0, -1}, Point4D{0, 0, ZFar})); // far plane
double thetta1 = M_PI*fov*0.5/180.0;
double thetta2 = atan(aspect*tan(thetta1));
clipPlanes.emplace_back(Plane(Point4D{-cos(thetta2), 0, sin(thetta2)}, Point4D{0, 0, 0})); // left plane
clipPlanes.emplace_back(Plane(Point4D{cos(thetta2), 0, sin(thetta2)}, Point4D{0, 0, 0})); // right plane
clipPlanes.emplace_back(Plane(Point4D{0, cos(thetta1), sin(thetta1)}, Point4D{0, 0, 0})); // down plane
clipPlanes.emplace_back(Plane(Point4D{0, -cos(thetta1), sin(thetta1)},Point4D{0, 0, 0})); // up plane
ready = true;
Log::log("Camera::init(): camera successfully initialized.");
}
std::vector<Triangle> &Camera::sorted() {
// Sort tris from back to front
// This is some replacement for Z-buffer
std::sort(triangles.begin(), triangles.end(), [](Triangle &t1, Triangle &t2)
{
std::vector<double> v_z1({t1[0].z(), t1[1].z(), t1[2].z()});
std::vector<double> v_z2({t2[0].z(), t2[1].z(), t2[2].z()});
std::sort(v_z1.begin(), v_z1.end());
std::sort(v_z2.begin(), v_z2.end());
double a = 1;
double b = 1;
double c = 1;
double z1 = (a*v_z1[0] + b*v_z1[1] + c*v_z1[2]);
double z2 = (a*v_z2[0] + b*v_z2[1] + c*v_z2[2]);
return z1 > z2;
});
return triangles;
}
void Camera::record() {
// Cleaning all tris and recalculation of View matrix
// That is like preparation for new camera shot: we need to set
// the position of camera and insert new cartridge for photo.
triangles.clear();
V = Matrix4x4::View(p_left, p_up, p_lookAt, p_position);
}
void Camera::rotateX(double rx) {
p_angle = Point4D{p_angle.x() + rx, p_angle.y(), p_angle.z()};
p_left = Matrix4x4::RotationX(rx) * p_left;
p_up = Matrix4x4::RotationX(rx) * p_up;
p_lookAt = Matrix4x4::RotationX(rx) * p_lookAt;
}
void Camera::rotateY(double ry) {
p_angle = Point4D{p_angle.x(), p_angle.y() + ry, p_angle.z()};
p_left = Matrix4x4::RotationY(ry) * p_left;
p_up = Matrix4x4::RotationY(ry) * p_up;
p_lookAt = Matrix4x4::RotationY(ry) * p_lookAt;
}
void Camera::rotateZ(double rz) {
p_angle = Point4D{p_angle.x(), p_angle.y(), p_angle.z() + rz};
p_left = Matrix4x4::RotationZ(rz) * p_left;
p_up = Matrix4x4::RotationZ(rz) * p_up;
p_lookAt = Matrix4x4::RotationZ(rz) * p_lookAt;
}
void Camera::rotate(double rx, double ry, double rz) {
rotateX(rx);
rotateY(ry);
rotateZ(rz);
if(v_attached.empty())
return;
for(auto& attached : v_attached)
attached->rotateRelativePoint(position(), Point4D{rx, ry, rz});
}
void Camera::rotate(const Point4D& r) {
rotate(r.x(), r.y(), r.z());
}
void Camera::rotate(const Point4D& v, double rv) {
p_left = Matrix4x4::Rotation(v, rv) * p_left;
p_up = Matrix4x4::Rotation(v, rv) * p_up;
p_lookAt = Matrix4x4::Rotation(v, rv) * p_lookAt;
if(v_attached.empty())
return;
for(auto& attached : v_attached)
attached->rotateRelativePoint(position(), v, rv);
}
void Camera::rotateLeft(double rl) {
p_angleLeftUpLookAt = Point4D{p_angleLeftUpLookAt.x() + rl, p_angleLeftUpLookAt.y(), p_angleLeftUpLookAt.z()};
rotate(p_left, rl);
if(v_attached.empty())
return;
for(auto& attached : v_attached)
attached->rotateRelativePoint(position(), p_left, rl);
}
void Camera::rotateUp(double ru) {
p_angleLeftUpLookAt = Point4D{p_angleLeftUpLookAt.x(), p_angleLeftUpLookAt.y() + ru, p_angleLeftUpLookAt.z()};
rotate(p_up, ru);
if(v_attached.empty())
return;
for(auto& attached : v_attached)
attached->rotateRelativePoint(position(), p_up, ru);
}
void Camera::rotateLookAt(double rlAt) {
p_angleLeftUpLookAt = Point4D{p_angleLeftUpLookAt.x(), p_angleLeftUpLookAt.y(), p_angleLeftUpLookAt.z() + rlAt};
rotate(p_lookAt, rlAt);
if(v_attached.empty())
return;
for(auto& attached : v_attached)
attached->rotateRelativePoint(position(), p_lookAt, rlAt);
}
void Camera::rotateRelativePoint(const Point4D &s, double rx, double ry, double rz) {
p_angle += Point4D{rx, ry, rz};
// Translate XYZ by vector r1
Point4D r1 = p_position - s;
// In translated coordinate system we rotate camera and position
Point4D r2 = Matrix4x4::Rotation(rx, ry, rz)*r1;
rotate(rx, ry, rz);
// After rotation we translate XYZ by vector -r2 and recalculate position
p_position = s + r2;
if(v_attached.empty())
return;
for(auto& attached : v_attached)
attached->rotateRelativePoint(s, Point4D{rx, ry, rz});
}
void Camera::rotateRelativePoint(const Point4D &s, const Point4D &r) {
rotateRelativePoint(s, r.x(), r.y(), r.z());
}
void Camera::rotateRelativePoint(const Point4D &s, const Point4D &v, double r) {
// Translate XYZ by vector r1
Point4D r1 = p_position - s;
// In translated coordinate system we rotate camera and position
Point4D r2 = Matrix4x4::Rotation(v, r)*r1;
rotate(v, r);
// After rotation we translate XYZ by vector -r2 and recalculate position
p_position = s + r2;
if(v_attached.empty())
return;
for(auto& attached : v_attached)
attached->rotateRelativePoint(s, v, r);
}
void Camera::translateToPoint(const Point4D &point) {
translate(point - p_position);
}

98
engine/Camera.h Executable file
View File

@ -0,0 +1,98 @@
//
// Created by Иван Ильин on 14.01.2021.
//
#ifndef ENGINE_CAMERA_H
#define ENGINE_CAMERA_H
#include <vector>
#include "Screen.h"
#include "Plane.h"
#include "Mesh.h"
class Camera : public Object, public Animatable{
private:
Point4D p_angleLeftUpLookAt;
Point4D p_left = Point4D{1, 0, 0, 0}; // internal X
Point4D p_up = Point4D{0, 1, 0, 0}; // internal Y
Point4D p_lookAt = Point4D{0, 0, 1, 0}; // internal Z
Matrix4x4 S; // screen space matrix
Matrix4x4 P; // projections matrix
Matrix4x4 V; // camera matrix
double aspect = 0;
// To accelerate calculations we can use precalculated matrix that does not chance
Matrix4x4 SP; // screen-space-projections matrix
Matrix4x4 VM; // camera-model-animation matrix
std::vector<Triangle> triangles;
std::vector<Plane> clipPlanes;
bool ready = false;
double w = 0;
double h = 0;
public:
Camera() = default;
Camera(const Camera& camera) = delete;
void init(int width, int height, double fov = 110.0, double ZNear = 0.1, double ZFar = 5000.0);
std::vector<Triangle>& project(Mesh& mesh, Screen::ViewMode mode);
void record();
[[nodiscard]] int buffSize() const { return triangles.size(); }
std::vector<Triangle>& sorted();
[[nodiscard]] Point4D position() const override { return p_position; }
[[nodiscard]] Point4D angle() const override { return p_angle; }
[[nodiscard]] Point4D angleLeftUpLookAt() const { return p_angleLeftUpLookAt; }
[[nodiscard]] Point4D eye() const { return p_position; }
[[nodiscard]] Point4D left() const { return p_left; }
[[nodiscard]] Point4D right() const { return -p_left; }
[[nodiscard]] Point4D up() const { return p_up; }
[[nodiscard]] Point4D down() const { return -p_up; }
[[nodiscard]] Point4D lookAt() const { return p_lookAt; }
void translate(const Point4D& dv) override {
p_position += dv;
if(v_attached.empty())
return;
for(const auto& attached : v_attached)
attached->translate(dv);
}
void translate(double dx, double dy, double dz) {
translate(Point4D{dx, dy, dz});
}
void translateToPoint(const Point4D& point);
void rotateX(double rx);
void rotateY(double ry);
void rotateZ(double rz);
void rotate(double rx, double ry, double rz);
void rotate(const Point4D& r) override;
void rotate(const Point4D& v, double rv) override;
void rotateLeft(double rl);
void rotateUp(double ru);
void rotateLookAt(double rlAt);
// Rotate mesh around XYZ by (rx, ry, rz) radians relative val 'point4D'
void rotateRelativePoint(const Point4D& s, double rl, double ru, double rlAt);
// Rotate mesh around XYZ by (r.x, r.y, r.z) radians relative val 'point4D'
void rotateRelativePoint(const Point4D& s, const Point4D& r) override;
// Rotate mesh around normalised vector 'v' by 'r' radians relative val 'point4D'
void rotateRelativePoint(const Point4D& s, const Point4D& v, double r) override;
};
#endif //INC_3DZAVR_CAMERA_H

38
engine/CameraController.cpp Executable file
View File

@ -0,0 +1,38 @@
//
// Created by Иван Ильин on 23.01.2021.
//
#include "CameraController.h"
#include <utility>
CameraController::CameraController(std::shared_ptr<Camera> camera, std::shared_ptr<Screen> screen) : camera(std::move(camera)), screen(std::move(screen)) {
}
void CameraController::update() {
// Left and right
if (Screen::isKeyPressed(sf::Keyboard::A))
camera->translate(camera->left()*Time::deltaTime()*5.0);
if (Screen::isKeyPressed(sf::Keyboard::D))
camera->translate(-camera->left()*Time::deltaTime()*5.0);
// Forward and backward
if (Screen::isKeyPressed(sf::Keyboard::W))
camera->translate(camera->lookAt()*Time::deltaTime()*5.0);
if (Screen::isKeyPressed(sf::Keyboard::S))
camera->translate(-camera->lookAt()*Time::deltaTime()*5.0);
if (Screen::isKeyPressed(sf::Keyboard::LShift))
camera->translate(0.0, -Time::deltaTime()*5.0, 0);
if (Screen::isKeyPressed(sf::Keyboard::Space))
camera->translate(0.0, Time::deltaTime()*5.0, 0);
// Mouse movement
Point4D disp = screen->getMouseDisplacement();
camera->rotateY(-disp.x()/1000.0);
camera->rotateLeft(disp.y()/1000.0);
}

21
engine/CameraController.h Executable file
View File

@ -0,0 +1,21 @@
//
// Created by Иван Ильин on 23.01.2021.
//
#ifndef ENGINE_CAMERACONTROLLER_H
#define ENGINE_CAMERACONTROLLER_H
#include "Camera.h"
#include "Screen.h"
class CameraController {
private:
std::shared_ptr<Camera> camera;
std::shared_ptr<Screen> screen;
public:
CameraController(std::shared_ptr<Camera> camera, std::shared_ptr<Screen> screen);
void update();
};
#endif //INC_3DZAVR_CAMERACONTROLLER_H

105
engine/Engine.cpp Executable file
View File

@ -0,0 +1,105 @@
//
// Created by Иван Ильин on 14.01.2021.
//
#include "Engine.h"
#include "utils/Time.h"
#include <iostream>
#include "ResourceManager.h"
#include "physics/Solver.h"
Engine::Engine() {
screen = std::make_shared<Screen>();
world = std::make_shared<World>();
camera = std::make_shared<Camera>();
}
void Engine::create(int screenWidth, int screenHeight, const std::string &name, bool verticalSync, sf::Color background, sf::Uint32 style) {
screen->open(screenWidth, screenHeight, name, verticalSync, background, style);
Log::log("Engine::create(): started engine (" + std::to_string(screenWidth) + " x " + std::to_string(screenHeight) + ") with name '" + name + "'.");
Time::update();
start();
camera->init(screenWidth, screenHeight);
screen->getMouseDisplacement(); // We do it to set mouse position in the center (see how getMouseDisplacement() works)
while (screen->isOpen()) {
screen->clear();
Time::update();
screen->keyboardControl();
update(Time::deltaTime());
world->garbageCollector();
/* Project all mesh
* Here we project all tris for each mesh 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 'b_updateWorld' equal to false in setUpdateWorld(bool):
if(b_updateWorld) {
camera->record();
for (auto &m : world->objects()) {
m.second->a_update();
camera->project(*m.second, screen->mode());
m.second->updatePhysicsState();
// isCollision detection:
if (m.second->isCollision()) {
m.second->setInCollision(false);
m.second->setCollisionNormal(Point4D{0, 0, 0});
for (auto &obj : world->objects()) {
if(obj.first != m.first) {
std::pair<bool, Simplex> gjk = m.second->checkGJKCollision(obj.second);
if (gjk.first) {
if (obj.second->isCollider()) {
CollisionPoint epa = m.second->EPA(gjk.second, obj.second);
Solver::solveCollision(m.second, obj.second, epa);
}
if (m.second->collisionCallBack() != nullptr)
m.second->collisionCallBack()(obj.first, obj.second);
}
}
}
}
}
// draw projected mesh
for (auto &t : camera->sorted())
screen->triangle(t);
camera->a_update();
triPerSec = camera->buffSize() * Time::fps();
if (b_debugText) {
screen->debugText(name + "\n\n X: " +
std::to_string((camera->eye().x())) + "\n Y: " +
std::to_string((camera->eye().y())) + "\n Z: " +
std::to_string((camera->eye().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");
}
}
gui();
screen->display();
}
exit();
}
void Engine::exit() {
if(screen->isOpen()) {
screen->close();
if(screen->isRender())
screen->setRender(false);
}
ResourceManager::unloadAllResources();
Log::log("Engine::exit(): exit engine (" + std::to_string(screen->width()) + " x " + std::to_string(screen->height()) + ") with name '" + screen->title() + "'.");
}

41
engine/Engine.h Executable file
View File

@ -0,0 +1,41 @@
//
// Created by Иван Ильин on 14.01.2021.
//
#ifndef ENGINE_ENGINE_H
#define ENGINE_ENGINE_H
#include "Screen.h"
#include "World.h"
#include "Camera.h"
#include "utils/Log.h"
#include "CameraController.h"
class Engine {
protected:
std::shared_ptr<Screen> screen;
std::shared_ptr<World> world;
std::shared_ptr<Camera> camera;
double triPerSec = 0;
bool b_debugText = true;
bool b_updateWorld = true;
public:
Engine();
virtual ~Engine() = default;
void create(int screenWidth = 1920, int screenHeight = 1080, const std::string& name = "engine", bool verticalSync = true, sf::Color background = sf::Color(255, 255, 255), sf::Uint32 style = sf::Style::Default);
virtual void start() {};
virtual void update(double elapsedTime) {};
void exit();
void debugText(bool value) { b_debugText = value; }
void setUpdateWorld(bool value) { b_updateWorld = value; }
virtual void gui(){}
};
#endif //INC_3DZAVR_TDZAVR_H

308
engine/Mesh.cpp Executable file
View File

@ -0,0 +1,308 @@
//
// Created by Иван Ильин on 13.01.2021.
//
#include <string>
#include <fstream>
#include <sstream>
#include <utility>
#include "Mesh.h"
#include "utils/Log.h"
using namespace std;
Mesh Mesh::operator*(const Matrix4x4 &matrix4X4) const {
return Mesh(*this) *= matrix4X4;
}
Mesh &Mesh::operator*=(const Matrix4x4 &matrix4X4) {
for (auto& t : tris)
t *= matrix4X4;
return *this;
}
Mesh &Mesh::loadObj(const std::string& filename, const std::string &materials, const Point4D& scale) {
auto objects = Mesh::LoadObjects(filename, materials, scale);
for(auto& obj : objects) {
for (auto &tri : obj->triangles()) {
tris.push_back(tri);
}
}
return *this;
}
Mesh::Mesh(const std::string& filename, const std::string &materials, const Point4D& scale){
loadObj(filename, materials, scale);
}
Mesh::Mesh(const vector<Triangle> &tries){
tris = tries;
}
Mesh::Mesh(const Mesh& mesh) : Animatable(mesh) {
*this = mesh;
}
Mesh Mesh::Obj(const std::string& filename) {
return Mesh(filename);
}
Mesh Mesh::Cube(double size) {
Mesh cube{};
cube.tris = {
{ Point4D{0.0, 0.0, 0.0, 1.0}, Point4D{0.0, 1.0, 0.0, 1.0}, Point4D{1.0, 1.0, 0.0, 1.0} },
{ Point4D{0.0, 0.0, 0.0, 1.0}, Point4D{1.0, 1.0, 0.0, 1.0}, Point4D{1.0, 0.0, 0.0, 1.0} },
{ Point4D{1.0, 0.0, 0.0, 1.0}, Point4D{1.0, 1.0, 0.0, 1.0}, Point4D{1.0, 1.0, 1.0, 1.0} },
{ Point4D{1.0, 0.0, 0.0, 1.0}, Point4D{1.0, 1.0, 1.0, 1.0}, Point4D{1.0, 0.0, 1.0, 1.0} },
{ Point4D{1.0, 0.0, 1.0, 1.0}, Point4D{1.0, 1.0, 1.0, 1.0}, Point4D{0.0, 1.0, 1.0, 1.0} },
{ Point4D{1.0, 0.0, 1.0, 1.0}, Point4D{0.0, 1.0, 1.0, 1.0}, Point4D{0.0, 0.0, 1.0, 1.0} },
{ Point4D{0.0, 0.0, 1.0, 1.0}, Point4D{0.0, 1.0, 1.0, 1.0}, Point4D{0.0, 1.0, 0.0, 1.0} },
{ Point4D{0.0, 0.0, 1.0, 1.0}, Point4D{0.0, 1.0, 0.0, 1.0}, Point4D{0.0, 0.0, 0.0, 1.0} },
{ Point4D{0.0, 1.0, 0.0, 1.0}, Point4D{0.0, 1.0, 1.0, 1.0}, Point4D{1.0, 1.0, 1.0, 1.0} },
{ Point4D{0.0, 1.0, 0.0, 1.0}, Point4D{1.0, 1.0, 1.0, 1.0}, Point4D{1.0, 1.0, 0.0, 1.0} },
{ Point4D{1.0, 0.0, 1.0, 1.0}, Point4D{0.0, 0.0, 1.0, 1.0}, Point4D{0.0, 0.0, 0.0, 1.0} },
{ Point4D{1.0, 0.0, 1.0, 1.0}, Point4D{0.0, 0.0, 0.0, 1.0}, Point4D{1.0, 0.0, 0.0, 1.0} },
};
return cube *= Matrix4x4::Scale(size, size, size);
}
void Mesh::translate(double dx, double dy, double dz) {
p_position += Point4D(dx, dy, dz);
if(v_attached.empty())
return;
for(auto attached : v_attached)
attached->translate(Point4D{dx, dy, dz});
}
void Mesh::rotate(double rx, double ry, double rz) {
p_angle += Point4D{rx, ry, rz};
*this *= Matrix4x4::Rotation(rx, ry, rz);
}
void Mesh::rotate(const Point4D &r) {
p_angle += r;
*this *= Matrix4x4::Rotation(r);
if(v_attached.empty())
return;
for(auto attached : v_attached)
attached->rotateRelativePoint(position(), r);
}
void Mesh::rotate(const Point4D &v, double r) {
*this *= Matrix4x4::Rotation(v, r);
if(v_attached.empty())
return;
for(auto attached : v_attached)
attached->rotateRelativePoint(position(), v, r);
}
void Mesh::scale(double sx, double sy, double sz) {
*this *= Matrix4x4::Scale(sx, sy, sz);
}
void Mesh::scale(const Point4D &s) {
*this *= Matrix4x4::Scale(s.x(), s.y(), s.z());
}
void Mesh::translate(const Point4D &t) {
translate(t.x(), t.y(), t.z());
}
Mesh &Mesh::operator=(const Mesh &mesh) {
tris = mesh.tris;
p_position = mesh.p_position;
c_color = mesh.c_color;
return *this;
}
void Mesh::rotateRelativePoint(const Point4D &s, double rx, double ry, double rz) {
p_angle += Point4D{rx, ry, rz};
// Translate XYZ by vector r1
Point4D r1 = p_position - s;
*this *= Matrix4x4::Translation(r1);
// In translated coordinate system we rotate mesh and position
Matrix4x4 rotationMatrix = Matrix4x4::Rotation(rx, ry, rz);
Point4D r2 = rotationMatrix*r1;
*this *= rotationMatrix;
// After rotation we translate XYZ by vector -r2 and recalculate position
*this *= Matrix4x4::Translation(-r2);
p_position = s + r2;
if(v_attached.empty())
return;
for(auto attached : v_attached)
attached->rotateRelativePoint(s, Point4D{rx, ry, rz});
}
void Mesh::rotateRelativePoint(const Point4D &s, const Point4D &r) {
p_angle += r;
rotateRelativePoint(s, r.x(), r.y(), r.z());
}
void Mesh::rotateRelativePoint(const Point4D &s, const Point4D &v, double r) {
// Translate XYZ by vector r1
Point4D r1 = p_position - s;
*this *= Matrix4x4::Translation(r1);
// In translated coordinate system we rotate mesh and position
Matrix4x4 rotationMatrix = Matrix4x4::Rotation(v, r);
Point4D r2 = rotationMatrix*r1;
*this *= rotationMatrix;
// After rotation we translate XYZ by vector -r2 and recalculate position
*this *= Matrix4x4::Translation(-r2);
p_position = s + r2;
if(v_attached.empty())
return;
for(auto attached : v_attached)
attached->rotateRelativePoint(s, v, r);
}
void Mesh::translateToPoint(const Point4D &point) {
translate(point - p_position);
}
void Mesh::setColor(sf::Color c) {
c_color = c;
for (auto& t : tris)
t.color = c_color;
}
std::vector<std::shared_ptr<Mesh>>
Mesh::LoadObjects(const string &filename, const string &materials, const Point4D &scale) {
std::vector<std::shared_ptr<Mesh>> objects;
map<string, sf::Color> maters;
ifstream file(filename);
if (!file.is_open())
{
Log::log("Mesh::LoadObjects(): cannot load file from " + filename);
return objects;
}
if(!materials.empty()) {
ifstream mat(materials);
if (!mat.is_open())
{
Log::log("Mesh::LoadObjects(): cannot load mat from " + materials);
return objects;
} else {
while (!mat.eof())
{
char line[128];
mat.getline(line, 128);
stringstream s;
s << line;
int color[4];
string matName;
s >> matName >> color[0] >> color[1] >> color[2] >> color[3];
maters.insert({matName, sf::Color(color[0],color[1],color[2], color[3])});
}
mat.close();
}
}
vector<Point4D> verts;
std::vector<Triangle> tris;
sf::Color currentColor = sf::Color(255, 245, 194, 255);
while (!file.eof())
{
char line[128];
file.getline(line, 128);
stringstream s;
s << line;
char junk;
if(line[0] == 'o') {
if(!tris.empty()) {
objects.push_back(make_shared<Mesh>(tris));
objects.back()->scale(scale);
}
tris.clear();
}
if (line[0] == 'v')
{
double x, y, z;
s >> junk >> x >> y >> z;
verts.emplace_back(x, y, z, 1);
}
if(line[0] == 'g') {
string matInfo;
s >> junk >> matInfo;
string colorName = matInfo.substr(matInfo.size()-3, 3);
currentColor = maters[matInfo.substr(matInfo.size()-3, 3)];
}
if (line[0] == 'f')
{
int f[3];
s >> junk >> f[0] >> f[1] >> f[2];
tris.emplace_back(verts[f[0] - 1], verts[f[1] - 1], verts[f[2] - 1] );
tris.back().color = currentColor;
}
}
if(!tris.empty()) {
objects.push_back(make_shared<Mesh>(tris));
objects.back()->scale(scale);
}
file.close();
return objects;
}
Mesh Mesh::LineTo(const Point4D& from, const Point4D& to, double line_width, sf::Color color) {
Mesh line;
Point4D v1 = (to - from).normalized();
Point4D v2 = from.cross3D(from + Point4D{1, 0, 0}).normalized();
Point4D v3 = v1.cross3D(v2).normalized();
// from plane
Point4D p1 = from - v2 * line_width/2.0 - v3 * line_width/2.0;
Point4D p2 = from - v2 * line_width/2.0 + v3 * line_width/2.0;
Point4D p3 = from + v2 * line_width/2.0 + v3 * line_width/2.0;
Point4D p4 = from + v2 * line_width/2.0 - v3 * line_width/2.0;
// to plane
Point4D p5 = to - v2 * line_width/2.0 - v3 * line_width/2.0;
Point4D p6 = to - v2 * line_width/2.0 + v3 * line_width/2.0;
Point4D p7 = to + v2 * line_width/2.0 + v3 * line_width/2.0;
Point4D p8 = to + v2 * line_width/2.0 - v3 * line_width/2.0;
line.tris = {
{ p2, p4, p1 },
{ p2, p3, p4 },
{ p1, p6, p2 },
{ p1, p5, p6 },
{ p2, p6, p7 },
{ p2, p7, p3 },
{ p6, p5, p8 },
{ p6, p8, p7 },
{ p4, p3, p7 },
{ p4, p7, p8 },
{ p1, p8, p5 },
{ p1, p4, p8 }
};
line.setColor(color);
return line;
}

84
engine/Mesh.h Executable file
View File

@ -0,0 +1,84 @@
//
// Created by Иван Ильин on 13.01.2021.
//
#ifndef ENGINE_MESH_H
#define ENGINE_MESH_H
#include <vector>
#include "Triangle.h"
#include "animation/Animatable.h"
#include "physics/RigidBody.h"
#include <SFML/Graphics.hpp>
#include "Object.h"
class Mesh : public Object, public Animatable, public RigidBody {
protected:
std::vector<Triangle> tris;
bool _visible = true;
sf::Color c_color = sf::Color(255, 245, 194);
// Operations with Matrix4x4
[[nodiscard]] Mesh operator*(const Matrix4x4& matrix4X4) const;
Mesh& operator*=(const Matrix4x4& matrix4X4);
std::function<void(const std::string&, const std::shared_ptr<Mesh>&)> _collisionCallBack;
public:
Mesh() = default;
Mesh(const Mesh& mesh);
explicit Mesh(const std::vector<Triangle>& tries);
Mesh& operator=(const Mesh& mesh);
explicit Mesh(const std::string& filename, const std::string &materials = "", const Point4D& scale = Point4D{1, 1, 1});
Mesh& loadObj(const std::string& filename, const std::string &materials = "", const Point4D& scale = Point4D{1, 1, 1});
[[nodiscard]] std::vector<Triangle>const &triangles() const { return tris; }
[[nodiscard]] std::vector<Triangle>& triangles() override { return tris; }
void setTriangles(const std::vector<Triangle>& t) override { tris = t; }
// Translate mesh
void translate(double dx, double dy, double dz);
void translate(const Point4D& t) override;
void translateToPoint(const Point4D& point);
// Rotate mesh around XYZ axes
void rotate(double rx, double ry, double rz);
void rotate(const Point4D& r) override;
// Rotate mesh around normalised vector 'v' by 'r' radians
void rotate(const Point4D& v, double r) override;
// Rotate mesh around XYZ by (rx, ry, rz) radians relative val 'point4D'
void rotateRelativePoint(const Point4D& point4D, double rx, double ry, double rz);
// Rotate mesh around XYZ by (r.x, r.y, r.z) radians relative val 'point4D'
void rotateRelativePoint(const Point4D& point4D, const Point4D& r) override;
// Rotate mesh around normalised vector 'v' by 'r' radians relative val 'point4D'
void rotateRelativePoint(const Point4D& point4D, const Point4D& v, double r) override;
void scale(double sx, double sy, double sz);
void scale(const Point4D& s);
void rotateToAngle(const Point4D& v) { rotate(v - p_angle); }
[[nodiscard]] Point4D position() const override { return p_position; }
[[nodiscard]] Point4D angle() const override { return p_angle; }
[[nodiscard]] sf::Color color() const override { return c_color; }
void setColor(sf::Color c) override;
Mesh static Cube(double size = 1.0);
Mesh static Obj(const std::string& filename);
Mesh static LineTo(const Point4D& from, const Point4D& to, double line_width = 0.1, sf::Color color = {150, 150, 150, 255});
void setVisible(bool visibility) { _visible = visibility; }
[[nodiscard]] bool isVisible() const { return _visible; }
std::vector<std::shared_ptr<Mesh>> static LoadObjects(const std::string& filename, const std::string &materials = "", const Point4D& scale = Point4D{1, 1, 1});
[[nodiscard]] const std::function<void(const std::string&, const std::shared_ptr<Mesh>&)>& collisionCallBack() const { return _collisionCallBack; }
void setCollisionCallBack(const std::function<void(const std::string&, const std::shared_ptr<Mesh>&)>& f) { _collisionCallBack = f; }
};
#endif //INC_3DZAVR_MESH_H

36
engine/Object.h Executable file
View File

@ -0,0 +1,36 @@
//
// Created by Иван Ильин on 15.03.2021.
//
#ifndef ENGINE_OBJECT_H
#define ENGINE_OBJECT_H
#include <vector>
#include "utils/Point4D.h"
#include <memory>
class Object {
protected:
std::vector<std::shared_ptr<Object>> v_attached;
Point4D p_position;
Point4D p_angle;
public:
Object() = default;
virtual void translate(const Point4D& dv) {}
virtual void rotate(const Point4D& r) {}
virtual void rotateRelativePoint(const Point4D& point4D, const Point4D& r) {}
virtual void rotate(const Point4D& v, double rv) {}
virtual void rotateRelativePoint(const Point4D& s, const Point4D& v, double r) {}
[[nodiscard]] Point4D position() const { return p_position; }
[[nodiscard]] Point4D angle() const { return p_angle; }
void attach(const std::shared_ptr<Object>& object) {
v_attached.push_back(object);
}
};
#endif //MINECRAFT_3DZAVR_OBJECT_H

111
engine/Plane.cpp Executable file
View File

@ -0,0 +1,111 @@
//
// Created by Иван Ильин on 19.01.2021.
//
#include "Plane.h"
Plane::Plane(const Triangle& tri) {
triangle = tri;
n = tri.norm();
p = tri[0];
}
Plane::Plane(const Point4D &N, const Point4D &P) {
n = N;
p = P;
}
Plane::Plane(const Plane &plane) {
triangle = plane.triangle;
n = plane.n;
p = plane.p;
}
double Plane::distance(const Point4D &point4D) const {
return point4D.dot(n) - p.dot(n);
}
std::pair<Point4D, double> Plane::intersection(const Point4D &start, const Point4D &end) {
double s_dot_n = start.dot(n);
double k = (s_dot_n - p.dot(n))/(s_dot_n - end.dot(n));
Point4D res = start + (end - start)*k;
return std::make_pair(res, k);
}
int Plane::clip(Triangle &tri, Triangle &additional_tri) {
n.normalize();
Point4D insidePoints[3]; int inside = 0;
Point4D outsidePoints[3]; int outside = 0;
Point4D insideTextures[3]; int insideT = 0;
Point4D outsideTextures[3]; int outsideT = 0;
double distances[3] = {distance(tri[0]), distance(tri[1]), distance(tri[2])};
for(int i = 0; i < 3; i++) {
if (distances[i] >= 0) {
insidePoints[inside++] = tri[i];
insideTextures[insideT++] = tri.t[i];
} else {
outsidePoints[outside++] = tri[i];
outsideTextures[outsideT++] = tri.t[i];
}
}
if(inside == 0) {
tri.clip = Triangle::Skipped;
return 0;
}
if(inside == 1) {
std::pair<Point4D, double> intersect1 = intersection(insidePoints[0], outsidePoints[0]);
std::pair<Point4D, double> intersect2 = intersection(insidePoints[0], outsidePoints[1]);
tri[0] = insidePoints[0];
tri.t[0] = insideTextures[0];
tri[1] = intersect1.first;
tri.t[1] = insideTextures[0] + (outsideTextures[0] - insideTextures[0]) * intersect1.second;
tri[2] = intersect2.first;
tri.t[2] = insideTextures[0] + (outsideTextures[1] - insideTextures[0]) * intersect2.second;
tri.clip = Triangle::Cropped;
return 1;
}
if(inside == 2) {
std::pair<Point4D, double> intersect1 = intersection(insidePoints[0], outsidePoints[0]);
std::pair<Point4D, double> intersect2 = intersection(insidePoints[1], outsidePoints[0]);
tri[0] = insidePoints[0];
tri.t[0] = insideTextures[0];
tri[1] = intersect1.first;
tri.t[1] = insideTextures[0] + (outsideTextures[0] - insideTextures[0])*intersect1.second;
tri[2] = insidePoints[1];
tri.t[2] = insideTextures[1];
additional_tri[0] = intersect1.first;
additional_tri.t[0] = insideTextures[0] + (outsideTextures[0] - insideTextures[0])*intersect1.second;
additional_tri[1] = intersect2.first;
additional_tri.t[1] = insideTextures[1] + (outsideTextures[0] - insideTextures[1])*intersect2.second;
additional_tri[2] = insidePoints[1];
additional_tri.t[2] = insideTextures[1];
tri.clip = Triangle::Doubled;
additional_tri.clip = Triangle::Doubled;
return 2;
}
if(inside == 3) {
return 1;
}
return 0;
}

35
engine/Plane.h Executable file
View File

@ -0,0 +1,35 @@
//
// Created by Иван Ильин on 19.01.2021.
//
#ifndef ENGINE_PLANE_H
#define ENGINE_PLANE_H
#include "utils/Point4D.h"
#include "Triangle.h"
class Plane {
private:
// You can define plane by defining the points in 3D space
Triangle triangle;
// Or by defining normal vector and one val laying on the plane
Point4D n = Point4D{0, 0, 1, 0};
Point4D p{};
public:
// A plain with normal vector 'n' and val 'p' lays on the plane
Plane() = default;
Plane(const Point4D& N, const Point4D& P);
Plane(const Plane& plane);
explicit Plane(const Triangle& tri);
[[nodiscard]] double distance(const Point4D& point4D) const;
// Point4D in space where line ('start' to 'end') intersects plain with normal vector 'n' and val 'p' lays on the plane
std::pair<Point4D, double> intersection(const Point4D& start, const Point4D& end);
int clip(Triangle& tri, Triangle& additional_tri);
[[nodiscard]] Point4D N() const { return n; }
[[nodiscard]] Point4D P() const { return p; }
};
#endif //INC_3DZAVR_PLANE_H

123
engine/ResourceManager.cpp Executable file
View File

@ -0,0 +1,123 @@
//
// Created by Neirokan on 09.05.2020
//
#include "ResourceManager.h"
#include <map>
#include <memory>
#include <iostream>
namespace ResourceManager
{
namespace
{
std::map<std::string, std::shared_ptr<sf::Texture>> _textures;
std::map<std::string, std::shared_ptr<sf::Font>> _fonts;
std::map<std::string, std::shared_ptr<sf::SoundBuffer>> _soundBuffers;
std::map<std::string, std::shared_ptr<sf::Shader>> _shaders;
}
void unloadTextures()
{
for (auto & _texture : _textures)
_texture.second.reset();
_textures.clear();
}
void unloadSoundBuffers()
{
for (auto & _soundBuffer : _soundBuffers)
_soundBuffer.second.reset();
_soundBuffers.clear();
}
void unloadFonts() {
for (auto & _font : _fonts)
_font.second.reset();
_fonts.clear();
}
void unloadShaders() {
for (auto& shader : _shaders)
shader.second.reset();
_shaders.clear();
}
void unloadAllResources()
{
unloadTextures();
unloadSoundBuffers();
unloadFonts();
unloadShaders();
}
std::shared_ptr<sf::Texture> loadTexture(const std::string& filename)
{
// If texture is already loaded - return pointer to it
auto it = _textures.find(filename);
if (it != _textures.end())
return it->second;
// Otherwise - try to load it. If failure - return zero
std::shared_ptr<sf::Texture> texture(new sf::Texture);
if (!texture->loadFromFile(filename))
return nullptr;
// If success - remember and return texture pointer
texture->setRepeated(true);
_textures.emplace(filename, texture);
return texture;
}
std::shared_ptr<sf::SoundBuffer> loadSoundBuffer(const std::string& filename)
{
// If sound buffer is already loaded - return pointer to it
auto it = _soundBuffers.find(filename);
if (it != _soundBuffers.end())
return it->second;
// Otherwise - try to load it. If failure - return zero
std::shared_ptr<sf::SoundBuffer> soundBuffer(new sf::SoundBuffer);
if (!soundBuffer->loadFromFile(filename))
return nullptr;
// If success - remember and return texture pointer
_soundBuffers.emplace(filename, soundBuffer);
return soundBuffer;
}
std::shared_ptr<sf::Font> loadFont(const std::string& filename) {
// If font is already loaded - return pointer to it
auto it = _fonts.find(filename);
if (it != _fonts.end())
return it->second;
// Otherwise - try to load it. If failure - return zero
std::shared_ptr<sf::Font> font(new sf::Font);
if (!font->loadFromFile(filename))
return nullptr;
// If success - remember and return texture pointer
_fonts.emplace(filename, font);
return font;
}
std::shared_ptr<sf::Shader> loadShader(const std::string& filename, sf::Shader::Type type) {
// If Shader is already loaded - return pointer to it
auto it = _shaders.find(filename);
if (it != _shaders.end())
return it->second;
// Otherwise - try to load it. If failure - return zero
std::shared_ptr<sf::Shader> shader(new sf::Shader);
if (!shader->loadFromFile(filename, type))
return nullptr;
// If success - remember and return texture pointer
_shaders.emplace(filename, shader);
return shader;
}
}

32
engine/ResourceManager.h Executable file
View File

@ -0,0 +1,32 @@
//
// Created by Neirokan on 09.05.2020
//
#ifndef ENGINE_RESOURCEMANAGER_H
#define ENGINE_RESOURCEMANAGER_H
#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>
#include <memory>
namespace ResourceManager
{
// Unloads all currently loaded textures.
void unloadTextures();
void unloadSoundBuffers();
void unloadFonts();
void unloadShaders();
void unloadAllResources();
// Try to load texture from file.
// If success returns pointer to texture.
// Otherwise returns nullptr.
std::shared_ptr<sf::Texture> loadTexture(const std::string& filename);
std::shared_ptr<sf::Font> loadFont(const std::string& filename);
std::shared_ptr<sf::SoundBuffer> loadSoundBuffer(const std::string& filename);
std::shared_ptr<sf::Shader> loadShader(const std::string& filename, sf::Shader::Type type);
};
#endif //PSEUDO3DENGINE_RESOURCEMANAGER_H

205
engine/Screen.cpp Executable file
View File

@ -0,0 +1,205 @@
//
// Created by Иван Ильин on 14.01.2021.
//
#include "Screen.h"
#include "utils/Time.h"
#include <utility>
#include "utils/Log.h"
#include "ResourceManager.h"
#include <cstdio>
void Screen::open(int screenWidth, int screenHeight, const std::string &name, bool verticalSync, sf::Color background, sf::Uint32 style) {
this->name = name;
w = screenWidth;
h = screenHeight;
this->background = background;
sf::ContextSettings settings;
settings.antialiasingLevel = 8;
window.create(sf::VideoMode(w, h), name, style, settings);
window.setVerticalSyncEnabled(verticalSync);
}
void Screen::display() {
sf::Event event{};
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
window.close();
}
}
std::string title = name + " (" + std::to_string(Time::fps()) + " fps)";
window.setTitle(title);
if(renderVideo || makeScreenShoot)
{
sf::Texture copyTexture;
copyTexture.create(window.getSize().x, window.getSize().y);
copyTexture.update(window);
if(makeScreenShoot)
copyTexture.copyToImage().saveToFile("../img/screen.png");
else
copyTexture.copyToImage().saveToFile("../film/png/" + std::to_string(frame++) + ".png");
makeScreenShoot = false;
}
window.display();
}
void Screen::clear() {
window.clear(background);
}
void Screen::line(const Point4D& p1, const Point4D& p2, sf::Color color)
{
if (!window.isOpen())
return;
sf::Vertex line[] =
{
sf::Vertex(sf::Vector2f(p1.x(), p1.y()), color),
sf::Vertex(sf::Vector2f(p2.x(), p2.y()), color)
};
window.draw(line, 2, sf::Lines);
}
void Screen::triangle(const Triangle& triangle)
{
if(vm == Frame || vm == Borders || vm == Xray || vm == Clipped || vm == Transparency || vm == Normals) {
sf::Vertex lines[4] =
{
sf::Vertex(sf::Vector2f(triangle[0].x(), triangle[0].y()), sf::Color(0, 0, 0, 255)),
sf::Vertex(sf::Vector2f(triangle[1].x(), triangle[1].y()), sf::Color(0, 0, 0, 255)),
sf::Vertex(sf::Vector2f(triangle[2].x(), triangle[2].y()), sf::Color(0, 0, 0, 255)),
sf::Vertex(sf::Vector2f(triangle[0].x(), triangle[0].y()), sf::Color(0, 0, 0, 255))
};
window.draw(lines, 4, sf::LineStrip);
}
if(vm == Frame || vm == Xray)
return; // no texture when we turn on Frame or Xray mode
sf::Vertex tris[3] =
{
sf::Vertex(sf::Vector2f(triangle[0].x(), triangle[0].y()), triangle.color),
sf::Vertex(sf::Vector2f(triangle[1].x(), triangle[1].y()), triangle.color),
sf::Vertex(sf::Vector2f(triangle[2].x(), triangle[2].y()), triangle.color)
};
window.draw(tris, 3, sf::Triangles);
}
void Screen::title(const std::string& title)
{
name = title;
}
bool Screen::isOpen() {
return window.isOpen();
}
void Screen::close() {
window.close();
}
bool Screen::isKeyPressed(sf::Keyboard::Key key) {
return sf::Keyboard::isKeyPressed(key);
}
Point4D Screen::getMousePosition() const {
sf::Vector2<int> pos = sf::Mouse::getPosition(window);
return Point4D(pos.x, pos.y, 0, 0);
}
Point4D Screen::getMouseDisplacement() const {
sf::Vector2<int> disp = sf::Mouse::getPosition(window) - sf::Vector2<int>(w/2, h/2);
setMouseInCenter();
return Point4D(disp.x, disp.y, 0, 0);
}
void Screen::setMouseInCenter() const {
sf::Mouse::setPosition({ w / 2, h / 2 }, window);
}
void Screen::setMouseCursorVisible(bool visible) {
window.setMouseCursorVisible(visible);
}
void Screen::keyboardControl() {
// Check all input after this condition please
if (!window.hasFocus())
return;
if(isKeyTapped(sf::Keyboard::Num1))
setMode(ViewMode::Default);
if(isKeyTapped(sf::Keyboard::Num2))
setMode(ViewMode::Borders);
if(isKeyTapped(sf::Keyboard::Num3))
setMode(ViewMode::Transparency);
if(isKeyTapped(sf::Keyboard::Num4))
setMode(ViewMode::Frame);
if(isKeyTapped(sf::Keyboard::Num5))
setMode(ViewMode::Xray);
if(isKeyTapped(sf::Keyboard::Num6))
setMode(ViewMode::Clipped);
if(isKeyTapped(sf::Keyboard::Num7))
setMode(ViewMode::Normals);
}
bool Screen::isKeyTapped(sf::Keyboard::Key key) {
if (!Screen::isKeyPressed(key))
return false;
if(tappedKeys.count(key) == 0) {
tappedKeys.emplace(key, Time::time());
return true;
} else if((Time::time() - tappedKeys[key]) > 0.2) {
tappedKeys[key] = Time::time();
return true;
}
return false;
}
bool Screen::isButtonPressed(sf::Mouse::Button button) {
return sf::Mouse::isButtonPressed(button);
}
bool Screen::isButtonTapped(sf::Mouse::Button button) {
if (!Screen::isButtonPressed(button))
return false;
if(tappedButtons.count(button) == 0) {
tappedButtons.emplace(button, Time::time());
return true;
} else if((Time::time() - tappedButtons[button]) > 0.2) {
tappedButtons[button] = Time::time();
return true;
}
return false;
}
void Screen::debugText(const std::string& text) {
sf::Text t;
t.setFont(*ResourceManager::loadFont(font));
t.setString(text);
t.setCharacterSize(30);
t.setFillColor(sf::Color::Black);
t.setPosition(10, 10);
window.draw(t);
}
void Screen::setRender(bool r) {
if(renderVideo && !r) {
std::string c = "ffmpeg -stats -r 60 -i ../film/png/%d.png -vcodec libx264 -crf 1 -pix_fmt yuv420p -frames " + std::to_string(frame) + " ../film/mp4/" + std::to_string(scene) + "_" + name + ".mp4";
popen(c.c_str(), "w");
frame = 0;
scene++;
}
renderVideo = r;
}

92
engine/Screen.h Executable file
View File

@ -0,0 +1,92 @@
//
// Created by Иван Ильин on 14.01.2021.
//
#ifndef ENGINE_SCREEN_H
#define ENGINE_SCREEN_H
#include <string>
#include "Triangle.h"
#include <SFML/Graphics.hpp>
#include <map>
#include "utils/Time.h"
class Screen {
public:
enum ViewMode {
Default = 0,
Frame,
Borders,
Xray,
Clipped,
Transparency,
Normals
};
private:
int w = 1920;
int h = 1080;
std::string name;
sf::Color background;
Screen::ViewMode vm = Screen::ViewMode::Default;
std::map<sf::Keyboard::Key, double> tappedKeys;
std::map<sf::Mouse::Button, double> tappedButtons;
std::string font = "../engine/fonts/Roboto-Thin.ttf";
bool renderVideo = false; // performance heavy. I use this to make sequence of .jpg files of screen and then convert this to .mp4 file
int frame = 0;
int scene = 0; // the number of scene
bool makeScreenShoot = false;
public:
sf::RenderWindow window;
void open(int screenWidth = 1920, int screenHeight = 1080, const std::string& name = "engine", bool verticalSync = true, sf::Color background = sf::Color(255, 255, 255), sf::Uint32 style = sf::Style::Default);
void display();
void clear();
void line(const Point4D& p1, const Point4D& p2, sf::Color color = {0, 0, 0});
void triangle(const Triangle& triangle );
void title(const std::string& title);
std::string title() const { return name; };
bool isOpen();
int width() const {return window.getSize().x;}
int height() const {return window.getSize().y;}
void close();
static bool isKeyPressed(sf::Keyboard::Key key); // returns true if this key is pressed
bool isKeyTapped(sf::Keyboard::Key key); // returns true if this key is tapped and 1/5 sec passed (button bouncing problem solved)
static bool isButtonPressed(sf::Mouse::Button button); // returns true if this button is pressed
bool isButtonTapped(sf::Mouse::Button button); // returns true if this button is tapped and 1/5 sec passed (button bouncing problem solved)
Point4D getMousePosition() const;
Point4D getMouseDisplacement() const;
void setMouseInCenter() const;
void setMouseCursorVisible(bool visible);
void setMode(ViewMode mode) { vm = mode; }
[[nodiscard]] ViewMode mode() const { return vm; }
void keyboardControl();
void debugText(const std::string& text);
void setRender(bool r);
bool isRender() const { return renderVideo; }
void makeScreen() { makeScreenShoot = true; }
};
#endif //INC_3DZAVR_SCREEN_H

80
engine/Triangle.cpp Executable file
View File

@ -0,0 +1,80 @@
//
// Created by Иван Ильин on 13.01.2021.
//
#include "Triangle.h"
Triangle::Triangle () {
p[0] = Point4D{0,0,0,1};
p[1] = Point4D{0,0,0,1};
p[2] = Point4D{0,0,0,1};
}
Triangle::Triangle(const Point4D& p1, const Point4D& p2, const Point4D& p3, double w) {
p[0] = Point4D{p1.x(), p1.y(), p1.z(), w};
p[1] = Point4D{p2.x(), p2.y(), p2.z(), w};
p[2] = Point4D{p3.x(), p3.y(), p3.z(), w};
}
Triangle Triangle::operator*(const Matrix4x4 &matrix4X4) const {
return Triangle(*this) *= matrix4X4;
}
Triangle &Triangle::operator*=(const Matrix4x4 &matrix4X4) {
p[0] = matrix4X4 * p[0];
p[1] = matrix4X4 * p[1];
p[2] = matrix4X4 * p[2];
return *this;
}
Point4D Triangle::norm() const {
Point4D v1 = p[1] - p[0];
Point4D v2 = p[2] - p[0];
return v1.cross3D(v2).normalize();
}
Point4D Triangle::operator[](int i) const {
return p[i];
}
Point4D &Triangle::operator[](int i) {
return p[i];
}
Point4D Triangle::pos() const {
return (p[0] + p[1] + p[2])/3.0;
}
Triangle::Triangle(const Triangle &triangle) {
clip = triangle.clip;
color = triangle.color;
p[0] = triangle[0];
p[1] = triangle[1];
p[2] = triangle[2];
}
bool Triangle::isPointInside(const Point4D &point) const {
Point4D triangleNorm = norm();
double dot1 = (point - p[0]).cross3D(p[1] - p[0]).dot(triangleNorm);
double dot2 = (point - p[1]).cross3D(p[2] - p[1]).dot(triangleNorm);
double dot3 = (point - p[2]).cross3D(p[0] - p[2]).dot(triangleNorm);
if((dot1 >= 0 && dot2 >= 0 && dot3 >= 0) || (dot1 <= 0 && dot2 <= 0 && dot3 <= 0))
return true;
return false;
}
Triangle &Triangle::operator=(const Triangle &triangle) {
if(&triangle != this) {
clip = triangle.clip;
color = triangle.color;
p[0] = triangle[0];
p[1] = triangle[1];
p[2] = triangle[2];
}
return *this;
}

45
engine/Triangle.h Executable file
View File

@ -0,0 +1,45 @@
//
// Created by Иван Ильин on 13.01.2021.
//
#ifndef ENGINE_TRIANGLE_H
#define ENGINE_TRIANGLE_H
#include "utils/Point4D.h"
#include "utils/Matrix4x4.h"
#include <SFML/Graphics.hpp>
class Triangle {
public:
// This is for clipping debug: you can distinguish how this triangle was clipped
enum ClipMode {
None = 0,
Cropped,
Doubled,
Skipped
};
ClipMode clip = None;
sf::Color color;
Point4D p[3]; // points in space
Point4D t[3]; // texture coordinates
Triangle ();
Triangle (const Triangle& triangle);
Triangle (const Point4D& p1, const Point4D& p2, const Point4D& p3, double w = 1);
Triangle& operator=(const Triangle& triangle);
[[nodiscard]] Point4D operator[] (int i) const;
[[nodiscard]] Point4D& operator[] (int i);
[[nodiscard]] Point4D norm() const;
// Operations with Matrix4x4
[[nodiscard]] Triangle operator*(const Matrix4x4& matrix4X4) const;
Triangle& operator*=(const Matrix4x4& matrix4X4);
[[nodiscard]] Point4D pos() const;
[[nodiscard]] bool isPointInside(const Point4D& point) const;
};
#endif //INC_3DZAVR_TRIANGLE_H

81
engine/World.cpp Executable file
View File

@ -0,0 +1,81 @@
//
// Created by Иван Ильин on 13.01.2021.
//
#include <fstream>
#include <sstream>
#include "World.h"
#include "utils/Log.h"
#include "Plane.h"
using namespace std;
void World::addMesh(const std::shared_ptr<Mesh>& mesh, const string &name) {
_objects.emplace(name, mesh);
Log::log("World::addMesh(): inserted mesh '" + name + "' with " + std::to_string(_objects[name]->triangles().size()) + " tris.");
}
void World::loadObj(const string &name, const string &filename,const std::string &materials, const Point4D& scale) {
_objects.emplace(name, std::make_shared<Mesh>(filename, materials, scale));
Log::log("World::loadObj(): inserted mesh from " + filename + " with name '" + name + "' with " + std::to_string(_objects[name]->triangles().size()) + " tris.");
}
void World::removeMesh(const string &name) {
_objToRemove.push_back(name);
}
std::shared_ptr<Mesh> World::operator[](const string &name) {
if(_objects.count(name) == 0)
Log::log("World::operator[]: mesh '" + name + "' does not exist.");
return _objects.find(name)->second;
}
std::pair<Point4D, string> World::rayCast(const Point4D& from, const Point4D& to) {
std::pair<Point4D, string> result{Point4D{0, 0,0, -1}, ""};
double minDistance = 10000;
for(auto& object : _objects) {
if((object.first.find("im") != std::string::npos) || (object.first.find("point") != std::string::npos) || (object.first.find("nr") != std::string::npos))
continue;
for(auto& tri : object.second->triangles()) {
Triangle tri_translated(tri[0] + object.second->position(), tri[1] + object.second->position(), tri[2] + object.second->position(), 0);
Plane plane(tri_translated);
auto intersection = plane.intersection(from, to);
double distance = (intersection.first - from).sqrAbs();
if(intersection.second > 0 && distance < minDistance && tri_translated.isPointInside(intersection.first)) {
minDistance = distance;
result = {intersection.first, object.first};
}
}
}
return result;
}
void World::loadMap(const string &filename, const string &name, const Point4D &scale, const string &materials) {
auto objs = Mesh::LoadObjects(filename, materials, scale);
for(int i = 0; i < objs.size(); i++) {
string meshName = name + "_" + to_string(i);
addMesh(objs[i], meshName);
}
}
void World::garbageCollector() {
for(auto& obj : _objToRemove) {
if(_objects.erase(obj) > 0)
Log::log("World::garbageCollector(): removed mesh '" + obj + "'");
else
Log::log("World::garbageCollector(): cannot remove mesh '" + obj + "': mesh does not exist.");
}
_objToRemove.clear();
}
void World::removeMeshInstantly(const string &name) {
if(_objects.erase(name) > 0)
Log::log("World::removeMeshInstantly(): removed mesh '" + name + "'");
else
Log::log("World::removeMeshInstantly(): cannot remove mesh '" + name + "': mesh does not exist.");
}

38
engine/World.h Executable file
View File

@ -0,0 +1,38 @@
//
// Created by Иван Ильин on 13.01.2021.
//
#ifndef ENGINE_WORLD_H
#define ENGINE_WORLD_H
#include <map>
#include "Mesh.h"
class World {
private:
std::map<std::string, std::shared_ptr<Mesh>> _objects;
std::vector<std::string> _objToRemove;
public:
World() = default;
[[nodiscard]] std::shared_ptr<Mesh> operator[] (const std::string& name);
[[nodiscard]] std::map<std::string, std::shared_ptr<Mesh>>& objects() { return _objects; }
void addMesh(const std::shared_ptr<Mesh>& mesh, const std::string& name = "");
void removeMesh(const std::string& name);
void removeMeshInstantly(const std::string& name);
void garbageCollector();
void loadObj(const std::string &name, const std::string &filename,const std::string &materials = "", const Point4D& scale = Point4D{1, 1, 1});
// rayCast returns pair of Point4D and std::string:
// 1) Point4D is point of collision (the last coordinate is -1 if there are no collisions)
// 2) std::string - name of the object
std::pair<Point4D, std::string> rayCast(const Point4D& from, const Point4D& to);
void loadMap(const std::string& filename, const std::string& name = "", const Point4D& scale = Point4D{1, 1, 1}, const std::string &materials = "../maps/materials.txt");
};
#endif //INC_3DZAVR_WORLD_H

40
engine/animation/AColor.h Executable file
View File

@ -0,0 +1,40 @@
//
// Created by Иван Ильин on 02.06.2021.
//
#ifndef ENGINE_ACOLOR_H
#define ENGINE_ACOLOR_H
#include "Animatable.h"
#include "Animation.h"
class AColor : public Animation {
private:
sf::Color newColor;
sf::Color startColor;
public:
AColor(const sf::Color &color, double duration, LoopOut looped, InterpolationType interpolationType) {
_duration = duration;
_looped = looped;
_intType = interpolationType;
_waitFor = true;
newColor = color;
}
bool update(Animatable& obj) override {
if(!_started)
startColor = obj.color();
Point4D start(startColor.r, startColor.g, startColor.b, startColor.a);
Point4D end(newColor.r, newColor.g, newColor.b, newColor.a);
Point4D mid = start + (end - start)*_p;
obj.setColor(sf::Color(static_cast<sf::Uint8>(mid.x()), static_cast<sf::Uint8>(mid.y()), static_cast<sf::Uint8>(mid.z()), static_cast<sf::Uint8>(mid.w())));
return updateState();
}
};
#endif //SHOOTER_3DZAVR_ACOLOR_H

36
engine/animation/AFunction.h Executable file
View File

@ -0,0 +1,36 @@
//
// Created by Иван Ильин on 06.04.2021.
//
#include <utility>
#include "Animation.h"
#ifndef ENGINE_AFUNCTION_H
#define ENGINE_AFUNCTION_H
class AFunction : public Animation {
private:
int _calls = 0;
int _allCalls = 1;
std::function<void()> _callBack;
public:
AFunction(std::function<void()> function, int calls, double duration, LoopOut looped, InterpolationType interpolationType) {
_callBack = std::move(function);
_allCalls = calls;
_duration = duration;
_looped = looped;
_intType = interpolationType;
}
bool update(Animatable& obj) override {
if(_allCalls != 0 && _p >= (double)(_calls+1) / (_allCalls+1)) {
_calls++;
_callBack();
}
return updateState();
}
};
#endif //MINECRAFT_3DZAVR_AFUNCTION_H

29
engine/animation/ARotate.h Executable file
View File

@ -0,0 +1,29 @@
//
// Created by Иван Ильин on 29.01.2021.
//
#ifndef ENGINE_AROTATE_H
#define ENGINE_AROTATE_H
#include "Animatable.h"
#include "Animation.h"
class ARotate : public Animation {
private:
Point4D value;
public:
ARotate(const Point4D& r, double duration, LoopOut looped, InterpolationType interpolationType) {
_duration = duration;
_looped = looped;
_intType = interpolationType;
value = r;
}
bool update(Animatable& obj) override {
obj.rotate(value * _dp);
return updateState();
}
};
#endif //INC_3DZAVR_AROTATE_H

39
engine/animation/AScale.h Executable file
View File

@ -0,0 +1,39 @@
//
// Created by Иван Ильин on 29.01.2021.
//
#ifndef ENGINE_ASCALE_H
#define ENGINE_ASCALE_H
#include "Animatable.h"
#include "Animation.h"
class AScale : public Animation {
private:
Point4D value;
std::vector<Triangle> triangles;
public:
AScale(const Point4D &s, double duration, LoopOut looped, InterpolationType interpolationType) {
_duration = duration;
_looped = looped;
_intType = interpolationType;
_waitFor = true;
value = s;
}
bool update(Animatable& obj) override {
if(!_started)
triangles = obj.triangles();
std::vector<Triangle> newTriangles;
for(auto &t : triangles) {
newTriangles.emplace_back(t * Matrix4x4::Scale(Point4D{1, 1, 1} + (value - Point4D{1, 1, 1}) * _p));
}
obj.setTriangles(newTriangles);
return updateState();
}
};
#endif //INC_3DZAVR_ASCALE_H

33
engine/animation/ATranslate.h Executable file
View File

@ -0,0 +1,33 @@
//
// Created by Иван Ильин on 29.01.2021.
//
#ifndef ENGINE_ATRANSLATE_H
#define ENGINE_ATRANSLATE_H
#include "Animatable.h"
#include "Animation.h"
class ATranslate : public Animation {
private:
Point4D value;
public:
ATranslate(const Point4D& t, double duration, LoopOut looped, InterpolationType interpolationType) {
_duration = duration;
_looped = looped;
_intType = interpolationType;
value = t;
}
bool update(Animatable& obj) override {
obj.translate(value * _dp);
return updateState();
}
[[nodiscard]] int type() const override {
return 2;
}
};
#endif //INC_3DZAVR_ATRANSLATE_H

View File

@ -0,0 +1,34 @@
//
// Created by Иван Ильин on 29.01.2021.
//
#ifndef ENGINE_ATRANSLATETOPOINT_H
#define ENGINE_ATRANSLATETOPOINT_H
#include "Animatable.h"
#include "Animation.h"
class ATranslateToPoint : public Animation {
private:
Point4D point;
Point4D value;
public:
ATranslateToPoint(const Point4D& p, double duration, LoopOut looped, InterpolationType interpolationType) {
_duration = duration;
_looped = looped;
_intType = interpolationType;
point = p;
}
bool update(Animatable& obj) override {
if(!_started) {
value = point - obj.position();
}
obj.translate(value * _dp);
return updateState();
}
};
#endif //INC_3DZAVR_ATRANSLATETOPOINT_H

30
engine/animation/AWait.h Executable file
View File

@ -0,0 +1,30 @@
//
// Created by Иван Ильин on 29.01.2021.
//
#ifndef ENGINE_AWAIT_H
#define ENGINE_AWAIT_H
#include "Animatable.h"
#include "Animation.h"
class AWait : public Animation {
private:
Point4D value;
public:
explicit AWait(double duration) {
_duration = duration;
_intType = linear;
_waitFor = true;
}
bool update(Animatable& obj) override {
return updateState();
}
[[nodiscard]] int type() const override {
return 1;
}
};
#endif //INC_3DZAVR_AWAIT_H

91
engine/animation/Animatable.cpp Executable file
View File

@ -0,0 +1,91 @@
//
// Created by Иван Ильин on 26.01.2021.
//
#include "Animatable.h"
#include <iostream>
#include <utility>
#include "ATranslate.h"
#include "ATranslateToPoint.h"
#include "ARotate.h"
#include "AScale.h"
#include "AWait.h"
#include "AFunction.h"
#include "AColor.h"
void Animatable::a_translate(const std::string& listName,
const Point4D &t,
double duration,
Animation::LoopOut looped,
Animation::InterpolationType interpolationType) {
animations[listName].emplace_back(new ATranslate(t, duration, looped, interpolationType));
}
void Animatable::a_translateToPoint(const std::string& listName,
const Point4D &point,
double duration,
Animation::LoopOut looped,
Animation::InterpolationType interpolationType) {
animations[listName].emplace_back(new ATranslateToPoint(point, duration, looped, interpolationType));
}
void Animatable::a_rotate(const std::string& listName,
const Point4D &r,
double duration,
Animation::LoopOut looped,
Animation::InterpolationType interpolationType) {
animations[listName].emplace_back(new ARotate(r, duration, looped, interpolationType));
}
void Animatable::a_scale(const std::string& listName,
const Point4D &s,
double duration,
Animation::LoopOut looped,
Animation::InterpolationType interpolationType) {
animations[listName].emplace_back(new AScale(s, duration, looped, interpolationType));
}
void Animatable::a_color(const std::string &listName, const sf::Color &color, double duration, Animation::LoopOut looped,
Animation::InterpolationType interpolationType) {
animations[listName].emplace_back(new AColor(color, duration, looped, interpolationType));
}
void Animatable::a_wait(const std::string& listName, double duration) {
animations[listName].emplace_back(new AWait(duration));
}
void Animatable::a_function(const std::string &listName,
std::function<void()> function,
int calls,
double duration,
Animation::LoopOut looped,
Animation::InterpolationType interpolationType) {
animations[listName].emplace_back(new AFunction(std::move(function), calls, duration, looped, interpolationType));
}
void Animatable::a_update() {
for (auto& [listName, animationList] : animations) {
if (animationList.empty())
continue;
auto it = animationList.begin();
// If it the front animation is 'a_wait()' we should wait until waiting time is over
if (it.operator*()->waitFor()) {
if (!it.operator*()->update(*this))
animationList.erase(it);
continue;
}
// Otherwise we iterate over all animation until we meet animations.end() or wait animation
while (!animationList.empty() && (it != animationList.end()) && (!it.operator*()->waitFor())) {
if (!it.operator*()->update(*this))
animationList.erase(it++);
else
it++;
}
}
}

92
engine/animation/Animatable.h Executable file
View File

@ -0,0 +1,92 @@
//
// Created by Иван Ильин on 26.01.2021.
//
#ifndef ENGINE_ANIMATABLE_H
#define ENGINE_ANIMATABLE_H
#include <list>
#include "../Triangle.h"
//class Animation;
#include "Animation.h"
#include <iostream>
#include <functional>
// All _objects in 3dzavr that should be animated must inherit class Animatable:
class Animatable {
protected:
std::map<std::string, std::list<Animation*>> animations;
public:
Animatable() = default;
virtual ~Animatable() = default;
// All methods about animation begins with 'a_'
void a_translate(const std::string& listName,
const Point4D& t,
double duration = 1,
Animation::LoopOut looped = Animation::None,
Animation::InterpolationType interpolationType = Animation::bezier);
void a_translateToPoint(const std::string& listName,
const Point4D& point,
double duration = 1,
Animation::LoopOut looped = Animation::None,
Animation::InterpolationType interpolationType = Animation::bezier);
void a_rotate(const std::string& listName,
const Point4D& r,
double duration = 1,
Animation::LoopOut looped = Animation::None,
Animation::InterpolationType interpolationType = Animation::bezier);
void a_scale(const std::string& listName,
const Point4D& s,
double duration = 1,
Animation::LoopOut looped = Animation::None,
Animation::InterpolationType interpolationType = Animation::bezier);
void a_wait(const std::string& listName, double duration = 1);
void a_function(const std::string& listName,
std::function<void()> function,
int calls = 1,
double duration = 1,
Animation::LoopOut looped = Animation::None,
Animation::InterpolationType interpolationType = Animation::bezier);
void a_color(const std::string& listName,
const sf::Color& color,
double duration = 1,
Animation::LoopOut looped = Animation::None,
Animation::InterpolationType interpolationType = Animation::bezier);
void a_update();
void a_stopAllAnimations() { animations.clear(); }
void a_stopAnimationList(const std::string& name) { animations[name].clear(); }
[[nodiscard]] bool isInAnim() const {
for(auto& animList : animations)
if (!animList.second.empty())
return true;
return false;
}
[[nodiscard]] bool isInAnimList(const std::string& name) { return !animations[name].empty(); }
// methods to override:
// If you want to create new animation you can either add new virtual function here
// or override one of the following function:
[[nodiscard]] virtual Point4D position() const { return Point4D{}; }
[[nodiscard]] virtual Point4D angle() const { return Point4D{}; }
virtual void translate(const Point4D& dv) {}
virtual void rotate(const Point4D& r) {}
[[nodiscard]] virtual std::vector<Triangle>& triangles() { return *(std::vector<Triangle>*)(new std::vector<Triangle>()); }
virtual void setTriangles(const std::vector<Triangle>& tris) {}
[[nodiscard]] virtual sf::Color color() const {return sf::Color(); }
virtual void setColor(sf::Color c) { }
};
#endif //INC_3DZAVR_ANIMATABLE_H

49
engine/animation/Animation.cpp Executable file
View File

@ -0,0 +1,49 @@
//
// Created by Иван Ильин on 27.01.2021.
//
#include "Animation.h"
#include <utility>
#include "../utils/Log.h"
bool Animation::updateState() {
if(!_started) {
_startAnimationPoint = Time::time();
_endAnimationPoint = _startAnimationPoint + _duration;
_started = true;
return _duration != 0;
}
_timeOld = _time;
// linear normalized time:
_time = (Time::time() - _startAnimationPoint)/(_endAnimationPoint - _startAnimationPoint);
if(_looped != Continue || _time < 0.5)
_dtime = _time - _timeOld;
else {
_time = _timeOld;
//_intType = linear;
}
switch (_intType) {
case bezier:
_p = Interpolation::Bezier(_bezier[0], _bezier[1], _time);
_dp = Interpolation::dBezier(_bezier[0], _bezier[1], _time, _dtime);
break;
case bouncing:
_p = Interpolation::Bouncing(_time);
_dp = Interpolation::dBouncing(_time, _dtime);
break;
case linear:
_p = Interpolation::Linear(_time);
_dp = Interpolation::dLinear(_time, _dtime);
break;
case cos:
_p = Interpolation::Cos(_time);
_dp = Interpolation::dCos(_time, _dtime);
break;
}
return (_time < 1) || _looped == Cycle;
}

62
engine/animation/Animation.h Executable file
View File

@ -0,0 +1,62 @@
//
// Created by Иван Ильин on 26.01.2021.
//
#ifndef ENGINE_ANIMATION_H
#define ENGINE_ANIMATION_H
#include "../utils/Time.h"
#include "../Triangle.h"
#include "Interpolation.h"
class Animatable;
class Animation {
public:
enum InterpolationType {
linear,
cos,
bezier,
bouncing
};
enum LoopOut {
None,
Cycle,
Continue
};
protected:
double _time = 0; // normalized time (from 0 to 1)
double _dtime = 0;
double _timeOld = 0;
double _endAnimationPoint = 0;
double _startAnimationPoint = 0;
double _duration = 0;
bool _started = false;
LoopOut _looped = None;
// p - animation progress
double _p = 0;
double _dp = 0;
InterpolationType _intType = bezier;
Point4D _bezier[2] = {Point4D{0.8, 0}, Point4D{0.2, 1}};
// If '_waitFor' == true then we need to finish all animation before starting this one. (for example for a_wait() or a_scale())
bool _waitFor = false;
bool updateState();
public:
Animation() = default;
virtual ~Animation() = default;
void setBezierParams(const Point4D& p1, const Point4D& p2) { _bezier[0] = p1; _bezier[1] = p2; }
[[nodiscard]] bool waitFor() const { return _waitFor; }
// You should override this method for your particular animation
virtual bool update(Animatable& obj) = 0;
[[nodiscard]] virtual int type() const{return 0;}
};
#endif //INC_3DZAVR_ANIMATION_H

View File

@ -0,0 +1,86 @@
//
// Created by Иван Ильин on 26.01.2021.
//
#ifndef ENGINE_INTERPOLATION_H
#define ENGINE_INTERPOLATION_H
#include "../utils/Point4D.h"
#include <cmath>
namespace Interpolation {
static double Linear(double t);
static double Cos(double t);
static double Bezier(const Point4D& p1, const Point4D& p2, double t);
static double Bouncing(double t);
static double dLinear(double t, double dt);
static double dCos(double t, double dt);
static double dBezier(const Point4D& p1, const Point4D& p2, double t, double dt);
static double dBouncing(double t, double dt);
};
double Interpolation::Linear(double t) {
if(t < 0)
t = -t;
return ((int)trunc(t) % 2) ? 1.0 - (t-trunc(t)) : (t-trunc(t));
}
double Interpolation::Cos(double t) {
return 0.5*(1 - cos(M_PI*Interpolation::Linear(t)));
}
double Interpolation::Bezier(const Point4D &p1, const Point4D &p2, double t) {
t = Interpolation::Linear(t);
double h = 0.000001;
double eps = 0.000001;
// We are trying to find 's' when px = t
auto f = [=](double s){
return 3.0*(1.0-s)*(1.0-s)*s*p1.x() + 3.0*(1.0-s)*s*s*p2.x() + s*s*s - t;
};
// Using found 's' we will calculate resulting py
auto py = [=](double s){
return 3.0*(1.0-s)*(1.0-s)*s*p1.y() + 3.0*(1.0-s)*s*s*p2.y() + s*s*s;
};
auto df = [=](double s){
return (f(s+h) - f(s-h))/(2.0*h);
};
// Newton method
double s1 = 0.0, s2 = 0.5;
int i = 0;
while(std::abs(s1 - s2) > eps) {
s1 = s2;
s2 = s1 - f(s1) / df(s1);
i++;
}
return py(s1);
}
double Interpolation::Bouncing(double t) {
t = Interpolation::Linear(t);
return 0.5*(1.0/(1.0 + exp(10.0*(-4.0*t+0.8))) + (1.0 + 2.5*sin(50.0*(t - 1.0/3.0))*exp(-7.0*t))/(1.0+exp(10.0*(-15.0*t + 3.1))));
}
double Interpolation::dLinear(double t, double dt) {
return ((int)trunc(t) % 2) ? -dt : dt;
}
double Interpolation::dCos(double t, double dt) {
return 0.5*M_PI*sin(M_PI*t)*dt;
}
double Interpolation::dBezier(const Point4D &p1, const Point4D &p2, double t, double dt) {
return Interpolation::Bezier(p1, p2, t + dt) - Interpolation::Bezier(p1, p2, t);
}
double Interpolation::dBouncing(double t, double dt) {
return Bouncing(t + dt) - Bouncing(t);
}
#endif //INC_3DZAVR_INTERPOLATION_H

BIN
engine/fonts/Roboto-Light.ttf Executable file

Binary file not shown.

BIN
engine/fonts/Roboto-Medium.ttf Executable file

Binary file not shown.

BIN
engine/fonts/Roboto-Thin.ttf Executable file

Binary file not shown.

BIN
engine/fonts/fontRU.ttf Executable file

Binary file not shown.

58
engine/gui/Button.cpp Executable file
View File

@ -0,0 +1,58 @@
//
// Created by Иван Ильин on 26.03.2021.
//
#include "Button.h"
#include "../ResourceManager.h"
void Button::select()
{
if (!selected && !pressed)
{
button.setTextureRect(sf::IntRect(selectedState.tx, selectedState.ty, w, h));
selected = true;
}
}
void Button::unSelect()
{
if (selected && !pressed)
{
button.setTextureRect(sf::IntRect(usualState.tx, usualState.ty, w, h));
selected = false;
}
}
void Button::press()
{
if (!pressed)
{
button.setTextureRect(sf::IntRect(pressedState.tx, pressedState.ty, w, h));
if(checkBox)
pressed = true;
clickSound.play();
click();
}
else
{
button.setTextureRect(sf::IntRect(usualState.tx, usualState.ty, w, h));
if(checkBox)
pressed = false;
}
}
void Button::init() {
button.setTexture(*ResourceManager::loadTexture(s_texture));
button.setTextureRect(sf::IntRect(usualState.tx, usualState.ty, w, h));
button.scale(sx, sy);
button.setPosition(x - w*sx/2, y - h*sy/2);
text.setFont(*ResourceManager::loadFont(s_font));
text.setString(s_text);
text.setCharacterSize(h*sy/2);
text.setFillColor(textColor);
text.setPosition(x - text.getLocalBounds().width/2, y - h*sy/2 + text.getLocalBounds().height/4);
clickSound.setBuffer(*ResourceManager::loadSoundBuffer(s_clickSound));
clickSound.setVolume(15);
}

58
engine/gui/Button.h Executable file
View File

@ -0,0 +1,58 @@
//
// Created by Иван Ильин on 26.03.2021.
//
#ifndef ENGINE_BUTTON_H
#define ENGINE_BUTTON_H
#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>
#include <functional>
struct tPos {
int tx;
int ty;
};
struct Button
{
int x;
int y;
int w;
int h;
std::function<void()> click;
std::string s_text;
double sx;
double sy;
std::string s_texture;
tPos usualState;
tPos selectedState;
tPos pressedState;
std::string s_font;
sf::Color textColor;
std::string s_clickSound;
sf::Sprite button;
sf::Text text;
sf::Sound clickSound;
bool selected = false;
bool pressed = false;
bool checkBox = false;
void select();
void unSelect();
void press();
void init();
};
#endif //MINECRAFT_3DZAVR_BUTTON_H

53
engine/gui/Window.cpp Executable file
View File

@ -0,0 +1,53 @@
//
// Created by Иван Ильин on 26.03.2021.
//
#include "Window.h"
#include <utility>
#include "ResourceManager.h"
void Window::addButton(int x, int y, int w, int h, std::function<void()> click, const std::string &text, double sx, double sy,
const std::string &texture, tPos usualState, tPos selectedState, tPos pressedState,
const std::string& font, sf::Color textColor, const std::string& clickSound) {
buttons.push_back(Button{x, y, w, h, std::move(click), text, sx, sy, texture, usualState, selectedState, pressedState, font, textColor, clickSound});
buttons.back().init();
}
void Window::update(const std::shared_ptr<Screen>& screen) {
screen->title(s_name);
screen->window.draw(back);
Point4D mousePos = screen->getMousePosition();
Point4D dMousePos = mousePos - prevMousePosition;
back.setPosition(back.getPosition() - sf::Vector2f(dMousePos.x()/30, dMousePos.y()/30));
bool isPressed = screen->isButtonTapped(sf::Mouse::Left);
for(auto& button : buttons) {
if( mousePos.x() > button.x - button.w*button.sx/2 && mousePos.y() > button.y - button.h*button.sy/2 &&
mousePos.x() < button.x + button.w*button.sx/2 && mousePos.y() < button.y + button.h*button.sy/2) {
button.select();
if(isPressed)
button.press();
} else {
button.unSelect();
}
if(screen->isOpen()) {
screen->window.draw(button.button);
screen->window.draw(button.text);
}
}
prevMousePosition = mousePos;
}
void Window::setBackgroundTexture(const std::string &texture, double sx, double sy, int w, int h) {
s_backTexture = texture;
std::shared_ptr<sf::Texture> t = ResourceManager::loadTexture(s_backTexture);
t->setRepeated(true);
back = sf::Sprite(*t, sf::IntRect(0, 0, w + w/30.0, h + h/30.0));
back.scale(sx, sy);
back.setPosition(sf::Vector2f(-w/30.0, -h/30.0));
}

41
engine/gui/Window.h Executable file
View File

@ -0,0 +1,41 @@
//
// Created by Иван Ильин on 26.03.2021.
//
#ifndef ENGINE_WINDOW_H
#define ENGINE_WINDOW_H
#include <utility>
#include <memory>
#include "Button.h"
#include "Screen.h"
class Window {
private:
std::string s_name;
std::string s_backTexture;
std::vector<Button> buttons;
sf::Sprite back;
Point4D prevMousePosition;
public:
explicit Window(std::string name = "Menu", std::string backTexture = "") : s_name(std::move(name)), s_backTexture(std::move(backTexture)){}
void addButton(int x, int y, int w, int h,
std::function<void()> click,
const std::string& text = "button", double sx = 1, double sy = 1,
const std::string& texture = "", tPos usualState = {}, tPos selectedState = {}, tPos pressedState = {},
const std::string& font = "../engine/fonts/Roboto-Medium.ttf", sf::Color textColor = {255, 255, 255}, const std::string& clickSound = "");
[[nodiscard]] std::string title() const { return s_name; }
void title(const std::string& title) { s_name = title; }
void setBackgroundTexture(const std::string& texture, double sx = 1, double sy = 1, int w = 1920, int h = 1080);
void update(const std::shared_ptr<Screen>& screen);
};
#endif //MINECRAFT_3DZAVR_WINDOW_H

126
engine/network/ClientUDP.cpp Executable file
View File

@ -0,0 +1,126 @@
//
// Created by Neirokan on 30.04.2020
//
#include "ClientUDP.h"
#include "MsgType.h"
#include <thread>
#include "../utils/Time.h"
#include <cmath>
#include "../utils/Log.h"
ClientUDP::ClientUDP() : _lastBroadcast(-INFINITY), _working(false)
{
_socket.setTimeoutCallback(std::bind(&ClientUDP::timeout, this, std::placeholders::_1));
}
bool ClientUDP::connected() const
{
return _socket.ownId();
}
bool ClientUDP::isWorking() const
{
return _working;
}
void ClientUDP::connect(sf::IpAddress ip, sf::Uint16 port)
{
sf::Packet packet;
packet << MsgType::Connect << NETWORK_VERSION;
_working = _socket.bind(0);
_socket.addConnection(_socket.serverId(), ip, port);
_socket.sendRely(packet, _socket.serverId());
Log::log("ClientUDP: connecting to the server...");
}
void ClientUDP::update()
{
if (!isWorking())
return;
while (isWorking() && process());
// Send new client information to server
if (Time::time() - _lastBroadcast > 1.0 / WORLD_UPDATE_RATE && connected()) {
updatePacket();
_lastBroadcast = Time::time();
}
// Socket update
_socket.update();
}
void ClientUDP::disconnect()
{
sf::Packet packet;
packet << MsgType::Disconnect << _socket.ownId();
_socket.send(packet, _socket.serverId());
_socket.unbind();
_working = false;
Log::log("ClientUDP: disconnected from the server.");
processDisconnected();
}
bool ClientUDP::timeout(sf::Uint16 id)
{
Log::log("ClientUDP: timeout from the server.");
if (id != _socket.serverId())
return true;
disconnect();
return false;
}
// Recive and process message.
// Returns true, if some message was received.
bool ClientUDP::process()
{
sf::Packet packet;
sf::Uint16 senderId;
sf::Uint16 targetId;
MsgType type;
if ((type = _socket.receive(packet, senderId)) == MsgType::Empty)
return false;
if (!connected() && type != MsgType::Init)
return true;
switch (type)
{
// here we process any operations based on msg type
case MsgType::Init:
packet >> targetId;
_socket.setId(targetId);
Log::log("ClientUDP: client Id = " + std::to_string(targetId) + " connected.");
processInit(packet);
break;
case MsgType::Update:
processUpdate(packet);
break;
case MsgType::NewClient:
Log::log("ClientUDP: new client init...");
processNewClient(packet);
break;
case MsgType::Disconnect:
packet >> targetId;
if (targetId == _socket.ownId())
disconnect();
Log::log("ClientUDP: client Id = " + std::to_string(targetId) + " disconnected from the server");
processDisconnect(targetId);
break;
default:
processCustomPacket(type, packet);
}
return true;
}

47
engine/network/ClientUDP.h Executable file
View File

@ -0,0 +1,47 @@
//
// Created by Neirokan on 30.04.2020
//
#ifndef ENGINE_CLIENTUDP_H
#define ENGINE_CLIENTUDP_H
#include "ReliableMsg.h"
#include "UDPSocket.h"
#include "config.h"
#include <memory>
class ClientUDP
{
protected:
UDPSocket _socket;
double _lastBroadcast;
bool _working;
bool process();
bool timeout(sf::Uint16 id);
public:
// create new ClientUDP()
explicit ClientUDP();
[[nodiscard]] bool isWorking() const;
[[nodiscard]] bool connected() const;
void connect(sf::IpAddress ip, sf::Uint16 port);
void disconnect();
void update();
// virtual functions
virtual void updatePacket(){};
virtual void processInit(sf::Packet& packet){};
virtual void processUpdate(sf::Packet& packet){};
virtual void processNewClient(sf::Packet& packet){};
virtual void processDisconnect(sf::Uint16 targetId){};
virtual void processCustomPacket(MsgType type, sf::Packet& packet){};
virtual void processDisconnected(){};
};
#endif //INC_3DZAVR_CLIENTUDP_H

18
engine/network/MsgType.cpp Executable file
View File

@ -0,0 +1,18 @@
//
// Created by Neirokan on 30.04.2020
//
#include "MsgType.h"
sf::Packet& operator<<(sf::Packet& packet, MsgType type)
{
return packet << (sf::Uint16)type;
}
sf::Packet& operator>>(sf::Packet& packet, MsgType& type)
{
sf::Uint16 temp;
packet >> temp;
type = (MsgType)temp;
return packet;
}

38
engine/network/MsgType.h Executable file
View File

@ -0,0 +1,38 @@
//
// Created by Neirokan on 30.04.2020
//
#ifndef ENGINE_MSGTYPE_H
#define ENGINE_MSGTYPE_H
#include <SFML/Network.hpp>
enum class MsgType
{
// internal messages
Empty, // Empty message (there are no message)
Error, // Error message (something went wrong)
Confirm, // confirm receive
// external messages
Connect, // connection (client ---> server)
Disconnect, // disconnect (client <==> server)
Init, // initialization (client <--- server)
Update, // update (client <--- server)
ClientUpdate, // update (client ---> server)
NewClient, // add new client (client <--- server)
// custom
Damage,
Kill,
FireTrace,
InitBonuses,
AddBonus,
RemoveBonus,
};
sf::Packet& operator<<(sf::Packet& packet, MsgType type);
sf::Packet& operator>>(sf::Packet& packet, MsgType& type);
#endif //INC_3DZAVR_MSGTYPE_H

23
engine/network/ReliableMsg.cpp Executable file
View File

@ -0,0 +1,23 @@
//
// Created by Neirokan on 30.04.2020
//
#include <cmath>
#include "ReliableMsg.h"
#include "utils/Time.h"
#include "config.h"
ReliableMsg::ReliableMsg(sf::Packet& packet, sf::IpAddress address, sf::Uint16 port) : packet(packet), address(address), port(port), lastTry(-INFINITY), firstTry(Time::time()) {}
ReliableMsg::ReliableMsg(const ReliableMsg& msg) : packet(msg.packet), address(msg.address), port(msg.port), lastTry(msg.lastTry), firstTry(msg.firstTry) {}
bool ReliableMsg::trySend(sf::UdpSocket& socket)
{
if (Time::time() - firstTry > TIMEOUT_SECONDS)
return false;
if (Time::time() - lastTry > RELIABLE_RETRY_TIME)
{
lastTry = Time::time();
socket.send(packet, address, port);
}
return true;
}

27
engine/network/ReliableMsg.h Executable file
View File

@ -0,0 +1,27 @@
//
// Created by Neirokan on 30.04.2020
//
#ifndef ENGINE_RELIABLEMSG_H
#define ENGINE_RELIABLEMSG_H
#include <SFML/Network.hpp>
class ReliableMsg
{
private:
sf::Packet packet;
sf::IpAddress address;
sf::Uint16 port;
double lastTry;
double firstTry;
public:
ReliableMsg(sf::Packet& packet, sf::IpAddress address, sf::Uint16 port);
ReliableMsg(const ReliableMsg& msg);
bool trySend(sf::UdpSocket& socket);
};
#endif //INC_3DZAVR_RELIABLEMSG_H

129
engine/network/ServerUDP.cpp Executable file
View File

@ -0,0 +1,129 @@
//
// Created by Neirokan on 30.04.2020
//
#include <fstream>
#include <sstream>
#include "ServerUDP.h"
#include "utils/Time.h"
#include "MsgType.h"
#include "config.h"
#include "../utils/Log.h"
ServerUDP::ServerUDP() : _lastBroadcast(-INFINITY), _working(false)
{
_socket.setTimeoutCallback(std::bind(&ServerUDP::timeout, this, std::placeholders::_1));
}
bool ServerUDP::isWorking() const
{
return _working;
}
bool ServerUDP::start(sf::Uint16 port)
{
_working = _socket.bind(port);
if(_working)
Log::log("ServerUDP: server successfully started.");
else
Log::log("ServerUDP: failed to start the server.");
return _working;
}
void ServerUDP::update()
{
if (!isWorking())
return;
while (process());
// World state broadcast
if (Time::time() - _lastBroadcast > 1.0 / WORLD_UPDATE_RATE) {
broadcast();
_lastBroadcast = Time::time();
}
// Socket update
_socket.update();
updateInfo();
}
void ServerUDP::stop()
{
for (auto it = _clients.begin(); it != _clients.end();)
{
sf::Packet packet;
packet << MsgType::Disconnect << *it;
_socket.send(packet, *it);
_clients.erase(it++);
}
_socket.unbind();
_working = false;
processStop();
Log::log("ServerUDP: the server was killed.");
}
bool ServerUDP::timeout(sf::Uint16 playerId)
{
sf::Packet packet;
packet << MsgType::Disconnect << playerId;
_clients.erase(playerId);
for (auto client : _clients)
_socket.sendRely(packet, client);
Log::log("ServerUDP: client Id = " + std::to_string(playerId) + " disconnected due to timeout.");
processDisconnect(playerId);
return true;
}
// Recive and process message.
// Returns true, if some message was received.
bool ServerUDP::process()
{
sf::Packet packet;
sf::Packet sendPacket;
sf::Uint16 senderId;
MsgType type;
if ((type = _socket.receive(packet, senderId)) == MsgType::Empty)
return false;
switch (type)
{
// here we process any operations based on msg type
case MsgType::Connect:
Log::log("ServerUDP: client Id = " + std::to_string(senderId) + " connecting...");
processConnect(senderId);
break;
case MsgType::ClientUpdate:
processClientUpdate(senderId, packet);
break;
case MsgType::Disconnect:
Log::log("ServerUDP: client Id = " + std::to_string(senderId) + " disconnected.");
sendPacket << MsgType::Disconnect << senderId;
_clients.erase(senderId);
_socket.removeConnection(senderId);
for (auto client : _clients)
_socket.sendRely(sendPacket, client);
processDisconnect(senderId);
break;
default:
processCustomPacket(type, packet, senderId);
}
return true;
}

50
engine/network/ServerUDP.h Executable file
View File

@ -0,0 +1,50 @@
//
// Created by Neirokan on 30.04.2020
//
#ifndef ENGINE_SERVERUDP_H
#define ENGINE_SERVERUDP_H
#include "World.h"
#include "Camera.h"
#include "ReliableMsg.h"
#include "UDPSocket.h"
#include <memory>
#include <set>
class ServerUDP
{
protected:
UDPSocket _socket;
double _lastBroadcast;
bool _working;
bool process();
bool timeout(sf::Uint16 id);
std::set<sf::Uint16> _clients{};
public:
explicit ServerUDP();
[[nodiscard]] bool isWorking() const;
bool start(sf::Uint16 port);
void stop();
void update();
virtual void updateInfo(){};
// virtual functions
virtual void broadcast(){};
// here you have to send Init message back to 'targetId' and send NewClient message to all '_clients'
virtual void processConnect(sf::Uint16 senderId){};
virtual void processClientUpdate(sf::Uint16 senderId, sf::Packet& packet){};
virtual void processDisconnect(sf::Uint16 senderId){};
virtual void processCustomPacket(MsgType type, sf::Packet& packet, sf::Uint16 senderId){};
virtual void processStop(){};
};
#endif //INC_3DZAVR_SERVERUDP_H

View File

@ -0,0 +1,44 @@
//
// Created by Neirokan on 30.04.2020
//
#include "UDPConnection.h"
#include "utils/Time.h"
#include "config.h"
UDPConnection::UDPConnection(sf::Uint16 id, sf::IpAddress ip, sf::Uint16 port) : _id(id), _ip(ip), _port(port), lastMsg(Time::time()) {}
sf::Uint16 UDPConnection::id() const
{
return _id;
}
const sf::IpAddress& UDPConnection::ip() const
{
return _ip;
}
sf::Uint16 UDPConnection::port() const
{
return _port;
}
bool UDPConnection::timeout() const
{
return Time::time() - lastMsg > TIMEOUT_SECONDS;
}
bool UDPConnection::same(sf::IpAddress& ip, sf::Uint16 port) const
{
return _ip == ip && _port == port;
}
void UDPConnection::update()
{
lastMsg = Time::time();
}
void UDPConnection::send(sf::UdpSocket& socket, sf::Packet& packet)
{
socket.send(packet, _ip, _port);
}

30
engine/network/UDPConnection.h Executable file
View File

@ -0,0 +1,30 @@
//
// Created by Neirokan on 30.04.2020
//
#ifndef ENGINE_UDPCONNECTION_H
#define ENGINE_UDPCONNECTION_H
#include <SFML/Network.hpp>
class UDPConnection
{
private:
sf::Uint16 _id;
sf::IpAddress _ip;
sf::Uint16 _port;
double lastMsg;
public:
explicit UDPConnection(sf::Uint16 id, sf::IpAddress ip, sf::Uint16 port);
[[nodiscard]] sf::Uint16 id() const;
[[nodiscard]] const sf::IpAddress& ip() const;
[[nodiscard]] sf::Uint16 port() const;
[[nodiscard]] bool timeout() const;
bool same(sf::IpAddress& ip, sf::Uint16 port) const;
void update();
void send(sf::UdpSocket& socket, sf::Packet& packet);
};
#endif //INC_3DZAVR_UDPCONNECTION_H

195
engine/network/UDPSocket.cpp Executable file
View File

@ -0,0 +1,195 @@
//
// Created by Neirokan on 30.04.2020
//
#include "UDPSocket.h"
#include "utils/Time.h"
#include "config.h"
#include <algorithm>
UDPSocket::UDPSocket() : _ownId(0), _nextRelyMsgId(0)
{
_socket.setBlocking(false);
}
void UDPSocket::addConnection(sf::Uint16 id, sf::IpAddress ip, sf::Uint16 port)
{
_connections.insert({ id, UDPConnection(id, ip, port) });
}
void UDPSocket::removeConnection(sf::Uint16 id)
{
_connections.erase(id);
}
bool UDPSocket::bind(sf::Uint16 port)
{
return _socket.bind(port) == sf::Socket::Status::Done;
}
void UDPSocket::unbind()
{
sf::Packet packet;
packet << MsgType::Disconnect << _ownId;
for (auto it = _connections.begin(); it != _connections.end();)
{
send(packet, it->first);
_connections.erase(it++);
}
_relyPackets.clear();
_confirmTimes.clear();
_socket.unbind();
setId(0);
}
void UDPSocket::setTimeoutCallback(std::function<bool(sf::Uint16)> callback)
{
_timeoutCallback = std::move(callback);
}
void UDPSocket::clearTimeoutCallback()
{
_timeoutCallback = nullptr;
}
void UDPSocket::setId(sf::Uint16 id)
{
_ownId = id;
}
sf::Uint16 UDPSocket::ownId() const
{
return _ownId;
}
sf::Uint16 UDPSocket::serverId() const
{
return _serverId;
}
void UDPSocket::sendRely(const sf::Packet& packet, const sf::IpAddress& ip, sf::Uint16 port)
{
sf::Packet finalPacket;
finalPacket << _ownId << true << _nextRelyMsgId;
finalPacket.append(packet.getData(), packet.getDataSize());
_relyPackets.insert({ _nextRelyMsgId++, ReliableMsg(finalPacket, ip, port) });
}
void UDPSocket::sendRely(const sf::Packet& packet, sf::Uint16 id)
{
if (!_connections.count(id))
return;
this->sendRely(packet, _connections.at(id).ip(), _connections.at(id).port());
}
void UDPSocket::send(const sf::Packet& packet, const sf::IpAddress& ip, sf::Uint16 port)
{
sf::Packet finalPacket;
finalPacket << _ownId << false << _serverId;
finalPacket.append(packet.getData(), packet.getDataSize());
_socket.send(finalPacket, ip, port);
}
void UDPSocket::send(const sf::Packet& packet, sf::Uint16 id)
{
if (!_connections.count(id))
return;
this->send(packet, _connections.at(id).ip(), _connections.at(id).port());
}
void UDPSocket::update()
{
for (auto it = _connections.begin(); it != _connections.end();)
{
if (!it->second.timeout())
++it;
else
{
if (_timeoutCallback && !_timeoutCallback(it->first))
return;
_connections.erase(it++);
}
}
for (auto it = _relyPackets.begin(); it != _relyPackets.end();)
{
if (!it->second.trySend(_socket))
_relyPackets.erase(it++);
else
++it;
}
for (auto it = _confirmTimes.begin(); it != _confirmTimes.end();)
{
if (Time::time() - it->second > TIMEOUT_SECONDS)
_confirmTimes.erase(it++);
else
++it;
}
}
MsgType UDPSocket::receive(sf::Packet& packet, sf::Uint16& senderId)
{
// Receive message
sf::IpAddress ip;
sf::Uint16 port;
packet.clear();
if (_socket.receive(packet, ip, port) != sf::Socket::Status::Done)
return MsgType::Empty;
// Read header
bool reply = false;
sf::Uint16 msgId = 0;
MsgType type;
senderId = 0;
if (!(packet >> senderId >> reply >> msgId >> type))
return MsgType::Error;
if (_connections.count(senderId))
_connections.at(senderId).update();
if (type == MsgType::Confirm)
{
_relyPackets.erase(msgId);
return MsgType::Confirm;
}
if (type == MsgType::Connect)
{
sf::Uint32 version = 0;
if (!(packet >> version) || version != NETWORK_VERSION)
return MsgType::Error;
sf::Uint16 tmp;
for (tmp = 64; tmp >= 1; tmp--)
{
if (!_connections.count(tmp))
senderId = tmp;
else
if (_connections.at(tmp).same(ip, port))
return MsgType::Error;
}
_connections.insert({ senderId, UDPConnection(senderId, ip, port) });
}
if (!_connections.count(senderId) || !_connections.at(senderId).same(ip, port) || reply && confirmed(msgId, senderId))
return MsgType::Error;
return type;
}
bool UDPSocket::confirmed(sf::Uint16 msgId, sf::Uint16 senderId)
{
sf::Packet confirmPacket;
confirmPacket << _ownId << false << msgId << MsgType::Confirm;
_connections.at(senderId).send(_socket, confirmPacket);
sf::Uint32 confirmId;
confirmId = (senderId << 16) | msgId;
bool repeat = _confirmTimes.count(confirmId);
_confirmTimes[confirmId] = Time::time();
return repeat;
}

53
engine/network/UDPSocket.h Executable file
View File

@ -0,0 +1,53 @@
//
// Created by Neirokan on 30.04.2020
//
#ifndef ENGINE_UDPSOCKET_H
#define ENGINE_UDPSOCKET_H
#include <memory>
#include <map>
#include <functional>
#include "ReliableMsg.h"
#include "UDPConnection.h"
#include "MsgType.h"
class UDPSocket
{
private:
sf::UdpSocket _socket;
sf::Uint16 _nextRelyMsgId;
sf::Uint16 _ownId;
const sf::Uint16 _serverId = 0;
std::map<sf::Uint16, UDPConnection> _connections;
std::map<sf::Uint16, ReliableMsg> _relyPackets;
std::map<sf::Uint32, double> _confirmTimes;
std::function<bool(sf::Uint16)> _timeoutCallback;
bool confirmed(sf::Uint16 msgId, sf::Uint16 senderId);
public:
explicit UDPSocket();
bool bind(sf::Uint16 port);
void unbind();
void setTimeoutCallback(std::function<bool(sf::Uint16)> callback);
void clearTimeoutCallback();
void addConnection(sf::Uint16 id, sf::IpAddress ip, sf::Uint16 port);
void removeConnection(sf::Uint16 id);
void setId(sf::Uint16 id);
[[nodiscard]] sf::Uint16 ownId() const;
[[nodiscard]] sf::Uint16 serverId() const;
void send(const sf::Packet& packet, const sf::IpAddress& ip, sf::Uint16 port);
void send(const sf::Packet& packet, sf::Uint16 id);
void sendRely(const sf::Packet& packet, const sf::IpAddress& ip, sf::Uint16 port);
void sendRely(const sf::Packet& packet, sf::Uint16 id);
void update();
MsgType receive(sf::Packet& packet, sf::Uint16& senderId);
};
#endif //INC_3DZAVR_UDPSOCKET_H

13
engine/network/config.h Executable file
View File

@ -0,0 +1,13 @@
//
// Created by Иван Ильин on 05.04.2021.
//
#ifndef ENGINE_CONFIG_H
#define ENGINE_CONFIG_H
#define NETWORK_VERSION 1U
#define TIMEOUT_SECONDS 5
#define WORLD_UPDATE_RATE 30
#define RELIABLE_RETRY_TIME (1.0/20)
#endif //INC_3DZAVR_CONFIG_H

2
engine/network/connect.txt Executable file
View File

@ -0,0 +1,2 @@
127.0.0.1
54000

1
engine/network/server.txt Executable file
View File

@ -0,0 +1 @@
54000

330
engine/physics/RigidBody.cpp Executable file
View File

@ -0,0 +1,330 @@
//
// Created by Иван Ильин on 05.02.2021.
//
#include "RigidBody.h"
#include "../Plane.h"
#include "../utils/Log.h"
#include "../utils/Time.h"
#include <iostream>
#include <cmath>
Point4D RigidBody::_findFurthestPoint(const Point4D& direction) {
Point4D maxPoint = {};
auto maxDistance = (double)-INFINITY;
for(auto& tri : triangles()){
for(auto point : tri.p){
point += position();
double distance = point.dot(direction);
if(distance > maxDistance) {
maxDistance = distance;
maxPoint = point;
}
}
}
return maxPoint;
}
Point4D RigidBody::_support(const std::shared_ptr<RigidBody>& obj, const Point4D& direction) {
Point4D p1 = _findFurthestPoint(direction);
Point4D p2 = obj->_findFurthestPoint(-direction);
return p1 - p2;
}
bool RigidBody::_nextSimplex(Simplex &points, Point4D &direction) {
switch (points.size()) {
case 2: return _line(points, direction);
case 3: return _triangle(points, direction);
case 4: return _tetrahedron(points, direction);
}
// never should be here
return false;
}
bool RigidBody::_line(Simplex& points, Point4D& direction) {
Point4D a = points[0];
Point4D b = points[1];
Point4D ab = b - a;
Point4D ao = - a;
if (ab.dot(ao) > 0) {
direction = ab.cross3D(ao).cross3D(ab);
} else {
points = { a };
direction = ao;
}
return false;
}
bool RigidBody::_triangle(Simplex &points, Point4D &direction) {
Point4D a = points[0];
Point4D b = points[1];
Point4D c = points[2];
Point4D ab = b - a;
Point4D ac = c - a;
Point4D ao = - a;
Point4D abc = ab.cross3D(ac);
if (abc.cross3D(ac).dot(ao) > 0) {
if (ac.dot(ao) > 0) {
points = { a, c };
direction = ac.cross3D(ao).cross3D(ac);
}
else
{
return _line(points = { a, b }, direction);
}
}
else
{
if (ab.cross3D(abc).dot(ao) > 0) {
return _line(points = { a, b }, direction);
}
else
{
if (abc.dot(ao) > 0) {
direction = abc;
} else {
points = { a, c, b };
direction = -abc;
}
}
}
return false;
}
bool RigidBody::_tetrahedron(Simplex &points, Point4D &direction) {
Point4D a = points[0];
Point4D b = points[1];
Point4D c = points[2];
Point4D d = points[3];
Point4D ab = b - a;
Point4D ac = c - a;
Point4D ad = d - a;
Point4D ao = - a;
Point4D abc = ab.cross3D(ac);
Point4D acd = ac.cross3D(ad);
Point4D adb = ad.cross3D(ab);
if (abc.dot(ao) > 0) {
return _triangle(points = { a, b, c }, direction);
}
if (acd.dot(ao) > 0) {
return _triangle(points = { a, c, d }, direction);
}
if (adb.dot(ao) > 0) {
return _triangle(points = { a, d, b }, direction);
}
return true;
}
std::pair<bool, Simplex> RigidBody::checkGJKCollision(const std::shared_ptr<RigidBody>& obj) {
// Get initial support point in any direction
Point4D support = _support(obj, Point4D::unit_x());
// Simplex is an array of points, max count is 4
Simplex points;
points.push_front(support);
// New direction is towards the origin
Point4D direction = -support;
int iterations = 0;
while (true && iterations < 50) {
support = _support(obj, direction);
if (support.dot(direction) <= 0)
return std::make_pair(false, points); // no collision
points.push_front(support);
if (_nextSimplex(points, direction)) {
if(obj->isCollider())
_inCollision = true;
return std::make_pair(true, points);
}
iterations++;
}
return std::make_pair(false, points); // no collision
}
CollisionPoint RigidBody::EPA(const Simplex& simplex, const std::shared_ptr<RigidBody>& obj) {
std::vector<Point4D> polytope(simplex.begin(), simplex.end());
std::vector<size_t> faces = {
0, 1, 2,
0, 3, 1,
0, 2, 3,
1, 3, 2
};
// list: vector4(normal, distance), index: min distance
auto [normals, minFace] = GetFaceNormals(polytope, faces);
Point4D minNormal;
double minDistance = INFINITY;
int iterations = 0;
while ((minDistance == INFINITY) && (iterations < 50)) {
minNormal = normals[minFace];
minDistance = normals[minFace].w();
Point4D support = _support(obj, minNormal);
double sDistance = minNormal.dot(support);
if (abs(sDistance - minDistance) > 0.0001) {
minDistance = INFINITY;
std::vector<std::pair<size_t, size_t>> uniqueEdges;
for (size_t i = 0; i < normals.size(); i++) {
if (normals[i].dot(support) > 0) {
size_t f = i * 3;
AddIfUniqueEdge(uniqueEdges, faces, f, f + 1);
AddIfUniqueEdge(uniqueEdges, faces, f + 1, f + 2);
AddIfUniqueEdge(uniqueEdges, faces, f + 2, f );
faces[f + 2] = faces.back(); faces.pop_back();
faces[f + 1] = faces.back(); faces.pop_back();
faces[f ] = faces.back(); faces.pop_back();
normals[i] = normals.back(); normals.pop_back();
i--;
}
}
std::vector<size_t> newFaces;
for (auto [edgeIndex1, edgeIndex2] : uniqueEdges) {
newFaces.push_back(edgeIndex1);
newFaces.push_back(edgeIndex2);
newFaces.push_back(polytope.size());
}
polytope.push_back(support);
auto [newNormals, newMinFace] = GetFaceNormals(polytope, newFaces);
if(newNormals.empty())
break;
double oldMinDistance = INFINITY;
for (size_t i = 0; i < normals.size(); i++) {
if (normals[i].w() < oldMinDistance) {
oldMinDistance = normals[i].w();
minFace = i;
}
}
if (newNormals[newMinFace].w() < oldMinDistance) {
minFace = newMinFace + normals.size();
}
faces .insert(faces .end(), newFaces .begin(), newFaces .end());
normals.insert(normals.end(), newNormals.begin(), newNormals.end());
}
iterations++;
}
CollisionPoint points;
points.normal = minNormal;
points.depth = minDistance + 0.0001;
points.hasCollision = minDistance < INFINITY;
_collisionNormal = minNormal;
return points;
}
std::pair<std::vector<Point4D>, size_t> RigidBody::GetFaceNormals(const std::vector<Point4D>& polytope, const std::vector<size_t>& faces) {
std::vector<Point4D> normals;
size_t minTriangle = 0;
double minDistance = INFINITY;
for (size_t i = 0; i < faces.size(); i += 3) {
Point4D a = polytope[faces[i ]];
Point4D b = polytope[faces[i + 1]];
Point4D c = polytope[faces[i + 2]];
Point4D normal = (b - a).cross3D(c - a).normalize();
double distance = normal.dot(a);
if (distance < 0) {
normal *= -1;
distance *= -1;
}
normal = Point4D{normal.x(), normal.y(), normal.z(), distance};
normals.emplace_back(normal);
if (distance < minDistance) {
minTriangle = i / 3;
minDistance = distance;
}
}
return { normals, minTriangle };
}
void RigidBody::AddIfUniqueEdge(std::vector<std::pair<size_t, size_t>>& edges, const std::vector<size_t>& faces, size_t a, size_t b) {
auto reverse = std::find( // 0--<--3
edges.begin(), // / \ B / A: 2-0
edges.end(), // / A \ / B: 0-2
std::make_pair(faces[b], faces[a]) // 1-->--2
);
if (reverse != edges.end()) {
edges.erase(reverse);
}
else {
edges.emplace_back(faces[a], faces[b]);
}
}
void RigidBody::updatePhysicsState() {
translate(p_velocity * Time::deltaTime());
p_velocity += p_acceleration * Time::deltaTime();
rotate(p_angularVelocity * Time::deltaTime());
p_angularVelocity += p_angularAcceleration * Time::deltaTime();
}
void RigidBody::setVelocity(const Point4D& velocity) {
p_velocity = velocity;
}
void RigidBody::addVelocity(const Point4D &velocity) {
p_velocity += velocity;
}
void RigidBody::setAngularVelocity(const Point4D& angularVelocity) {
p_angularVelocity = angularVelocity;
}
void RigidBody::setAcceleration(const Point4D& acceleration) {
p_acceleration = acceleration;
}
void RigidBody::setAngularAcceleration(const Point4D& angularAcceleration) {
p_angularAcceleration = angularAcceleration;
}

88
engine/physics/RigidBody.h Executable file
View File

@ -0,0 +1,88 @@
//
// Created by Иван Ильин on 05.02.2021.
//
#ifndef ENGINE_RIGIDBODY_H
#define ENGINE_RIGIDBODY_H
#include <vector>
#include <memory>
#include "../utils/Point4D.h"
#include "../Triangle.h"
#include "Simplex.h"
struct CollisionPoint {
Point4D a; // Furthest point of a into b
Point4D b; // Furthest point of b into a
Point4D normal; // b a normalized
double depth; // Length of b a
bool hasCollision;
};
class RigidBody {
protected:
Point4D p_velocity;
Point4D p_acceleration;
Point4D p_angularVelocity;
Point4D p_angularAcceleration;
double _mass = 1.0;
bool _collision = false;
bool _isCollider = true;
bool _inCollision = false;
Point4D _collisionNormal;
Point4D _findFurthestPoint(const Point4D& direction);
Point4D _support(const std::shared_ptr<RigidBody>& obj, const Point4D& direction);
static bool _nextSimplex(Simplex& points, Point4D& direction);
static bool _line(Simplex& points, Point4D& direction);
static bool _triangle(Simplex& points, Point4D& direction);
static bool _tetrahedron(Simplex& points, Point4D& direction);
static std::pair<std::vector<Point4D>, size_t> GetFaceNormals(const std::vector<Point4D>& polytope, const std::vector<size_t>& faces);
static void AddIfUniqueEdge(std::vector<std::pair<size_t, size_t>>& edges, const std::vector<size_t>& faces, size_t a, size_t b);
public:
RigidBody() = default;
virtual ~RigidBody() = default;
std::pair<bool, Simplex> checkGJKCollision(const std::shared_ptr<RigidBody>& obj);
CollisionPoint EPA(const Simplex& simplex, const std::shared_ptr<RigidBody>& obj);
[[nodiscard]] bool isCollision() const { return _collision; }
[[nodiscard]] bool inCollision() const {return _inCollision; }
[[nodiscard]] bool isCollider() const {return _isCollider; }
[[nodiscard]] Point4D collisionNormal() const {return _collisionNormal; }
void setInCollision(bool c) { _inCollision = c; }
void setCollisionNormal(const Point4D& c) { _collisionNormal = c; }
void setCollision(bool c) { _collision= c; }
void setCollider(bool c) { _isCollider = c; }
[[nodiscard]] virtual std::vector<Triangle>& triangles() = 0;
[[nodiscard]] virtual Point4D position() const = 0;
virtual void translate(const Point4D& dv) = 0;
virtual void rotate(const Point4D& r) = 0;
void updatePhysicsState();
void setVelocity(const Point4D& velocity);
void setAngularVelocity(const Point4D& angularVelocity);
void addVelocity(const Point4D& velocity);
void setAcceleration(const Point4D& acceleration);
void setAngularAcceleration(const Point4D& angularAcceleration);
[[nodiscard]] Point4D velocity() const { return p_velocity; }
[[nodiscard]] double mass() const { return _mass; }
void setMass(double val) { _mass = val; }
};
#endif //INC_3DZAVR_RIGIDBODY_H

38
engine/physics/Simplex.h Executable file
View File

@ -0,0 +1,38 @@
//
// Created by Иван Ильин on 08.03.2021.
//
#ifndef ENGINE_SIMPLEX_H
#define ENGINE_SIMPLEX_H
#include "../utils/Point4D.h"
struct Simplex {
private:
std::array<Point4D, 4> m_points{};
unsigned m_size = 0;
public:
Simplex() = default;
Simplex& operator=(std::initializer_list<Point4D> list) {
for (auto v = list.begin(); v != list.end(); v++) {
m_points[std::distance(list.begin(), v)] = *v;
}
m_size = list.size();
return *this;
}
void push_front(const Point4D& point) {
m_points = { point, m_points[0], m_points[1], m_points[2] };
m_size = std::min(m_size + 1, 4u);
}
Point4D& operator[](unsigned i) { return m_points[i]; }
[[nodiscard]] unsigned size() const { return m_size; }
[[nodiscard]] auto begin() const { return m_points.begin(); }
[[nodiscard]] auto end() const { return m_points.end() - (4 - m_size); }
};
#endif //INC_3DZAVR_SIMPLEX_H

38
engine/physics/Solver.cpp Executable file
View File

@ -0,0 +1,38 @@
//
// Created by Иван Ильин on 10.03.2021.
//
#include "Solver.h"
#include "utils/Log.h"
void Solver::solveCollision(const std::shared_ptr<RigidBody>& obj1, const std::shared_ptr<RigidBody>& obj2, const CollisionPoint& collision) {
if(!collision.hasCollision)
return;
Point4D obj1_velocity_parallel = collision.normal * obj1->velocity().dot(collision.normal);
obj1_velocity_parallel = Point4D{obj1_velocity_parallel.x(), obj1_velocity_parallel.y(), obj1_velocity_parallel.z(), 0};
Point4D obj1_velocity_perpendicular = obj1->velocity() - obj1_velocity_parallel;
Point4D obj2_velocity_parallel = collision.normal * obj2->velocity().dot(collision.normal);
Point4D obj2_velocity_perpendicular = obj2->velocity() - obj2_velocity_parallel;
if(obj1->isCollision()) {
if(obj1->velocity().dot(collision.normal) > 0) {
obj1->setVelocity(obj1_velocity_perpendicular);
}
}
if(obj2->isCollision()) {
obj2->setVelocity(obj2_velocity_perpendicular - obj2_velocity_parallel * 0.8);
}
if(obj1->isCollision() && obj2->isCollision()) {
obj1->translate(-collision.normal * collision.depth/2.0);
obj2->translate(collision.normal * collision.depth/2.0);
} else if(obj1->isCollision())
obj1->translate(-collision.normal * collision.depth);
else
obj2->translate(collision.normal * collision.depth);
}

16
engine/physics/Solver.h Executable file
View File

@ -0,0 +1,16 @@
//
// Created by Иван Ильин on 10.03.2021.
//
#ifndef ENGINE_SOLVER_H
#define ENGINE_SOLVER_H
#include "RigidBody.h"
class Solver {
public:
static void solveCollision(const std::shared_ptr<RigidBody>& obj1, const std::shared_ptr<RigidBody>& obj2, const CollisionPoint& collision);
};
#endif //INC_3DZAVR_SOLVER_H

22
engine/utils/Log.cpp Executable file
View File

@ -0,0 +1,22 @@
//
// Created by Иван Ильин on 13.01.2021.
//
#include "Log.h"
#include "Time.h"
#include <ctime>
#include <fstream>
#include <iostream>
namespace Log
{
void log(const std::string& message) {
time_t now = time(nullptr);
char* dt = ctime(&now);
std::fstream file("3dzavr_log.txt", std::ios::out | std::ios::app);
file << dt << "\t" << message << " (" << Time::fps() << " fps)" << std::endl;
std::cout << dt << "\t" << message << " (" << Time::fps() << " fps)" << std::endl;
file.close();
}
}

16
engine/utils/Log.h Executable file
View File

@ -0,0 +1,16 @@
//
// Created by Иван Ильин on 13.01.2021.
//
#ifndef ENGINE_LOG_H
#define ENGINE_LOG_H
#include <string>
namespace Log
{
void log(const std::string& message);
};
#endif //INC_3DZAVR_LOG_H

430
engine/utils/Matrix4x4.cpp Executable file
View File

@ -0,0 +1,430 @@
//
// Created by Иван Ильин on 12.01.2021.
//
#include "Matrix4x4.h"
#include <cassert>
#include <cmath>
[[nodiscard]] const std::array<double, 4>& Matrix4x4::operator[] (int i) const {
return _arr_matrix[i];
}
std::array<double, 4> &Matrix4x4::operator[](int i) {
return _arr_matrix[i];
}
[[nodiscard]] Matrix4x4 Matrix4x4::operator-() const {
Matrix4x4 result(*this);
for(int i = 0; i < 4; i++)
for(int j = 0; j < 4; j++)
result[i][j] = -_arr_matrix[i][j];
return result;
}
[[nodiscard]] Matrix4x4 Matrix4x4::operator+() const {
return Matrix4x4(*this);
}
bool Matrix4x4::operator==(const Matrix4x4 &matrix4X4) const {
for(int i = 0; i < 4; i++)
for(int j = 0; j < 4; j++)
if(_arr_matrix[i][j] != matrix4X4[i][j])
return false;
return true;
}
bool Matrix4x4::operator!=(const Matrix4x4 &matrix4X4) const {
return !(*this==matrix4X4);
}
Matrix4x4 Matrix4x4::operator+(const Matrix4x4 &matrix4X4) const {
return Matrix4x4(*this) += matrix4X4;
}
Matrix4x4 Matrix4x4::operator-(const Matrix4x4 &matrix4X4) const {
return Matrix4x4(*this) -= matrix4X4;
}
Matrix4x4 Matrix4x4::operator*(const Matrix4x4 &matrix4X4) const {
return Matrix4x4(*this) *= matrix4X4;
}
Matrix4x4 Matrix4x4::operator/(const Matrix4x4 &matrix4X4) const {
return Matrix4x4(*this) /= matrix4X4;
}
Matrix4x4 &Matrix4x4::operator+=(const Matrix4x4 &matrix4X4) {
for(int i = 0; i < 4; i++)
for(int j = 0; j < 4; j++)
_arr_matrix[i][j] += matrix4X4[i][j];
return *this;
}
Matrix4x4 &Matrix4x4::operator-=(const Matrix4x4 &matrix4X4) {
(*this) += -matrix4X4;
return *this;
}
Matrix4x4 &Matrix4x4::operator*=(const Matrix4x4 &matrix4X4) {
Matrix4x4 copy(*this);
this->setZero();
for(int i = 0; i < 4; i++)
for(int j = 0; j < 4; j++)
for(int k = 0; k < 4; k++)
_arr_matrix[i][j] += copy[i][k] * matrix4X4[k][j];
return *this;
}
Matrix4x4 &Matrix4x4::operator/=(const Matrix4x4 &matrix4X4) {
for(int i = 0; i < 4; i++)
for(int j = 0; j < 4; j++) {
assert(matrix4X4[i][j] != 0);
_arr_matrix[i][j] /= matrix4X4[i][j];
}
return *this;
}
Matrix4x4 Matrix4x4::operator+(double number) const {
return Matrix4x4(*this) += number;
}
Matrix4x4 Matrix4x4::operator-(double number) const {
return *this+(-number);
}
Matrix4x4 Matrix4x4::operator*(double number) const {
return Matrix4x4(*this) *= number;
}
Matrix4x4 Matrix4x4::operator/(double number) const {
assert(number != 0);
return *this*(1.0/number);
}
Matrix4x4 &Matrix4x4::operator+=(double number) {
for(int i = 0; i < 4; i++)
for(int j = 0; j < 4; j++)
_arr_matrix[i][j] += number;
return *this;
}
Matrix4x4 &Matrix4x4::operator-=(double number) {
return (*this) += -number;
}
Matrix4x4 &Matrix4x4::operator*=(double number) {
for(int i = 0; i < 4; i++)
for(int j = 0; j < 4; j++)
_arr_matrix[i][j] *= number;
return *this;
}
Matrix4x4 &Matrix4x4::operator/=(double number) {
return (*this) *= 1.0/number;
}
Point4D Matrix4x4::operator*(const Point4D &point4D) const {
return Point4D(
_arr_matrix[0][0] * point4D.x() + _arr_matrix[0][1] * point4D.y() + _arr_matrix[0][2] * point4D.z() + _arr_matrix[0][3] * point4D.w(),
_arr_matrix[1][0] * point4D.x() + _arr_matrix[1][1] * point4D.y() + _arr_matrix[1][2] * point4D.z() + _arr_matrix[1][3] * point4D.w(),
_arr_matrix[2][0] * point4D.x() + _arr_matrix[2][1] * point4D.y() + _arr_matrix[2][2] * point4D.z() + _arr_matrix[2][3] * point4D.w(),
_arr_matrix[3][0] * point4D.x() + _arr_matrix[3][1] * point4D.y() + _arr_matrix[3][2] * point4D.z() + _arr_matrix[3][3] * point4D.w()
);
}
double Matrix4x4::det3D() {
return _arr_matrix[0][0] * (_arr_matrix[1][1] * _arr_matrix[2][2] - _arr_matrix[1][2] * _arr_matrix[2][1]) -
_arr_matrix[0][1] * (_arr_matrix[1][0] * _arr_matrix[2][2] - _arr_matrix[1][2] * _arr_matrix[2][0]) +
_arr_matrix[0][2] * (_arr_matrix[1][0] * _arr_matrix[2][1] - _arr_matrix[2][0] * _arr_matrix[1][1]);
}
Matrix4x4 Matrix4x4::inv3D() {
double det = det3D();
assert(det != 0);
Matrix4x4 result{};
result[3][3] = 1.0;
result[0][0] = _arr_matrix[1][1] * _arr_matrix[2][2] - _arr_matrix[2][1] * _arr_matrix[1][2];
result[1][0] = _arr_matrix[0][1] * _arr_matrix[2][2] - _arr_matrix[2][1] * _arr_matrix[0][2];
result[2][0] = _arr_matrix[0][1] * _arr_matrix[1][2] - _arr_matrix[1][1] * _arr_matrix[0][2];
result[0][1] = _arr_matrix[1][0] * _arr_matrix[2][2] - _arr_matrix[2][0] * _arr_matrix[1][2];
result[1][1] = _arr_matrix[0][0] * _arr_matrix[2][2] - _arr_matrix[2][0] * _arr_matrix[0][2];
result[2][1] = _arr_matrix[0][0] * _arr_matrix[1][2] - _arr_matrix[1][0] * _arr_matrix[0][2];
result[0][2] = _arr_matrix[1][0] * _arr_matrix[2][1] - _arr_matrix[2][0] * _arr_matrix[1][1];
result[1][2] = _arr_matrix[0][0] * _arr_matrix[2][1] - _arr_matrix[2][0] * _arr_matrix[0][1];
result[2][2] = _arr_matrix[0][0] * _arr_matrix[1][1] - _arr_matrix[1][0] * _arr_matrix[0][1];
return result /= det;
}
Matrix4x4 &Matrix4x4::transpose() {
Matrix4x4 result{};
for(int i = 0; i < 4; i++)
for(int j = 0; j < 4; j++)
result[i][j] = _arr_matrix[j][i];
*this = result;
return *this;
}
Matrix4x4 &Matrix4x4::setIdentity() {
for(int i = 0; i < 4; i++)
for(int j = 0; j < 4; j++)
if(i == j)
_arr_matrix[j][i] = 1.0;
else
_arr_matrix[j][i] = 0.0;
return *this;
}
Matrix4x4 &Matrix4x4::setOnes() {
setConstants(1.0);
return *this;
}
Matrix4x4 &Matrix4x4::setZero() {
setConstants(0.0);
return *this;
}
Matrix4x4 &Matrix4x4::setConstants(double value) {
for(int i = 0; i < 4; i++)
for(int j = 0; j < 4; j++)
_arr_matrix[j][i] = value;
return *this;
}
const std::array<std::array<double, 4>, 4> &Matrix4x4::data() const {
return _arr_matrix;
}
std::array<std::array<double, 4>, 4> &Matrix4x4::data() {
return _arr_matrix;
}
Matrix4x4 Matrix4x4::Identity() {
return Matrix4x4().setIdentity();
}
Matrix4x4 Matrix4x4::Zero() {
return Matrix4x4();
}
Matrix4x4 Matrix4x4::Constant(double value) {
return Matrix4x4().setConstants(value);
}
Matrix4x4 Matrix4x4::Scale(double sx, double sy, double sz) {
Matrix4x4 s{};
s[0][0] = sx;
s[1][1] = sy;
s[2][2] = sz;
s[3][3] = 1;
return s;
}
Matrix4x4 Matrix4x4::Translation(double dx, double dy, double dz) {
Matrix4x4 t{};
/*
* ( 1 0 0 dx )(x) (x + dx)
* ( 0 1 0 dy )(y) = (y + dy)
* ( 0 0 1 dz )(z) (z + dz)
* ( 0 0 0 1 )(1) ( 1 )
*/
t[0][0] = 1.0;
t[1][1] = 1.0;
t[2][2] = 1.0;
t[3][3] = 1.0;
t[0][3] = dx;
t[1][3] = dy;
t[2][3] = dz;
return t;
}
Matrix4x4 Matrix4x4::Translation(const Point4D& v) {
Matrix4x4 t{};
/*
* ( 1 0 0 dx )(x) (x + dx)
* ( 0 1 0 dy )(y) = (y + dy)
* ( 0 0 1 dz )(z) (z + dz)
* ( 0 0 0 1 )(1) ( 1 )
*/
t[0][0] = 1.0;
t[1][1] = 1.0;
t[2][2] = 1.0;
t[3][3] = 1.0;
t[0][3] = v.x();
t[1][3] = v.y();
t[2][3] = v.z();
return t;
}
Matrix4x4 Matrix4x4::RotationX(double rx) {
Matrix4x4 Rx{};
Rx[0][0] = 1.0;
Rx[1][1] = cos(rx);
Rx[1][2] = -sin(rx);
Rx[2][1] = sin(rx);
Rx[2][2] = cos(rx);
Rx[3][3] = 1.0;
return Rx;
}
Matrix4x4 Matrix4x4::RotationY(double ry) {
Matrix4x4 Ry{};
Ry[1][1] = 1.0;
Ry[0][0] = cos(ry);
Ry[0][2] = sin(ry);
Ry[2][0] = -sin(ry);
Ry[2][2] = cos(ry);
Ry[3][3] = 1.0;
return Ry;
}
Matrix4x4 Matrix4x4::RotationZ(double rz) {
Matrix4x4 Rz{};
Rz[2][2] = 1.0;
Rz[0][0] = cos(rz);
Rz[0][1] = -sin(rz);
Rz[1][0] = sin(rz);
Rz[1][1] = cos(rz);
Rz[3][3] = 1.0;
return Rz;
}
Matrix4x4 Matrix4x4::Rotation(double rx, double ry, double rz) {
return RotationX(rx) * RotationY(ry) * RotationZ(rz);
}
Matrix4x4 Matrix4x4::Rotation(Point4D v, double rv) {
Matrix4x4 Rv{};
v.normalize();
Rv[0][0] = cos(rv) + (1.0 - cos(rv))*v.x()*v.x();
Rv[0][1] = (1.0 - cos(rv))*v.x()*v.y() - sin(rv)*v.z();
Rv[0][2] = (1.0 - cos(rv))*v.x()*v.z() + sin(rv)*v.y();
Rv[1][0] = (1.0 - cos(rv))*v.x()*v.y() + sin(rv)*v.z();
Rv[1][1] = cos(rv) + (1.0 - cos(rv))*v.y()*v.y();
Rv[1][2] = (1.0 - cos(rv))*v.y()*v.z() - sin(rv)*v.x();
Rv[2][0] = (1.0 - cos(rv))*v.z()*v.x() - sin(rv)*v.y();
Rv[2][1] = (1.0 - cos(rv))*v.z()*v.y() + sin(rv)*v.x();
Rv[2][2] = cos(rv) + (1.0 - cos(rv))*v.z()*v.z();
Rv[3][3] = 1.0;
return Rv;
}
Matrix4x4 Matrix4x4::Projection(double fov, double aspect, double ZNear, double ZFar) {
Matrix4x4 p{};
p[0][0] = 1.0/(tan(M_PI*fov*0.5/180.0)*aspect);
p[1][1] = 1.0/tan(M_PI*fov*0.5/180.0);
p[2][2] = ZFar/(ZFar - ZNear);
p[2][3] = -ZFar*ZNear/(ZFar - ZNear);
p[3][2] = 1.0;
return p;
}
Matrix4x4 Matrix4x4::ScreenSpace(int width, int height) {
Matrix4x4 s{};
s[0][0] = -0.5*width;
s[1][1] = -0.5*height;
s[2][2] = 1.0;
s[0][3] = 0.5*width;
s[1][3] = 0.5*height;
s[3][3] = 1.0;
return s;
}
Matrix4x4 Matrix4x4::View(const Point4D &left, const Point4D &up, const Point4D &lookAt, const Point4D &eye) {
Matrix4x4 V{};
V.setZero();
V[0][0] = left[0];
V[0][1] = left[1];
V[0][2] = left[2];
V[0][3] = -eye.dot(left);
V[1][0] = up[0];
V[1][1] = up[1];
V[1][2] = up[2];
V[1][3] = -eye.dot(up);
V[2][0] = lookAt[0];
V[2][1] = lookAt[1];
V[2][2] = lookAt[2];
V[2][3] = -eye.dot(lookAt);
V[3][3] = 1.0;
return V;
}
Matrix4x4 Matrix4x4::ViewInverse(const Point4D &left, const Point4D &up, const Point4D &lookAt, const Point4D &eye) {
Matrix4x4 inv{};
inv.setZero();
inv[0][0] = left[0];
inv[1][0] = left[1];
inv[2][0] = left[2];
inv[0][3] = eye[0];
inv[0][1] = up[0];
inv[1][1] = up[1];
inv[2][1] = up[2];
inv[1][3] = eye[1];
inv[0][2] = lookAt[0];
inv[1][2] = lookAt[1];
inv[2][2] = lookAt[2];
inv[2][3] = eye[2];
inv[3][3] = 1.0;
return inv;
}
Matrix4x4 Matrix4x4::Rotation(const Point4D &v) {
return RotationX(v.x())*RotationY(v.y())*RotationZ(v.z());
}
Matrix4x4 Matrix4x4::Scale(const Point4D &s) {
return Matrix4x4::Scale(s.x(), s.y(), s.z());
}

88
engine/utils/Matrix4x4.h Executable file
View File

@ -0,0 +1,88 @@
//
// Created by Иван Ильин on 12.01.2021.
//
#ifndef ENGINE_MATRIX4X4_H
#define ENGINE_MATRIX4X4_H
#include <array>
#include "Point4D.h"
class Matrix4x4 {
private:
std::array<std::array<double, 4>, 4> _arr_matrix{};
public:
Matrix4x4 () = default;
Matrix4x4& operator=(const Matrix4x4& matrix4X4) = default;
[[nodiscard]] const std::array<double, 4>& operator[] (int i) const;
[[nodiscard]] std::array<double, 4>& operator[] (int i);
[[nodiscard]] Matrix4x4 operator-() const;
[[nodiscard]] Matrix4x4 operator+() const;
// Boolean operations
bool operator==(const Matrix4x4& matrix4X4) const;
bool operator!=(const Matrix4x4& matrix4X4) const;
// Operations with Matrix4x4
[[nodiscard]] Matrix4x4 operator+(const Matrix4x4& matrix4X4) const;
[[nodiscard]] Matrix4x4 operator-(const Matrix4x4& matrix4X4) const;
[[nodiscard]] Matrix4x4 operator*(const Matrix4x4& matrix4X4) const;
[[nodiscard]] Matrix4x4 operator/(const Matrix4x4& matrix4X4) const;
Matrix4x4& operator+=(const Matrix4x4& matrix4X4);
Matrix4x4& operator-=(const Matrix4x4& matrix4X4);
Matrix4x4& operator*=(const Matrix4x4& matrix4X4);
Matrix4x4& operator/=(const Matrix4x4& matrix4X4);
// Operations with numbers
[[nodiscard]] Matrix4x4 operator+(double number) const;
[[nodiscard]] Matrix4x4 operator-(double number) const;
[[nodiscard]] Matrix4x4 operator*(double number) const;
[[nodiscard]] Matrix4x4 operator/(double number) const;
Matrix4x4& operator+=(double number);
Matrix4x4& operator-=(double number);
Matrix4x4& operator*=(double number);
Matrix4x4& operator/=(double number);
// Operations with Point4D
[[nodiscard]] Point4D operator*(const Point4D& point4D) const;
// Other useful methods
[[nodiscard]] double det3D();
[[nodiscard]] Matrix4x4 inv3D();
Matrix4x4& transpose();
Matrix4x4& setIdentity();
Matrix4x4& setOnes();
Matrix4x4& setZero();
Matrix4x4& setConstants(double value);
[[nodiscard]] const std::array<std::array<double, 4>, 4> & data() const;
[[nodiscard]] std::array<std::array<double, 4>, 4> & data();
// Any useful matrix
Matrix4x4 static Identity();
Matrix4x4 static Zero();
Matrix4x4 static Constant (double value);
Matrix4x4 static Scale(double sx, double sy, double sz);
Matrix4x4 static Scale(const Point4D& s);
Matrix4x4 static Translation(double dx, double dy, double dz);
Matrix4x4 static Translation(const Point4D& v);
Matrix4x4 static Rotation(const Point4D& r);
Matrix4x4 static RotationX (double rx);
Matrix4x4 static RotationY (double ry);
Matrix4x4 static RotationZ (double rz);
Matrix4x4 static Rotation (double rx, double ry, double rz);
Matrix4x4 static Rotation (Point4D v, double rv);
Matrix4x4 static View(const Point4D& left, const Point4D& up, const Point4D& lookAt, const Point4D& eye);
Matrix4x4 static ViewInverse(const Point4D& left, const Point4D& up, const Point4D& lookAt, const Point4D& eye);
Matrix4x4 static Projection (double fov = 90.0, double aspect = 1.0, double ZNear = 1.0, double ZFar = 10.0);
Matrix4x4 static ScreenSpace (int width, int height);
};
#endif //INC_3DZAVR_MATRIX4X4_H

175
engine/utils/Point4D.cpp Executable file
View File

@ -0,0 +1,175 @@
//
// Created by Иван Ильин on 12.01.2021.
//
#include "Point4D.h"
#include <cmath>
Point4D::Point4D (double x, double y, double z, double w) {
_arr_point[0] = x;
_arr_point[1] = y;
_arr_point[2] = z;
_arr_point[3] = w;
}
Point4D::Point4D(const Point4D &point4D) {
_arr_point[0] = point4D.x();
_arr_point[1] = point4D.y();
_arr_point[2] = point4D.z();
_arr_point[3] = point4D.w();
}
Point4D &Point4D::operator=(const Point4D &point4D) {
if (&point4D != this)
{
_arr_point[0] = point4D.x();
_arr_point[1] = point4D.y();
_arr_point[2] = point4D.z();
_arr_point[3] = point4D.w();
}
return *this;
}
[[nodiscard]] double Point4D::operator[] (int i) const {
return _arr_point[i];
}
[[nodiscard]] double& Point4D::operator[] (int i) {
return _arr_point[i];
}
[[nodiscard]] Point4D Point4D::operator-() const {
return Point4D(-x(), -y(), -z(), -w());
}
[[nodiscard]] Point4D Point4D::operator+() const {
return Point4D(*this);
}
// Boolean operations
bool Point4D::operator==(const Point4D& point4D) const
{
return this == &point4D || (*this - point4D).sqrAbs() < 0.0000000001;
}
bool Point4D::operator!=(const Point4D& point4D) const
{
return this != &point4D && (*this - point4D).sqrAbs() > 0.0000000001;
}
// Operations with Point4D
Point4D Point4D::operator+(const Point4D& point4D) const
{
return Point4D(*this) += point4D;
}
Point4D Point4D::operator-(const Point4D& point4D) const
{
return Point4D(*this) -= point4D;
}
Point4D Point4D::operator*(const Point4D& point4D) const
{
return Point4D(*this) *= point4D;
}
Point4D Point4D::operator/(const Point4D& point4D) const
{
return Point4D(*this) /= point4D;
}
Point4D& Point4D::operator+=(const Point4D& point4D)
{
_arr_point[0] += point4D.x();
_arr_point[1] += point4D.y();
_arr_point[2] += point4D.z();
_arr_point[3] += point4D.w();
return *this;
}
Point4D& Point4D::operator-=(const Point4D& point4D)
{
(*this) += -point4D;
return *this;
}
Point4D& Point4D::operator*=(const Point4D& point4D) {
_arr_point[0] *= point4D.x();
_arr_point[1] *= point4D.y();
_arr_point[2] *= point4D.z();
_arr_point[3] *= point4D.w();
return *this;
}
Point4D& Point4D::operator/=(const Point4D& point4D) {
_arr_point[0] /= point4D.x();
_arr_point[1] /= point4D.y();
_arr_point[2] /= point4D.z();
_arr_point[3] /= point4D.w();
return *this;
}
double Point4D::dot(const Point4D& point4D) const
{
return point4D.x() * x() + point4D.y() * y() + point4D.z() * z() + point4D.w() * w();
}
[[nodiscard]] Point4D Point4D::cross3D(const Point4D& point4D) const {
Point4D cross{y() * point4D.z() - point4D.y() * z(),
z() * point4D.x() - point4D.z() * x(),
x() * point4D.y() - point4D.x() * y()};
return cross;
}
// Operations with numbers
Point4D Point4D::operator+(double number) const {
return Point4D(*this) += number;
}
Point4D Point4D::operator-(double number) const {
return Point4D(*this) -= number;
}
Point4D Point4D::operator*(double number) const
{
return Point4D(*this) *= number;
}
Point4D Point4D::operator/(double number) const
{
return Point4D(*this) /= number;
}
Point4D& Point4D::operator+=(double number) {
_arr_point[0] += number;
_arr_point[1] += number;
_arr_point[2] += number;
_arr_point[3] += number;
return *this;
}
Point4D& Point4D::operator-=(double number) {
return *this += -number;
}
Point4D& Point4D::operator*=(double number)
{
_arr_point[0] *= number;
_arr_point[1] *= number;
_arr_point[2] *= number;
_arr_point[3] *= number;
return *this;
}
Point4D& Point4D::operator/=(double number)
{
return *this *= 1.0/number;
}
// Other useful methods
double Point4D::sqrAbs() const
{
return x()*x() + y()*y() + z()*z() + w()*w();
}
double Point4D::abs() const
{
return sqrt(sqrAbs());
}
Point4D& Point4D::normalize()
{
double length = this->abs();
_arr_point[0] /= length;
_arr_point[1] /= length;
_arr_point[2] /= length;
_arr_point[3] /= length;
return *this;
}
Point4D Point4D::normalized() const {
Point4D res(*this);
return res.normalize();
}

72
engine/utils/Point4D.h Executable file
View File

@ -0,0 +1,72 @@
//
// Created by Иван Ильин on 12.01.2021.
//
#ifndef ENGINE_POINT4D_H
#define ENGINE_POINT4D_H
#include <array>
class Point4D {
private:
std::array<double, 4> _arr_point{};
public:
Point4D () = default;
Point4D (const Point4D& point4D);
explicit Point4D (double x, double y = 0.0, double z = 0.0, double w = 0.0);
Point4D& operator=(const Point4D& point4D);
[[nodiscard]] double x() const{ return _arr_point[0]; }
[[nodiscard]] double y() const{ return _arr_point[1]; }
[[nodiscard]] double z() const{ return _arr_point[2]; }
[[nodiscard]] double w() const{ return _arr_point[3]; }
[[nodiscard]] double operator[] (int i) const;
[[nodiscard]] double& operator[] (int i);
[[nodiscard]] Point4D operator-() const;
[[nodiscard]] Point4D operator+() const;
// Boolean operations
bool operator==(const Point4D& point4D) const;
bool operator!=(const Point4D& point4D) const;
// Operations with Point4D
[[nodiscard]] Point4D operator+(const Point4D& point4D) const;
[[nodiscard]] Point4D operator-(const Point4D& point4D) const;
[[nodiscard]] Point4D operator*(const Point4D& point4D) const;
[[nodiscard]] Point4D operator/(const Point4D& point4D) const;
Point4D& operator+=(const Point4D& point4D);
Point4D& operator-=(const Point4D& point4D);
Point4D& operator*=(const Point4D& point4D);
Point4D& operator/=(const Point4D& point4D);
[[nodiscard]] double dot(const Point4D& point4D) const; // Returns dot product
[[nodiscard]] Point4D cross3D(const Point4D& point4D) const; // Returns cross product
// Operations with numbers
[[nodiscard]] Point4D operator+(double number) const;
[[nodiscard]] Point4D operator-(double number) const;
[[nodiscard]] Point4D operator*(double number) const;
[[nodiscard]] Point4D operator/(double number) const;
Point4D& operator+=(double number);
Point4D& operator-=(double number);
Point4D& operator*=(double number);
Point4D& operator/=(double number);
// Other useful methods
[[nodiscard]] double sqrAbs() const; // Returns squared vector length
[[nodiscard]] double abs() const; // Returns vector length
Point4D& normalize(); // Normalize vector and returns
[[nodiscard]] Point4D normalized() const; // Returns normalized vector without changing
static Point4D unit_x() {return Point4D{1, 0, 0, 0}; }
static Point4D unit_y() {return Point4D{0, 1, 0, 0}; }
static Point4D unit_z() {return Point4D{0, 0, 1, 0}; }
};
#endif //INC_3DZAVR_POINT4D_H

78
engine/utils/Time.cpp Executable file
View File

@ -0,0 +1,78 @@
//
// Created by Иван Ильин on 11.01.2021.
//
#include "Time.h"
#include <chrono>
#define BIG_STEP (1.0/15.0)
using namespace std::chrono;
namespace Time
{
namespace
{
// High precision time
high_resolution_clock::time_point _start = high_resolution_clock::now();
high_resolution_clock::time_point _last;
// FPS counter
high_resolution_clock::time_point _fpsStart;
milliseconds _fpsCountTime = milliseconds(1000);
int _fpsCounter = 0;
double _lastFps = 0;
// Compatibility
double _time;
double _deltaTime;
double _realDeltaTime;
}
double time()
{
return _time;
}
double deltaTime()
{
return _deltaTime;
}
double realDeltaTime()
{
return _realDeltaTime;
}
void update()
{
high_resolution_clock::time_point t = high_resolution_clock::now();
_deltaTime = duration<double>(t - _last).count();
_time = duration<double>(t - _start).count();
// in case when fps < 10 it is useful to decrease _deltaTime (to avoid collision problems)
if(_deltaTime > BIG_STEP)
_deltaTime = BIG_STEP;
_realDeltaTime = duration<double>(t - _last).count();
_last = t;
if(_deltaTime > 10000)
return;
_fpsCounter++;
if (t - _fpsStart > _fpsCountTime)
{
_lastFps = _fpsCounter / duration<double>(t - _fpsStart).count();
_fpsCounter = 0;
_fpsStart = t;
}
}
int fps()
{
// Cast is faster than floor and has the same behavior for positive numbers
return static_cast<int>(_lastFps);
}
}

18
engine/utils/Time.h Executable file
View File

@ -0,0 +1,18 @@
//
// Created by Иван Ильин on 11.01.2021.
//
#ifndef ENGINE_TIME_H
#define ENGINE_TIME_H
namespace Time
{
int fps();
double time();
double deltaTime();
double realDeltaTime();
void update();
}
#endif //INC_3DZAVR_TIME_H

BIN
img/gamePlay2.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 466 KiB

BIN
img/gamePlay3.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 543 KiB

BIN
img/gamePlay4.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 430 KiB

BIN
img/gamePlay5.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 465 KiB

BIN
img/gamePlay6.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

BIN
img/gamePlay7.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 419 KiB

200
main.cpp Executable file
View File

@ -0,0 +1,200 @@
//
// Created by Иван Ильин on 06.02.2021.
//
#include "Engine.h"
#include <iostream>
#include "Player.h"
#include "ResourceManager.h"
#include "gui/Window.h"
#include "Client.h"
#include "Server.h"
#include <fstream>
using namespace std;
// Read server/client settings and start both.
// If client doesn't connect to the localhost - server doesn't start.
void InitNetwork(const shared_ptr<Server>& server, const shared_ptr<Client>& client)
{
std::string clientIp;
sf::Uint16 clientPort;
sf::Uint16 serverPort;
std::ifstream connectfile("connect.txt", std::ifstream::in);
// If failed to read client settings
if (!connectfile.is_open() || !(connectfile >> clientIp >> clientPort) || sf::IpAddress(clientIp) == sf::IpAddress::None)
{
connectfile.close();
// Create file and write default settings
clientIp = "127.0.0.1";
clientPort = 54000;
std::ofstream temp("connect.txt", std::ofstream::out);
temp << clientIp << std::endl << clientPort;
temp.close();
}
connectfile.close();
// If failed to read server settings
connectfile.open("server.txt", std::ifstream::in);
if (!connectfile.is_open() || !(connectfile >> serverPort))
{
connectfile.close();
// Create file and write default settings
serverPort = 54000;
std::ofstream temp("server.txt", std::ofstream::out);
temp << serverPort;
temp.close();
}
connectfile.close();
if (clientIp == sf::IpAddress::LocalHost) {
server->start(serverPort);
if(server->isWorking())
server->generateBonuses();
}
client->connect(clientIp, clientPort);
}
class Shooter : public Engine {
private:
shared_ptr<Player> player;
sf::Sound backgroundNoise;
Window mainMenu;
shared_ptr<Server> server;
shared_ptr<Client> client;
bool inGame = false;
public:
Shooter() = default;
void start() override;
void update(double elapsedTime) override;
void gui() override;
void play();
};
void Shooter::start() {
// This code executed once in the beginning:
debugText(false);
screen->setMouseCursorVisible(true);
world->loadMap("../maps/map1.obj", "map", Point4D{5, 5, 5});
player = std::make_shared<Player>();
client = std::make_shared<Client>(player, world);
server = std::make_shared<Server>();
player->setAddTraceCallBack([this](const Point4D& from, const Point4D& to){ client->addTrace(from, to); });
player->attachCamera(camera, screen);
player->attachWorld(world);
setUpdateWorld(false);
world->addMesh(player, player->name());
player->setDamagePlayerCallBack([this] (sf::Uint16 targetId, double damage) { client->damagePlayer(targetId, damage); });
player->setTakeBonusCallBack([this] (const string& bonusName) { client->takeBonus(bonusName); });
// windows init:
mainMenu.title("Main menu");
mainMenu.setBackgroundTexture("../textures/back.png", 1.1, 1.1, screen->width(), screen->height());
mainMenu.addButton(screen->width()/2, 200, 200, 20, [this] () { this->play(); }, "Play", 5, 5, "../textures/gui.png", {0, 66}, {0, 86}, {0, 46}, "../engine/fonts/Roboto-Medium.ttf", {255, 255, 255}, "../sound/click.ogg");
mainMenu.addButton(screen->width()/2, 350, 200, 20, [this] () { this->player->translateToPoint(Point4D{0, 0, 0}); this->player->setVelocity({}); this->play(); }, "Respawn", 5, 5, "../textures/gui.png", {0, 66}, {0, 86}, {0, 46}, "../engine/fonts/Roboto-Medium.ttf", {255, 255, 255}, "../sound/click.ogg");
mainMenu.addButton(screen->width()/2, 500, 200, 20, [this] () { client->disconnect(); server->stop(); this->exit();}, "Exit", 5, 5, "../textures/gui.png", {0, 66}, {0, 86}, {0, 46}, "../engine/fonts/Roboto-Medium.ttf", {255, 255, 255}, "../sound/click.ogg");
// connecting to the server
InitNetwork(server, client);
// Waiting for connect and updating server if it's same window
while (client->isWorking() && !client->connected())
{
client->update();
server->update();
Time::update();
}
// If connect fail - return to menu
if (!client->isWorking())
{
inGame = false;
server->stop();
}
}
void Shooter::update(double elapsedTime) {
// This code executed every time step:
server->update();
client->update();
// Check all input after this condition please
if (!screen->window.hasFocus())
return;
if(screen->isKeyTapped(sf::Keyboard::Escape)) {
inGame = !inGame;
screen->setMouseCursorVisible(!inGame);
}
if(inGame) {
screen->title("Shooter");
player->update();
} else {
mainMenu.update(screen);
}
setUpdateWorld(inGame);
// background sounds and music control
if(backgroundNoise.getStatus() != sf::Sound::Status::Playing) {
backgroundNoise.setBuffer(*ResourceManager::loadSoundBuffer("../sound/backNoise.ogg"));
backgroundNoise.play();
}
}
void Shooter::gui() {
if(inGame) {
// aim
sf::Sprite sprite;
sprite.setTexture(*ResourceManager::loadTexture("../textures/gui.png"));
sprite.setTextureRect(sf::IntRect(243, 3, 9, 9));
sprite.scale(3, 3);
sprite.setPosition(screen->width() / 2.0 - 27.0/2.0, screen->height() / 2 - 27.0/2.0);
sprite.setColor(sf::Color(0,0,0, 200));
screen->window.draw(sprite);
// health player stats
player->drawStats();
}
}
void Shooter::play() {
inGame = true;
screen->setMouseCursorVisible(!inGame);
}
int main(int argc, char* argv[]) {
Shooter game;
//game.create(1280, 720, "Shooter");
//game.create(1920, 1080, "Shooter", true, {255, 255, 255}, sf::Style::Fullscreen);
game.create(2048, 1152, "Shooter");
//game.create(3072, 1920, "Shooter", true, {255, 255, 255}, sf::Style::Fullscreen);
return 0;
}

3393
maps/map1.obj Executable file

File diff suppressed because it is too large Load Diff

38
maps/materials.txt Executable file
View File

@ -0,0 +1,38 @@
000 196 173 255 255
001 255 255 255 100
002 179 116 122 255
003 198 73 231 255
004 86 81 125 255
005 126 179 231 255
006 231 180 162 255
007 105 148 231 255
008 231 160 111 255
009 231 161 90 255
010 147 231 139 255
011 255 199 179 100
012 231 88 85 100
013 231 66 78 255
014 198 73 231 255
016 117 231 150 100
017 103 79 100 100
018 198 73 231 255
019 103 79 231 100
020 231 30 217 255
021 117 231 150 100
022 231 66 78 255
023 231 30 217 255
024 231 88 85 100
025 231 161 90 255
026 85 231 139 255
027 255 199 179 100
028 126 179 231 255
029 198 73 231 255
030 105 148 231 255
031 231 180 162 255
032 85 231 139 255
033 231 160 111 255
034 144 103 84 255
035 179 116 122 255
036 196 173 255 255
037 86 81 125 255
038 147 231 139 255

51
obj/ability.obj Executable file
View File

@ -0,0 +1,51 @@
# Blender v2.91.0 OBJ File: 'ability.blend'
# www.blender.org
mtllib ability.mtl
o Cube
v 0.005449 0.446474 -0.361719
v 0.005449 0.446546 -0.372617
v 0.004566 -0.366134 0.374185
v 0.004566 -0.366294 0.365054
v -0.005449 0.446474 -0.361719
v -0.005449 0.446546 -0.372617
v -0.004566 -0.366134 0.374185
v -0.004566 -0.366294 0.365054
v -0.112195 -0.019100 0.085016
v 0.112195 0.200628 0.130515
v -0.112195 0.200628 0.130515
v 0.112195 -0.019100 0.085016
v -0.112195 0.031076 -0.073104
v 0.112195 -0.186195 -0.129173
v -0.112195 -0.186195 -0.129173
v 0.112195 0.031076 -0.073104
g Cube_Cube_Material.000
usemtl Material.000
s off
f 11 3 10
f 3 8 4
f 13 6 15
f 12 8 9
f 12 3 4
f 5 2 6
f 16 12 14
f 14 9 15
f 7 9 8
f 13 10 16
f 5 16 1
f 2 15 6
f 2 16 14
f 9 13 15
f 11 7 3
f 3 7 8
f 13 5 6
f 12 4 8
f 12 10 3
f 5 1 2
f 16 10 12
f 14 12 9
f 7 11 9
f 13 11 10
f 5 13 16
f 2 14 15
f 2 1 16
f 9 11 13

1
obj/ability_mat.txt Executable file
View File

@ -0,0 +1 @@
000 51 19 198 150

Some files were not shown because too many files have changed in this diff Show More