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