shooter/engine/World.cpp

165 lines
5.8 KiB
C++
Raw Normal View History

2021-09-13 15:53:43 +03:00
//
// Created by Иван Ильин on 13.01.2021.
//
2021-10-31 11:39:08 +03:00
#include <sstream>
#include <cmath>
2021-09-13 15:53:43 +03:00
#include "World.h"
#include "utils/Log.h"
2022-07-11 16:58:05 +03:00
#include "math/Plane.h"
#include "utils/ResourceManager.h"
2021-09-13 15:53:43 +03:00
using namespace std;
2022-02-24 07:11:04 +03:00
std::shared_ptr<RigidBody> World::addBody(std::shared_ptr<RigidBody> body) {
2021-10-28 16:58:02 +03:00
_objects.emplace(body->name(), body);
2021-10-31 11:39:08 +03:00
Log::log("World::addBody(): inserted body '" + body->name().str() + "' with " +
std::to_string(_objects[body->name()]->triangles().size()) + " tris.");
2022-02-24 07:11:04 +03:00
return _objects[body->name()];
2021-09-13 15:53:43 +03:00
}
2022-02-24 07:11:04 +03:00
std::shared_ptr<RigidBody> World::loadBody(const ObjectNameTag &tag, const string &filename, const Vec3D &scale) {
2021-11-09 22:54:20 +03:00
_objects.emplace(tag, std::make_shared<RigidBody>(tag, filename, scale));
2021-10-31 11:39:08 +03:00
Log::log("World::loadBody(): inserted body from " + filename + " with title '" + tag.str() + "' with " +
std::to_string(_objects[tag]->triangles().size()) + " tris.");
2022-02-24 07:11:04 +03:00
return _objects[tag];
2021-09-13 15:53:43 +03:00
}
2021-10-31 11:39:08 +03:00
IntersectionInformation World::rayCast(const Vec3D &from, const Vec3D &to, const std::string &skipTags) {
2021-09-13 15:53:43 +03:00
2021-10-22 19:42:32 +03:00
// make vector of tags, that we are going to escape
2021-10-31 11:39:08 +03:00
vector<std::string> tagsToSkip;
2021-10-22 19:42:32 +03:00
stringstream s(skipTags);
std::string t;
2021-10-29 18:41:07 +03:00
while (s >> t) {
tagsToSkip.push_back(t);
}
2021-10-22 19:42:32 +03:00
bool intersected = false;
2021-10-28 16:58:02 +03:00
Vec3D point{};
Triangle triangle;
2021-10-17 10:21:10 +03:00
std::string bodyName;
double minDistance = Consts::RAY_CAST_MAX_DISTANCE;
2021-10-22 19:42:32 +03:00
std::shared_ptr<RigidBody> intersectedBody = nullptr;
2021-09-13 15:53:43 +03:00
2021-10-31 11:39:08 +03:00
for (auto&[name, body] : _objects) {
2021-10-22 19:42:32 +03:00
2021-10-29 18:41:07 +03:00
bool escapeThisBody = false;
2021-10-31 11:39:08 +03:00
for (auto &escapeTag : tagsToSkip) {
2021-11-09 22:54:20 +03:00
if (name.contains(ObjectNameTag(escapeTag))) {
2021-10-29 18:41:07 +03:00
escapeThisBody = true;
2021-10-29 23:44:37 +03:00
break;
2021-10-29 18:41:07 +03:00
}
}
2021-10-31 11:39:08 +03:00
if (escapeThisBody) {
2021-09-13 15:53:43 +03:00
continue;
2021-10-28 16:58:02 +03:00
}
2021-09-13 15:53:43 +03:00
2022-07-07 13:14:00 +03:00
Matrix4x4 model = body->model();
2022-07-11 16:58:05 +03:00
// It is computationally more efficient not to transform all object's triangles from model to global
// coordinate system, but translate 'from' and 'to' vectors inside once and check triangles without performing
// many matrix multiplication.
2022-07-07 13:14:00 +03:00
Matrix4x4 invModel = body->invModel();
Vec3D v = (to - from).normalized();
Vec3D v_model = invModel*v;
Vec3D from_model = invModel*(from - body->position());
Vec3D to_model = invModel*(to - body->position());
2022-07-11 16:58:05 +03:00
2021-10-31 11:39:08 +03:00
for (auto &tri : body->triangles()) {
2021-09-13 15:53:43 +03:00
2022-07-07 13:14:00 +03:00
if(tri.norm().dot(v_model) > 0) {
continue;
}
2022-07-11 16:58:05 +03:00
auto intersection = Plane(tri).intersection(from_model, to_model);
if (intersection.second > 0 && tri.isPointInside(intersection.first)) {
// When you change to model coordinate system you also will get distance scaled by invModel.
// Due-to this effect if you scale some object in x times you will get distance in x times smaller.
// That's why we need to perform distance calculation in the global coordinate system where metric
// is the same for all objects.
Triangle globalTriangle(model * tri[0], model * tri[1], model * tri[2], tri.color());
auto globalIntersection = Plane(globalTriangle).intersection(from, to);
double globalDistance = (globalIntersection.first - from).abs();
if(globalDistance < minDistance) {
minDistance = globalDistance;
point = globalIntersection.first;
triangle = globalTriangle;
bodyName = name.str();
intersected = true;
intersectedBody = body;
//Triangle triangleRED = Triangle(model * tri[0], model * tri[1], model * tri[2], sf::Color(255, 0, 0));
//addBody(std::make_shared<RigidBody>(Mesh(ObjectNameTag("Test" + std::to_string(rand())), std::vector<Triangle>({triangleRED}))));
}
2021-09-13 15:53:43 +03:00
}
}
}
2022-07-07 13:14:00 +03:00
return IntersectionInformation{point, sqrt(minDistance), triangle, ObjectNameTag(bodyName), intersectedBody, intersected};
2021-09-13 15:53:43 +03:00
}
2021-10-31 11:39:08 +03:00
void World::loadMap(const std::string &filename, const Vec3D &scale) {
auto objs = ResourceManager::loadObjects(filename);
2021-10-31 11:39:08 +03:00
for (auto &i : objs) {
2021-11-09 22:54:20 +03:00
std::shared_ptr<RigidBody> obj = std::make_shared<RigidBody>(*i, false);
2021-10-28 16:58:02 +03:00
addBody(obj);
obj->scale(scale);
2021-09-13 15:53:43 +03:00
}
}
2021-10-31 11:39:08 +03:00
void World::removeBody(const ObjectNameTag &tag) {
if (_objects.erase(tag) > 0) {
2021-10-17 10:21:10 +03:00
Log::log("World::removeBody(): removed body '" + tag.str() + "'");
2021-10-28 16:58:02 +03:00
} else {
2021-10-17 10:21:10 +03:00
Log::log("World::removeBody(): cannot remove body '" + tag.str() + "': body does not exist.");
2021-10-28 16:58:02 +03:00
}
}
2021-10-31 11:39:08 +03:00
void World::checkCollision(const ObjectNameTag &tag) {
if (_objects[tag]->hasCollision()) {
2021-10-17 10:21:10 +03:00
_objects[tag]->setInCollision(false);
2021-10-09 14:38:24 +03:00
for (auto it = _objects.begin(); it != _objects.end();) {
auto obj = it->second;
2021-10-17 10:21:10 +03:00
ObjectNameTag name = it->first;
2021-10-09 14:38:24 +03:00
it++;
if ((name == tag) || !(obj->isCollider() || obj->isTrigger())) {
continue;
}
std::pair<bool, Simplex> gjk = _objects[tag]->checkGJKCollision(obj);
if (gjk.first) {
if (obj->isCollider()) {
CollisionPoint epa = _objects[tag]->EPA(gjk.second, obj);
_objects[tag]->solveCollision(epa);
}
if (_objects[tag]->collisionCallBack() != nullptr) {
_objects[tag]->collisionCallBack()(name, obj);
}
}
}
}
}
void World::update() {
2021-11-09 22:54:20 +03:00
for (auto &[nameTag, obj] : _objects) {
obj->updatePhysicsState();
checkCollision(nameTag);
}
}
2021-10-31 11:39:08 +03:00
std::shared_ptr<RigidBody> World::body(const ObjectNameTag &tag) {
if (_objects.count(tag) == 0) {
return nullptr;
2021-10-28 16:58:02 +03:00
}
2021-10-17 10:21:10 +03:00
return _objects.find(tag)->second;
2021-09-13 15:53:43 +03:00
}