Initial commit
commit
7e5c96c123
|
@ -0,0 +1,3 @@
|
|||
# Project exclude paths
|
||||
/cmake-build-debug/
|
||||
/cmake-build-release/
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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)
|
|
@ -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());
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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)
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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() + "'.");
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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.");
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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++;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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));
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,2 @@
|
|||
127.0.0.1
|
||||
54000
|
|
@ -0,0 +1 @@
|
|||
54000
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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());
|
||||
}
|
|
@ -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
|
|
@ -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();
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
Binary file not shown.
After Width: | Height: | Size: 466 KiB |
Binary file not shown.
After Width: | Height: | Size: 543 KiB |
Binary file not shown.
After Width: | Height: | Size: 430 KiB |
Binary file not shown.
After Width: | Height: | Size: 465 KiB |
Binary file not shown.
After Width: | Height: | Size: 139 KiB |
Binary file not shown.
After Width: | Height: | Size: 419 KiB |
|
@ -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;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
000 51 19 198 150
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue