mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-08-04 09:44:10 +00:00
Public release of the DLU server code!
Have fun!
This commit is contained in:
19
dPhysics/Singleton.h
Normal file
19
dPhysics/Singleton.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
template <typename T>
|
||||
class Singleton {
|
||||
public:
|
||||
static T& Instance() {
|
||||
static T instance{};
|
||||
return instance;
|
||||
}
|
||||
|
||||
virtual ~Singleton() = default;
|
||||
Singleton(const Singleton& other) = delete;
|
||||
Singleton(Singleton&& other) = delete;
|
||||
Singleton& operator=(const Singleton& other) = delete;
|
||||
Singleton& operator=(Singleton&& other) = delete;
|
||||
|
||||
protected:
|
||||
Singleton() = default;
|
||||
};
|
117
dPhysics/dpCollisionChecks.cpp
Normal file
117
dPhysics/dpCollisionChecks.cpp
Normal file
@@ -0,0 +1,117 @@
|
||||
#include "dpCollisionChecks.h"
|
||||
#include "dpEntity.h"
|
||||
#include "dpShapeBase.h"
|
||||
#include "dpShapeSphere.h"
|
||||
#include "dpShapeBox.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
using namespace dpCollisionChecks;
|
||||
|
||||
bool dpCollisionChecks::AreColliding(dpEntity* a, dpEntity* b) {
|
||||
auto shapeA = a->GetShape();
|
||||
auto shapeB = b->GetShape();
|
||||
|
||||
//Sphere to sphere collision
|
||||
if (shapeA->GetShapeType() == dpShapeType::Sphere && shapeB->GetShapeType() == dpShapeType::Sphere) {
|
||||
return CheckSpheres(a, b);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool dpCollisionChecks::CheckSpheres(dpEntity* a, dpEntity* b) {
|
||||
if (!a || !b) return false;
|
||||
|
||||
auto posA = a->GetPosition();
|
||||
auto distance = Vector3::DistanceSquared(posA, b->GetPosition());
|
||||
|
||||
auto sphereA = static_cast<dpShapeSphere*>(a->GetShape());
|
||||
auto sphereB = static_cast<dpShapeSphere*>(b->GetShape());
|
||||
const auto radius = sphereA->GetRadius() + sphereB->GetRadius();
|
||||
|
||||
if (distance <= radius * radius)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool dpCollisionChecks::CheckBoxes(dpEntity* a, dpEntity* b) {
|
||||
if (!a || !b) return false;
|
||||
|
||||
auto boxA = static_cast<dpShapeBox*>(a->GetShape());
|
||||
auto boxB = static_cast<dpShapeBox*>(b->GetShape());
|
||||
|
||||
const auto& posA = a->GetPosition();
|
||||
const auto& posB = b->GetPosition();
|
||||
|
||||
for (const auto& vert : boxA->GetVertices()) {
|
||||
if (boxB->IsVertInBox(vert))
|
||||
return true;
|
||||
}
|
||||
|
||||
/*//Check if we're overlapping on X/Z:
|
||||
if ((boxA->GetMaxWidth() >= boxB->GetMinWidth()) && //If our max width is greater than starting X of b
|
||||
(boxA->GetMinWidth() <= boxB->GetMaxWidth()) && //If our start x is less than b's max width
|
||||
(boxA->GetMaxDepth() >= boxB->GetMinDepth()) &&
|
||||
(boxA->GetMinDepth() <= boxB->GetMaxDepth())) {
|
||||
|
||||
//Check if we're in the right height
|
||||
if (boxA->GetTop() <= boxB->GetTop() && boxA->GetTop() >= boxB->GetBottom() || //If our top Y is within their minY/maxY bounds
|
||||
boxA->GetBottom() <= boxB->GetTop() && boxA->GetBottom() >= boxB->GetBottom()) //Or our bottom Y
|
||||
return true; //We definitely are colliding.
|
||||
}*/
|
||||
|
||||
/*//Check if we're overlapping on X/Z:
|
||||
if ((boxA->GetMaxWidth() >= posB.x) && //If our max width is greater than starting X of b
|
||||
(posA.x <= boxB->GetMaxWidth()) && //If our start x is less than b's max width
|
||||
(boxA->GetMaxDepth() >= posB.z) &&
|
||||
(posA.z <= boxB->GetMaxDepth())) {
|
||||
|
||||
//Check if we're in the right height
|
||||
if (boxA->GetTop() <= boxB->GetTop() && boxA->GetTop() >= posB.y || //If our top Y is within their minY/maxY bounds
|
||||
posA.y <= boxB->GetTop() && posA.y >= posB.y) //Or our bottom Y
|
||||
return true; //We definitely are colliding.
|
||||
}*/
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool dpCollisionChecks::CheckSphereBox(dpEntity* a, dpEntity* b) {
|
||||
if (!a || !b) return false;
|
||||
|
||||
NiPoint3 boxPos;
|
||||
dpShapeBox* box;
|
||||
|
||||
NiPoint3 spherePos;
|
||||
dpShapeSphere* sphere;
|
||||
|
||||
//Figure out which is the box and which is the sphere
|
||||
if (a->GetShape()->GetShapeType() == dpShapeType::Box) {
|
||||
box = static_cast<dpShapeBox*>(a->GetShape());
|
||||
sphere = static_cast<dpShapeSphere*>(b->GetShape());
|
||||
boxPos = a->GetPosition();
|
||||
spherePos = b->GetPosition();
|
||||
}
|
||||
else {
|
||||
box = static_cast<dpShapeBox*>(b->GetShape());
|
||||
sphere = static_cast<dpShapeSphere*>(a->GetShape());
|
||||
boxPos = b->GetPosition();
|
||||
spherePos = a->GetPosition();
|
||||
}
|
||||
|
||||
//Get closest point from the box to the sphere center by clamping
|
||||
float x = std::max(box->m_MinX, std::min(spherePos.x, box->m_MaxX));
|
||||
float y = std::max(box->m_MinY, std::min(spherePos.y, box->m_MaxY));
|
||||
float z = std::max(box->m_MinZ, std::min(spherePos.z, box->m_MaxZ));
|
||||
|
||||
//Check the distance between that point & our sphere
|
||||
float dX = x - spherePos.x;
|
||||
float dY = y - spherePos.y;
|
||||
float dZ = z - spherePos.z;
|
||||
float distanceSquared = (dX * dX) + (dY * dY) + (dZ * dZ);
|
||||
const float radius = sphere->GetRadius();
|
||||
|
||||
return distanceSquared < radius * radius;
|
||||
}
|
12
dPhysics/dpCollisionChecks.h
Normal file
12
dPhysics/dpCollisionChecks.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
class dpEntity;
|
||||
|
||||
namespace dpCollisionChecks {
|
||||
bool AreColliding(dpEntity* a, dpEntity* b);
|
||||
|
||||
bool CheckSpheres(dpEntity* a, dpEntity* b);
|
||||
|
||||
bool CheckBoxes(dpEntity* a, dpEntity* b);
|
||||
|
||||
bool CheckSphereBox(dpEntity* a, dpEntity* b);
|
||||
};
|
14
dPhysics/dpCollisionGroups.h
Normal file
14
dPhysics/dpCollisionGroups.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* Collision Groups
|
||||
*/
|
||||
|
||||
enum eCollisionGroup : uint8_t
|
||||
{
|
||||
COLLISION_GROUP_ALL = 0 << 0,
|
||||
COLLISION_GROUP_NEUTRAL = 1 << 0,
|
||||
COLLISION_GROUP_FRIENDLY = 1 << 1,
|
||||
COLLISION_GROUP_ENEMY = 1 << 2,
|
||||
COLLISION_GROUP_DYNAMIC = 1 << 3,
|
||||
};
|
7
dPhysics/dpCommon.h
Normal file
7
dPhysics/dpCommon.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
enum class dpShapeType : unsigned short {
|
||||
Invalid = 0,
|
||||
Sphere,
|
||||
Box
|
||||
};
|
151
dPhysics/dpEntity.cpp
Normal file
151
dPhysics/dpEntity.cpp
Normal file
@@ -0,0 +1,151 @@
|
||||
#include "dpEntity.h"
|
||||
#include "dpShapeSphere.h"
|
||||
#include "dpShapeBox.h"
|
||||
#include "dpGrid.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
dpEntity::dpEntity(const LWOOBJID& objectID, dpShapeType shapeType, bool isStatic) {
|
||||
m_ObjectID = objectID;
|
||||
m_IsStatic = isStatic;
|
||||
m_CollisionShape = nullptr;
|
||||
m_Scale = 1.0f;
|
||||
m_CollisionGroup = COLLISION_GROUP_ALL;
|
||||
|
||||
switch (shapeType) {
|
||||
case dpShapeType::Sphere:
|
||||
m_CollisionShape = new dpShapeSphere(this, 1.0f);
|
||||
break;
|
||||
|
||||
case dpShapeType::Box:
|
||||
m_CollisionShape = new dpShapeBox(this, 1.0f, 1.0f, 1.0f);
|
||||
break;
|
||||
|
||||
default:
|
||||
std::cout << "No shape for shapeType: " << (int)shapeType << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
dpEntity::dpEntity(const LWOOBJID& objectID, NiPoint3 boxDimensions, bool isStatic) {
|
||||
m_ObjectID = objectID;
|
||||
m_IsStatic = isStatic;
|
||||
m_CollisionShape = nullptr;
|
||||
m_Scale = 1.0f;
|
||||
m_CollisionGroup = COLLISION_GROUP_ALL;
|
||||
|
||||
m_CollisionShape = new dpShapeBox(this, boxDimensions.x, boxDimensions.y, boxDimensions.z);
|
||||
if (boxDimensions.x > 100.0f) m_IsGargantuan = true;
|
||||
}
|
||||
|
||||
dpEntity::dpEntity(const LWOOBJID& objectID, float width, float height, float depth, bool isStatic) {
|
||||
m_ObjectID = objectID;
|
||||
m_IsStatic = isStatic;
|
||||
m_CollisionShape = nullptr;
|
||||
m_Scale = 1.0f;
|
||||
m_CollisionGroup = COLLISION_GROUP_ALL;
|
||||
|
||||
m_CollisionShape = new dpShapeBox(this, width, height, depth);
|
||||
if (width > 100.0f) m_IsGargantuan = true;
|
||||
}
|
||||
|
||||
dpEntity::dpEntity(const LWOOBJID& objectID, float radius, bool isStatic) {
|
||||
m_ObjectID = objectID;
|
||||
m_IsStatic = isStatic;
|
||||
m_CollisionShape = nullptr;
|
||||
m_Scale = 1.0f;
|
||||
m_CollisionGroup = COLLISION_GROUP_ALL;
|
||||
|
||||
m_CollisionShape = new dpShapeSphere(this, radius);
|
||||
if (radius > 200.0f) m_IsGargantuan = true;
|
||||
}
|
||||
|
||||
dpEntity::~dpEntity() {
|
||||
delete m_CollisionShape;
|
||||
m_CollisionShape = nullptr;
|
||||
}
|
||||
|
||||
void dpEntity::Update(float deltaTime) {
|
||||
m_NewObjects.clear();
|
||||
m_RemovedObjects.clear();
|
||||
|
||||
if (m_IsStatic) return;
|
||||
//m_Position = m_Position + (m_Velocity * deltaTime);
|
||||
}
|
||||
|
||||
void dpEntity::CheckCollision(dpEntity* other) {
|
||||
if (!m_CollisionShape) return;
|
||||
|
||||
if ((m_CollisionGroup & other->m_CollisionGroup) & (~COLLISION_GROUP_DYNAMIC)) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool wasFound = (m_CurrentlyCollidingObjects.find(other->GetObjectID()) != m_CurrentlyCollidingObjects.end());
|
||||
|
||||
bool isColliding = m_CollisionShape->IsColliding(other->GetShape());
|
||||
|
||||
if (isColliding && !wasFound) {
|
||||
m_CurrentlyCollidingObjects.emplace(other->GetObjectID(), other);
|
||||
m_NewObjects.push_back(other);
|
||||
|
||||
//if (m_CollisionShape->GetShapeType() == dpShapeType::Sphere && other->GetShape()->GetShapeType() == dpShapeType::Sphere)
|
||||
//std::cout << "started sphere col at: " << other->GetPosition().x << ", " << other->GetPosition().y << ", " << other->GetPosition().z << std::endl;
|
||||
}
|
||||
else if (!isColliding && wasFound) {
|
||||
m_CurrentlyCollidingObjects.erase(other->GetObjectID());
|
||||
m_RemovedObjects.push_back(other);
|
||||
|
||||
//if (m_CollisionShape->GetShapeType() == dpShapeType::Sphere && other->GetShape()->GetShapeType() == dpShapeType::Sphere)
|
||||
// std::cout << "stopped sphere col at: " << other->GetPosition().x << ", " << other->GetPosition().y << ", " << other->GetPosition().z << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void dpEntity::SetPosition(const NiPoint3& newPos) {
|
||||
if (!m_CollisionShape) return;
|
||||
|
||||
//Update the grid if needed:
|
||||
if (m_Grid) m_Grid->Move(this, newPos.x, newPos.z);
|
||||
|
||||
//If we're a box, we need to first undo the previous position, otherwise things get screwy:
|
||||
if (m_CollisionShape->GetShapeType() == dpShapeType::Box) {
|
||||
auto box = static_cast<dpShapeBox*>(m_CollisionShape);
|
||||
|
||||
if (m_Position != NiPoint3()) {
|
||||
box->SetPosition(NiPoint3(-m_Position.x, -m_Position.y, -m_Position.z));
|
||||
}
|
||||
|
||||
box->SetPosition(newPos);
|
||||
}
|
||||
|
||||
m_Position = newPos;
|
||||
}
|
||||
|
||||
void dpEntity::SetRotation(const NiQuaternion& newRot) {
|
||||
m_Rotation = newRot;
|
||||
|
||||
if (m_CollisionShape->GetShapeType() == dpShapeType::Box) {
|
||||
auto box = static_cast<dpShapeBox*>(m_CollisionShape);
|
||||
box->SetRotation(newRot);
|
||||
}
|
||||
}
|
||||
|
||||
void dpEntity::SetScale(float newScale) {
|
||||
m_Scale = newScale;
|
||||
|
||||
if (m_CollisionShape->GetShapeType() == dpShapeType::Box) {
|
||||
auto box = static_cast<dpShapeBox*>(m_CollisionShape);
|
||||
box->SetScale(newScale);
|
||||
}
|
||||
}
|
||||
|
||||
void dpEntity::SetVelocity(const NiPoint3& newVelocity) {
|
||||
m_Velocity = newVelocity;
|
||||
}
|
||||
|
||||
void dpEntity::SetAngularVelocity(const NiPoint3& newAngularVelocity) {
|
||||
m_AngularVelocity = newAngularVelocity;
|
||||
}
|
||||
|
||||
void dpEntity::SetGrid(dpGrid* grid) {
|
||||
m_Grid = grid;
|
||||
m_Grid->Add(this);
|
||||
}
|
86
dPhysics/dpEntity.h
Normal file
86
dPhysics/dpEntity.h
Normal file
@@ -0,0 +1,86 @@
|
||||
#pragma once
|
||||
#include "NiPoint3.h"
|
||||
#include "NiQuaternion.h"
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include "dCommonVars.h"
|
||||
#include "dpCommon.h"
|
||||
#include "dpShapeBase.h"
|
||||
#include "dpCollisionGroups.h"
|
||||
#include "dpGrid.h"
|
||||
|
||||
class dpEntity {
|
||||
friend class dpGrid; //using friend here for now so grid can access everything
|
||||
|
||||
public:
|
||||
dpEntity(const LWOOBJID& objectID, dpShapeType shapeType, bool isStatic = true);
|
||||
dpEntity(const LWOOBJID& objectID, NiPoint3 boxDimensions, bool isStatic = true);
|
||||
dpEntity(const LWOOBJID& objectID, float width, float height, float depth, bool isStatic = true);
|
||||
dpEntity(const LWOOBJID& objectID, float radius, bool isStatic = true);
|
||||
|
||||
~dpEntity();
|
||||
|
||||
void Update(float deltaTime);
|
||||
|
||||
void CheckCollision(dpEntity* other);
|
||||
|
||||
const NiPoint3& GetPosition() const { return m_Position; }
|
||||
const NiQuaternion& GetRotation() const { return m_Rotation; }
|
||||
const float GetScale() const { return m_Scale; }
|
||||
|
||||
const NiPoint3& GetVelocity() const { return m_Velocity; }
|
||||
const NiPoint3& GetAngularVelocity() const { return m_AngularVelocity; }
|
||||
|
||||
void SetPosition(const NiPoint3& newPos);
|
||||
void SetRotation(const NiQuaternion& newRot);
|
||||
void SetScale(float newScale);
|
||||
|
||||
void SetVelocity(const NiPoint3& newVelocity);
|
||||
void SetAngularVelocity(const NiPoint3& newAngularVelocity);
|
||||
|
||||
dpShapeBase* GetShape() { return m_CollisionShape; }
|
||||
|
||||
bool GetIsStatic() const { return m_IsStatic; }
|
||||
|
||||
uint8_t GetCollisionGroup() const { return m_CollisionGroup; }
|
||||
void SetCollisionGroup(uint8_t value) { m_CollisionGroup = value; }
|
||||
|
||||
bool GetSleeping() const { return m_Sleeping; }
|
||||
void SetSleeping(bool value) { m_Sleeping = value; }
|
||||
|
||||
const std::vector<dpEntity*>& GetNewObjects() const { return m_NewObjects; }
|
||||
const std::vector<dpEntity*>& GetRemovedObjects() const { return m_RemovedObjects; }
|
||||
const std::map<LWOOBJID, dpEntity*>& GetCurrentlyCollidingObjects() const { return m_CurrentlyCollidingObjects; }
|
||||
|
||||
void PreUpdate() { m_NewObjects.clear(); m_RemovedObjects.clear(); }
|
||||
|
||||
const LWOOBJID& GetObjectID() const { return m_ObjectID; }
|
||||
|
||||
void SetGrid(dpGrid* grid);
|
||||
|
||||
bool GetIsGargantuan() const { return m_IsGargantuan; }
|
||||
|
||||
private:
|
||||
LWOOBJID m_ObjectID;
|
||||
dpShapeBase* m_CollisionShape;
|
||||
bool m_IsStatic;
|
||||
|
||||
NiPoint3 m_Position;
|
||||
NiQuaternion m_Rotation;
|
||||
float m_Scale;
|
||||
|
||||
NiPoint3 m_Velocity;
|
||||
NiPoint3 m_AngularVelocity;
|
||||
|
||||
dpGrid* m_Grid = nullptr;
|
||||
|
||||
uint8_t m_CollisionGroup;
|
||||
bool m_Sleeping = false;
|
||||
|
||||
bool m_IsGargantuan = false;
|
||||
|
||||
std::vector<dpEntity*> m_NewObjects;
|
||||
std::vector<dpEntity*> m_RemovedObjects;
|
||||
std::map<LWOOBJID, dpEntity*> m_CurrentlyCollidingObjects;
|
||||
};
|
163
dPhysics/dpGrid.cpp
Normal file
163
dPhysics/dpGrid.cpp
Normal file
@@ -0,0 +1,163 @@
|
||||
#include "dpGrid.h"
|
||||
#include "dpEntity.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
dpGrid::dpGrid(int numCells, int cellSize) {
|
||||
NUM_CELLS = numCells;
|
||||
CELL_SIZE = cellSize;
|
||||
|
||||
//dumb method but i can't be bothered
|
||||
|
||||
//fill x
|
||||
for (int i = 0; i < NUM_CELLS; i++) {
|
||||
m_Cells.push_back(std::vector<std::forward_list<dpEntity*>>());
|
||||
}
|
||||
|
||||
//fill z
|
||||
for (int i = 0; i < NUM_CELLS; i++) {
|
||||
for (int i = 0; i < NUM_CELLS; i++) {
|
||||
m_Cells[i].push_back(std::forward_list<dpEntity*>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dpGrid::~dpGrid() {
|
||||
for (auto& x : m_Cells) { //x
|
||||
for (auto& y : x) { //y
|
||||
for (auto en : y) {
|
||||
if (!en) continue;
|
||||
delete en;
|
||||
en = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dpGrid::Add(dpEntity* entity) {
|
||||
//Determine which grid cell it's in.
|
||||
int cellX = (int)std::round(entity->m_Position.x) / dpGrid::CELL_SIZE + NUM_CELLS / 2;
|
||||
int cellZ = (int)std::round(entity->m_Position.z) / dpGrid::CELL_SIZE + NUM_CELLS / 2;
|
||||
|
||||
if (cellX < 0) cellX = 0;
|
||||
if (cellZ < 0) cellZ = 0;
|
||||
if (cellX > NUM_CELLS) cellX = NUM_CELLS;
|
||||
if (cellZ > NUM_CELLS) cellZ = NUM_CELLS;
|
||||
|
||||
//Add to cell:
|
||||
m_Cells[cellX][cellZ].push_front(entity);
|
||||
|
||||
//To verify that the object isn't gargantuan:
|
||||
if (entity->GetScale() >= CELL_SIZE * 2 || entity->GetIsGargantuan())
|
||||
m_GargantuanObjects.insert(std::make_pair(entity->m_ObjectID, entity));
|
||||
}
|
||||
|
||||
void dpGrid::Move(dpEntity* entity, float x, float z) {
|
||||
int oldCellX = (int)std::round(entity->m_Position.x) / dpGrid::CELL_SIZE + NUM_CELLS / 2;
|
||||
int oldCellZ = (int)std::round(entity->m_Position.z) / dpGrid::CELL_SIZE + NUM_CELLS / 2;
|
||||
|
||||
int cellX = (int)std::round(x) / dpGrid::CELL_SIZE + NUM_CELLS / 2;
|
||||
int cellZ = (int)std::round(z) / dpGrid::CELL_SIZE + NUM_CELLS / 2;
|
||||
|
||||
if (cellX < 0) cellX = 0;
|
||||
if (cellZ < 0) cellZ = 0;
|
||||
if (cellX > NUM_CELLS) cellX = NUM_CELLS;
|
||||
if (cellZ > NUM_CELLS) cellZ = NUM_CELLS;
|
||||
|
||||
if (oldCellX < 0) oldCellX = 0;
|
||||
if (oldCellZ < 0) oldCellZ = 0;
|
||||
if (oldCellX > NUM_CELLS) oldCellX = NUM_CELLS;
|
||||
if (oldCellZ > NUM_CELLS) oldCellZ = NUM_CELLS;
|
||||
|
||||
if (oldCellX == cellX && oldCellZ == cellZ) return;
|
||||
|
||||
//Remove from perv cell:
|
||||
m_Cells[oldCellX][oldCellZ].remove(entity);
|
||||
|
||||
//Add to the new cell
|
||||
m_Cells[cellX][cellZ].push_front(entity);
|
||||
}
|
||||
|
||||
void dpGrid::Delete(dpEntity* entity) {
|
||||
if (!entity) return;
|
||||
int oldCellX = (int)std::round(entity->m_Position.x) / dpGrid::CELL_SIZE + NUM_CELLS / 2;
|
||||
int oldCellZ = (int)std::round(entity->m_Position.z) / dpGrid::CELL_SIZE + NUM_CELLS / 2;
|
||||
|
||||
if (oldCellX < 0) oldCellX = 0;
|
||||
if (oldCellZ < 0) oldCellZ = 0;
|
||||
if (oldCellX > NUM_CELLS) oldCellX = NUM_CELLS;
|
||||
if (oldCellZ > NUM_CELLS) oldCellZ = NUM_CELLS;
|
||||
|
||||
m_Cells[oldCellX][oldCellZ].remove(entity);
|
||||
|
||||
if (m_GargantuanObjects.find(entity->m_ObjectID) != m_GargantuanObjects.end())
|
||||
m_GargantuanObjects.erase(entity->m_ObjectID);
|
||||
|
||||
if (entity) delete entity;
|
||||
entity = nullptr;
|
||||
}
|
||||
|
||||
void dpGrid::Update(float deltaTime) {
|
||||
//Pre-update:
|
||||
for (auto& x : m_Cells) { //x
|
||||
for (auto& y : x) { //y
|
||||
for (auto en : y) {
|
||||
if (!en) continue;
|
||||
en->PreUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Actual collision detection update:
|
||||
for (int x = 0; x < NUM_CELLS; x++) {
|
||||
for (int y = 0; y < NUM_CELLS; y++) {
|
||||
HandleCell(x, y, deltaTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dpGrid::HandleEntity(dpEntity* entity, dpEntity* other) {
|
||||
if (!entity || !other) return;
|
||||
|
||||
if (other->GetIsStatic())
|
||||
other->CheckCollision(entity); //swap "other" and "entity" if you want dyn objs to handle collisions.
|
||||
}
|
||||
|
||||
void dpGrid::HandleCell(int x, int z, float deltaTime) {
|
||||
auto& entities = m_Cells[x][z]; //vector of entities contained within this cell.
|
||||
|
||||
for (auto en : entities) {
|
||||
if (!en) continue;
|
||||
if (en->GetIsStatic() || en->GetSleeping()) continue;
|
||||
|
||||
//Check against all entities that are in the same cell as us
|
||||
for (auto other : entities)
|
||||
HandleEntity(en, other);
|
||||
|
||||
//To try neighbouring cells as well: (can be disabled if needed)
|
||||
//we only check 4 of the 8 neighbouring cells, otherwise we'd get duplicates and cpu cycles wasted...
|
||||
|
||||
if (x > 0 && z > 0) {
|
||||
for (auto other : m_Cells[x - 1][z - 1])
|
||||
HandleEntity(en, other);
|
||||
}
|
||||
|
||||
if (x > 0) {
|
||||
for (auto other : m_Cells[x - 1][z])
|
||||
HandleEntity(en, other);
|
||||
}
|
||||
|
||||
if (z > 0) {
|
||||
for (auto other : m_Cells[x][z - 1])
|
||||
HandleEntity(en, other);
|
||||
}
|
||||
|
||||
if (x > 0 && z < NUM_CELLS - 1) {
|
||||
for (auto other : m_Cells[x - 1][z + 1])
|
||||
HandleEntity(en, other);
|
||||
}
|
||||
|
||||
for (auto other : m_GargantuanObjects)
|
||||
HandleEntity(en, other.second);
|
||||
}
|
||||
}
|
34
dPhysics/dpGrid.h
Normal file
34
dPhysics/dpGrid.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <forward_list>
|
||||
#include <map>
|
||||
#include "dCommonVars.h"
|
||||
|
||||
class dpEntity;
|
||||
|
||||
class dpGrid {
|
||||
public:
|
||||
//LU has a chunk size of 64x64, with each chunk unit being 3.2 ingame units.
|
||||
int NUM_CELLS = 12; //Most worlds consist of 10 or 11 chunks, so I'm picking 12 to be safe.
|
||||
int CELL_SIZE = 205; //64 * 3.2 = 204.8 rounded up
|
||||
|
||||
public:
|
||||
dpGrid(int numCells, int cellSize);
|
||||
~dpGrid();
|
||||
|
||||
void Add(dpEntity* entity);
|
||||
void Move(dpEntity* entity, float x, float z);
|
||||
void Delete(dpEntity* entity);
|
||||
|
||||
void Update(float deltaTime);
|
||||
|
||||
private:
|
||||
void HandleEntity(dpEntity* entity, dpEntity* other);
|
||||
void HandleCell(int x, int z, float deltaTime);
|
||||
|
||||
private:
|
||||
//cells on X, cells on Y for that X, then another vector that contains the entities within that cell.
|
||||
std::vector<std::vector<std::forward_list<dpEntity*>>> m_Cells;
|
||||
std::map<LWOOBJID, dpEntity*> m_GargantuanObjects;
|
||||
};
|
17
dPhysics/dpShapeBase.cpp
Normal file
17
dPhysics/dpShapeBase.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#include "dpShapeBase.h"
|
||||
#include "dpEntity.h"
|
||||
#include <iostream>
|
||||
|
||||
dpShapeBase::dpShapeBase(dpEntity* parentEntity) :
|
||||
m_ParentEntity(parentEntity)
|
||||
{
|
||||
}
|
||||
|
||||
dpShapeBase::~dpShapeBase() {
|
||||
}
|
||||
|
||||
bool dpShapeBase::IsColliding(dpShapeBase* other) {
|
||||
std::cout << "Base shapes do not have any *shape* to them, and thus cannot be overlapping." << std::endl;
|
||||
std::cout << "You should be using a shape class inherited from this base class." << std::endl;
|
||||
return false;
|
||||
}
|
20
dPhysics/dpShapeBase.h
Normal file
20
dPhysics/dpShapeBase.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
#include "dpCommon.h"
|
||||
|
||||
class dpEntity;
|
||||
|
||||
class dpShapeBase {
|
||||
public:
|
||||
dpShapeBase(dpEntity* parentEntity);
|
||||
virtual ~dpShapeBase();
|
||||
|
||||
virtual bool IsColliding(dpShapeBase* other) = 0;
|
||||
|
||||
const dpShapeType& GetShapeType() const { return m_ShapeType; }
|
||||
|
||||
dpEntity* GetParentEntity() const { return m_ParentEntity; }
|
||||
|
||||
protected:
|
||||
dpEntity* m_ParentEntity;
|
||||
dpShapeType m_ShapeType = dpShapeType::Invalid;
|
||||
};
|
176
dPhysics/dpShapeBox.cpp
Normal file
176
dPhysics/dpShapeBox.cpp
Normal file
@@ -0,0 +1,176 @@
|
||||
#include "dpShapeBase.h"
|
||||
#include "dpShapeBox.h"
|
||||
#include "dpShapeSphere.h"
|
||||
#include "dpCollisionChecks.h"
|
||||
#include "dpEntity.h"
|
||||
|
||||
#include "NiPoint3.h"
|
||||
#include "NiQuaternion.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
dpShapeBox::dpShapeBox(dpEntity* parentEntity, float width, float height, float depth) :
|
||||
dpShapeBase(parentEntity),
|
||||
m_Width(width/2),
|
||||
m_Height(height/2),
|
||||
m_Depth(depth/2),
|
||||
m_Scale(1.0f)
|
||||
{
|
||||
m_ShapeType = dpShapeType::Box;
|
||||
|
||||
InitVertices();
|
||||
}
|
||||
|
||||
dpShapeBox::~dpShapeBox() {
|
||||
}
|
||||
|
||||
bool dpShapeBox::IsColliding(dpShapeBase* other) {
|
||||
if (!other) return false;
|
||||
|
||||
switch (other->GetShapeType()) {
|
||||
case dpShapeType::Sphere:
|
||||
return dpCollisionChecks::CheckSphereBox(m_ParentEntity, other->GetParentEntity());
|
||||
|
||||
case dpShapeType::Box:
|
||||
return dpCollisionChecks::CheckBoxes(m_ParentEntity, other->GetParentEntity());
|
||||
|
||||
default:
|
||||
std::cout << "No collision detection for: " << (int)m_ShapeType << "-to-" << (int)other->GetShapeType() << " collision!" << std::endl;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const float dpShapeBox::GetMaxWidth() {
|
||||
return m_ParentEntity->GetPosition().x + m_Width;
|
||||
}
|
||||
|
||||
const float dpShapeBox::GetTop() {
|
||||
return m_ParentEntity->GetPosition().y + (m_Height * 2);
|
||||
}
|
||||
|
||||
const float dpShapeBox::GetMaxDepth() {
|
||||
return m_ParentEntity->GetPosition().z + m_Depth;
|
||||
}
|
||||
|
||||
const float dpShapeBox::GetMinWidth() {
|
||||
return m_ParentEntity->GetPosition().x - m_Width;
|
||||
}
|
||||
|
||||
const float dpShapeBox::GetBottom() {
|
||||
return m_ParentEntity->GetPosition().y; //- m_Height;
|
||||
}
|
||||
|
||||
const float dpShapeBox::GetMinDepth() {
|
||||
return m_ParentEntity->GetPosition().z - m_Depth;
|
||||
}
|
||||
|
||||
void dpShapeBox::SetScale(float scale) {
|
||||
if (isScaled) return;
|
||||
isScaled = true;
|
||||
|
||||
m_Width *= scale;
|
||||
m_Height *= scale;
|
||||
m_Depth *= scale;
|
||||
|
||||
//fuuuckkk yoouu
|
||||
InitVertices();
|
||||
|
||||
//SetRotation(m_ParentEntity->GetRotation());
|
||||
}
|
||||
|
||||
void dpShapeBox::SetRotation(const NiQuaternion& rotation) {
|
||||
if (m_HasBeenRotated) return; //Boxes cannot be rotated more than once.
|
||||
m_HasBeenRotated = true;
|
||||
|
||||
m_TopMinLeft = m_TopMinLeft.RotateByQuaternion(rotation);
|
||||
m_TopMaxLeft = m_TopMaxLeft.RotateByQuaternion(rotation);
|
||||
m_TopMinRight = m_TopMinRight.RotateByQuaternion(rotation);
|
||||
m_TopMaxRight = m_TopMaxRight.RotateByQuaternion(rotation);
|
||||
|
||||
m_BottomMinLeft = m_BottomMinLeft.RotateByQuaternion(rotation);
|
||||
m_BottomMinRight = m_BottomMinRight.RotateByQuaternion(rotation);
|
||||
m_BottomMaxLeft = m_BottomMaxLeft.RotateByQuaternion(rotation);
|
||||
m_BottomMaxRight = m_BottomMaxRight.RotateByQuaternion(rotation);
|
||||
|
||||
InsertVertices();
|
||||
}
|
||||
|
||||
bool dpShapeBox::IsVertInBox(const NiPoint3& vert) {
|
||||
//if we are in the correct height
|
||||
if (vert.y >= m_MinY && vert.y <= m_MaxY) {
|
||||
|
||||
//if we're inside the x bounds
|
||||
if (vert.x >= m_MinX && vert.x <= m_MaxX) {
|
||||
|
||||
//if we're inside the z bounds
|
||||
if (vert.z >= m_MinZ && vert.z <= m_MaxZ)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void dpShapeBox::InitVertices() {
|
||||
//The four top verts
|
||||
m_TopMinLeft = NiPoint3(GetMinWidth(), GetTop(), GetMinDepth());
|
||||
m_TopMaxLeft = NiPoint3(GetMinWidth(), GetTop(), GetMaxDepth());
|
||||
|
||||
m_TopMinRight = NiPoint3(GetMaxWidth(), GetTop(), GetMinDepth());
|
||||
m_TopMaxRight = NiPoint3(GetMaxWidth(), GetTop(), GetMaxDepth());
|
||||
|
||||
//The four bottom verts
|
||||
m_BottomMinLeft = NiPoint3(GetMinWidth(), GetBottom(), GetMinDepth());
|
||||
m_BottomMaxLeft = NiPoint3(GetMinWidth(), GetBottom(), GetMaxDepth());
|
||||
|
||||
m_BottomMinRight = NiPoint3(GetMaxWidth(), GetBottom(), GetMinDepth());
|
||||
m_BottomMaxRight = NiPoint3(GetMaxWidth(), GetBottom(), GetMaxDepth());
|
||||
|
||||
InsertVertices();
|
||||
}
|
||||
|
||||
void dpShapeBox::SetPosition(const NiPoint3& position) {
|
||||
if (isTransformed) return;
|
||||
isTransformed = true;
|
||||
|
||||
for (auto& vert : m_Vertices) {
|
||||
vert.x += position.x;
|
||||
vert.y += position.y;
|
||||
vert.z += position.z;
|
||||
}
|
||||
|
||||
m_TopMinLeft = m_Vertices[0];
|
||||
m_TopMaxLeft = m_Vertices[1];
|
||||
m_TopMinRight = m_Vertices[2];
|
||||
m_TopMaxRight = m_Vertices[3];
|
||||
|
||||
m_BottomMinLeft = m_Vertices[4];
|
||||
m_BottomMaxLeft = m_Vertices[5];
|
||||
m_BottomMinRight = m_Vertices[6];
|
||||
m_BottomMaxRight = m_Vertices[7];
|
||||
|
||||
for (auto& vert : m_Vertices) {
|
||||
if (m_MinX >= vert.x) m_MinX = vert.x;
|
||||
if (m_MinY >= vert.y) m_MinY = vert.y;
|
||||
if (m_MinZ >= vert.z) m_MinZ = vert.z;
|
||||
|
||||
if (m_MaxX <= vert.x) m_MaxX = vert.x;
|
||||
if (m_MaxY <= vert.y) m_MaxY = vert.y;
|
||||
if (m_MaxZ <= vert.z) m_MaxZ = vert.z;
|
||||
}
|
||||
}
|
||||
|
||||
void dpShapeBox::InsertVertices() {
|
||||
//Insert into our vector:
|
||||
m_Vertices.clear();
|
||||
m_Vertices.push_back(m_TopMinLeft);
|
||||
m_Vertices.push_back(m_TopMaxLeft);
|
||||
m_Vertices.push_back(m_TopMinRight);
|
||||
m_Vertices.push_back(m_TopMaxRight);
|
||||
|
||||
m_Vertices.push_back(m_BottomMinLeft);
|
||||
m_Vertices.push_back(m_BottomMaxLeft);
|
||||
m_Vertices.push_back(m_BottomMinRight);
|
||||
m_Vertices.push_back(m_BottomMaxRight);
|
||||
}
|
72
dPhysics/dpShapeBox.h
Normal file
72
dPhysics/dpShapeBox.h
Normal file
@@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
#include "dpShapeBase.h"
|
||||
#include <vector>
|
||||
#include "NiPoint3.h"
|
||||
#include "NiQuaternion.h"
|
||||
|
||||
class dpShapeBox : public dpShapeBase {
|
||||
public:
|
||||
dpShapeBox(dpEntity* parentEntity, float width, float height, float depth);
|
||||
~dpShapeBox();
|
||||
|
||||
bool IsColliding(dpShapeBase* other);
|
||||
|
||||
const float GetScale() const { return m_Scale; }
|
||||
|
||||
const float GetWidth() const { return m_Width; }
|
||||
const float GetHeight() const { return m_Height; }
|
||||
const float GetDepth() const { return m_Depth; }
|
||||
|
||||
const float GetMaxWidth();
|
||||
const float GetTop();
|
||||
const float GetMaxDepth();
|
||||
|
||||
const float GetMinWidth();
|
||||
const float GetBottom();
|
||||
const float GetMinDepth();
|
||||
|
||||
void SetScale(float scale);
|
||||
|
||||
void SetRotation(const NiQuaternion& rotation);
|
||||
|
||||
const std::vector<NiPoint3>& GetVertices() const { return m_Vertices; }
|
||||
|
||||
bool IsVertInBox(const NiPoint3& vert);
|
||||
|
||||
void InitVertices();
|
||||
|
||||
void SetPosition(const NiPoint3& position);
|
||||
|
||||
//idc atm
|
||||
float m_MinX = 9999.0f;
|
||||
float m_MaxX = -9999.0f;
|
||||
float m_MinY = 9999.0f;
|
||||
float m_MaxY = -9999.0f;
|
||||
float m_MinZ = 9999.0f;
|
||||
float m_MaxZ = -9999.0f;
|
||||
|
||||
private:
|
||||
float m_Width; //X
|
||||
float m_Height; //Y
|
||||
float m_Depth; //Z
|
||||
|
||||
std::vector<NiPoint3> m_Vertices;
|
||||
|
||||
NiPoint3 m_TopMinLeft;
|
||||
NiPoint3 m_TopMinRight;
|
||||
NiPoint3 m_TopMaxLeft;
|
||||
NiPoint3 m_TopMaxRight;
|
||||
|
||||
NiPoint3 m_BottomMinLeft;
|
||||
NiPoint3 m_BottomMinRight;
|
||||
NiPoint3 m_BottomMaxLeft;
|
||||
NiPoint3 m_BottomMaxRight;
|
||||
|
||||
float m_Scale;
|
||||
|
||||
bool m_HasBeenRotated = false;
|
||||
bool isScaled = false;
|
||||
bool isTransformed = false;
|
||||
|
||||
void InsertVertices();
|
||||
};
|
30
dPhysics/dpShapeSphere.cpp
Normal file
30
dPhysics/dpShapeSphere.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#include "dpShapeSphere.h"
|
||||
#include "dpCollisionChecks.h"
|
||||
#include <iostream>
|
||||
|
||||
dpShapeSphere::dpShapeSphere(dpEntity* parentEntity, float radius) :
|
||||
dpShapeBase(parentEntity),
|
||||
m_Radius(radius)
|
||||
{
|
||||
m_ShapeType = dpShapeType::Sphere;
|
||||
}
|
||||
|
||||
dpShapeSphere::~dpShapeSphere() {
|
||||
}
|
||||
|
||||
bool dpShapeSphere::IsColliding(dpShapeBase* other) {
|
||||
if (!other) return false;
|
||||
|
||||
switch (other->GetShapeType()) {
|
||||
case dpShapeType::Sphere:
|
||||
return dpCollisionChecks::CheckSpheres(m_ParentEntity, other->GetParentEntity());
|
||||
|
||||
case dpShapeType::Box:
|
||||
return dpCollisionChecks::CheckSphereBox(m_ParentEntity, other->GetParentEntity());
|
||||
|
||||
default:
|
||||
std::cout << "No collision detection for: " << (int)m_ShapeType << "-to-" << (int)other->GetShapeType() << " collision!" << std::endl;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
17
dPhysics/dpShapeSphere.h
Normal file
17
dPhysics/dpShapeSphere.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include "dpShapeBase.h"
|
||||
|
||||
class dpShapeSphere : public dpShapeBase {
|
||||
public:
|
||||
dpShapeSphere(dpEntity* parentEntity, float radius);
|
||||
~dpShapeSphere();
|
||||
|
||||
bool IsColliding(dpShapeBase* other);
|
||||
|
||||
const float GetRadius() const { return m_Radius; }
|
||||
|
||||
void SetScale(float scale) { m_Radius = scale; }
|
||||
|
||||
private:
|
||||
float m_Radius;
|
||||
};
|
357
dPhysics/dpWorld.cpp
Normal file
357
dPhysics/dpWorld.cpp
Normal file
@@ -0,0 +1,357 @@
|
||||
#include "dpWorld.h"
|
||||
#include "dpEntity.h"
|
||||
#include "dpGrid.h"
|
||||
#include "DetourCommon.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "dConfig.h"
|
||||
|
||||
void dpWorld::Initialize(unsigned int zoneID) {
|
||||
phys_sp_tilecount = std::atoi(Game::config->GetValue("phys_sp_tilecount").c_str());
|
||||
phys_sp_tilesize = std::atoi(Game::config->GetValue("phys_sp_tilesize").c_str());
|
||||
|
||||
//If spatial partitioning is enabled, then we need to create the m_Grid.
|
||||
//if m_Grid exists, then the old method will be used.
|
||||
//SP will NOT be used unless it is added to ShouldUseSP();
|
||||
if (std::atoi(Game::config->GetValue("phys_spatial_partitioning").c_str()) == 1
|
||||
&& ShouldUseSP(zoneID)) {
|
||||
m_Grid = new dpGrid(phys_sp_tilecount, phys_sp_tilesize);
|
||||
}
|
||||
|
||||
Game::logger->Log("dpWorld", "Physics world initialized!\n");
|
||||
|
||||
if (ShouldLoadNavmesh(zoneID)) {
|
||||
if (LoadNavmeshByZoneID(zoneID)) Game::logger->Log("dpWorld", "Loaded navmesh!\n");
|
||||
else Game::logger->Log("dpWorld", "Error(s) occurred during navmesh load.\n");
|
||||
}
|
||||
}
|
||||
|
||||
dpWorld::~dpWorld() {
|
||||
if (m_Grid) {
|
||||
delete m_Grid;
|
||||
m_Grid = nullptr;
|
||||
}
|
||||
|
||||
RecastCleanup();
|
||||
}
|
||||
|
||||
void dpWorld::StepWorld(float deltaTime) {
|
||||
if (m_Grid) {
|
||||
m_Grid->Update(deltaTime);
|
||||
return;
|
||||
}
|
||||
|
||||
//Pre update:
|
||||
for (auto entity : m_StaticEntities) {
|
||||
if (!entity || entity->GetSleeping()) continue;
|
||||
entity->PreUpdate();
|
||||
}
|
||||
|
||||
//Do actual update:
|
||||
for (auto entity : m_DynamicEntites) {
|
||||
if (!entity || entity->GetSleeping()) continue;
|
||||
|
||||
entity->Update(deltaTime);
|
||||
|
||||
for (auto other : m_StaticEntities) {
|
||||
if (!other || other->GetSleeping() || entity->GetObjectID() == other->GetObjectID()) continue;
|
||||
|
||||
other->CheckCollision(entity); //swap "other" and "entity" if you want dyn objs to handle collisions.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dpWorld::AddEntity(dpEntity* entity) {
|
||||
if (m_Grid) entity->SetGrid(m_Grid); //This sorts this entity into the right cell
|
||||
else { //old method, slow
|
||||
if (entity->GetIsStatic()) m_StaticEntities.push_back(entity);
|
||||
else m_DynamicEntites.push_back(entity);
|
||||
}
|
||||
}
|
||||
|
||||
void dpWorld::RemoveEntity(dpEntity* entity) {
|
||||
if (!entity) return;
|
||||
|
||||
if (m_Grid) {
|
||||
m_Grid->Delete(entity);
|
||||
}
|
||||
else {
|
||||
if (entity->GetIsStatic()) {
|
||||
for (size_t i = 0; i < m_StaticEntities.size(); ++i) {
|
||||
if (m_StaticEntities[i] == entity) {
|
||||
delete m_StaticEntities[i];
|
||||
m_StaticEntities[i] = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (size_t i = 0; i < m_DynamicEntites.size(); ++i) {
|
||||
if (m_DynamicEntites[i] == entity) {
|
||||
delete m_DynamicEntites[i];
|
||||
m_DynamicEntites[i] = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dpWorld::RecastCleanup() {
|
||||
if (m_triareas) delete[] m_triareas;
|
||||
m_triareas = 0;
|
||||
|
||||
rcFreeHeightField(m_solid);
|
||||
m_solid = 0;
|
||||
rcFreeCompactHeightfield(m_chf);
|
||||
m_chf = 0;
|
||||
rcFreeContourSet(m_cset);
|
||||
m_cset = 0;
|
||||
rcFreePolyMesh(m_pmesh);
|
||||
m_pmesh = 0;
|
||||
rcFreePolyMeshDetail(m_dmesh);
|
||||
m_dmesh = 0;
|
||||
dtFreeNavMesh(m_navMesh);
|
||||
m_navMesh = 0;
|
||||
|
||||
dtFreeNavMeshQuery(m_navQuery);
|
||||
m_navQuery = 0;
|
||||
|
||||
if (m_ctx) delete m_ctx;
|
||||
}
|
||||
|
||||
bool dpWorld::LoadNavmeshByZoneID(unsigned int zoneID) {
|
||||
std::string path = "./res/maps/navmeshes/" + std::to_string(zoneID) + ".bin";
|
||||
m_navMesh = LoadNavmesh(path.c_str());
|
||||
|
||||
if (m_navMesh) { m_navQuery = dtAllocNavMeshQuery(); m_navQuery->init(m_navMesh, 2048); }
|
||||
else return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
dtNavMesh* dpWorld::LoadNavmesh(const char* path) {
|
||||
FILE* fp;
|
||||
|
||||
#ifdef _WIN32
|
||||
fopen_s(&fp, path, "rb");
|
||||
#elif __APPLE__
|
||||
// macOS has 64bit file IO by default
|
||||
fp = fopen(path, "rb");
|
||||
#else
|
||||
fp = fopen64(path, "rb");
|
||||
#endif
|
||||
|
||||
if (!fp) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Read header.
|
||||
NavMeshSetHeader header;
|
||||
size_t readLen = fread(&header, sizeof(NavMeshSetHeader), 1, fp);
|
||||
if (readLen != 1) {
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (header.magic != NAVMESHSET_MAGIC) {
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (header.version != NAVMESHSET_VERSION) {
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dtNavMesh* mesh = dtAllocNavMesh();
|
||||
if (!mesh) {
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dtStatus status = mesh->init(&header.params);
|
||||
if (dtStatusFailed(status)) {
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Read tiles.
|
||||
for (int i = 0; i < header.numTiles; ++i) {
|
||||
NavMeshTileHeader tileHeader;
|
||||
readLen = fread(&tileHeader, sizeof(tileHeader), 1, fp);
|
||||
if (readLen != 1)
|
||||
return 0;
|
||||
|
||||
if (!tileHeader.tileRef || !tileHeader.dataSize)
|
||||
break;
|
||||
|
||||
unsigned char* data = (unsigned char*)dtAlloc(tileHeader.dataSize, DT_ALLOC_PERM);
|
||||
if (!data) break;
|
||||
memset(data, 0, tileHeader.dataSize);
|
||||
readLen = fread(data, tileHeader.dataSize, 1, fp);
|
||||
if (readLen != 1)
|
||||
return 0;
|
||||
|
||||
mesh->addTile(data, tileHeader.dataSize, DT_TILE_FREE_DATA, tileHeader.tileRef, 0);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
bool dpWorld::ShouldLoadNavmesh(unsigned int zoneID) {
|
||||
return true; //We use default paths now. Might re-tool this function later.
|
||||
|
||||
//TODO: Add to this list as the navmesh folder grows.
|
||||
switch (zoneID) {
|
||||
case 1100:
|
||||
case 1150:
|
||||
case 1151:
|
||||
case 1200:
|
||||
case 1201:
|
||||
case 1300:
|
||||
case 1400:
|
||||
case 1603:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool dpWorld::ShouldUseSP(unsigned int zoneID) {
|
||||
//TODO: Add to this list as needed. Only large maps should be added as tiling likely makes little difference on small maps.
|
||||
switch (zoneID) {
|
||||
case 1100: //Avant Gardens
|
||||
case 1200: //Nimbus Station
|
||||
case 1300: //Gnarled Forest
|
||||
case 1400: //Forbidden Valley
|
||||
case 1800: //Crux Prime
|
||||
case 1900: //Nexus Tower
|
||||
case 2000: //Ninjago
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
float dpWorld::GetHeightAtPoint(const NiPoint3& location) {
|
||||
if (m_navMesh == nullptr) {
|
||||
return location.y;
|
||||
}
|
||||
|
||||
float toReturn = 0.0f;
|
||||
float pos[3];
|
||||
pos[0] = location.x;
|
||||
pos[1] = location.y;
|
||||
pos[2] = location.z;
|
||||
|
||||
dtPolyRef nearestRef = 0;
|
||||
float polyPickExt[3] = { 32.0f, 32.0f, 32.0f };
|
||||
dtQueryFilter filter{};
|
||||
|
||||
m_navQuery->findNearestPoly(pos, polyPickExt, &filter, &nearestRef, 0);
|
||||
m_navQuery->getPolyHeight(nearestRef, pos, &toReturn);
|
||||
|
||||
if (toReturn == 0.0f) {
|
||||
toReturn = location.y;
|
||||
}
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
std::vector<NiPoint3> dpWorld::GetPath(const NiPoint3& startPos, const NiPoint3& endPos, float speed) {
|
||||
std::vector<NiPoint3> path;
|
||||
|
||||
//allows for non-navmesh maps (like new custom maps) to have "basic" enemies.
|
||||
if (m_navMesh == nullptr) {
|
||||
//how many points to generate between start/end?
|
||||
//note: not actually 100% accurate due to rounding, but worst case it causes them to go a tiny bit faster
|
||||
//than their speed value would normally allow at the end.
|
||||
int numPoints = startPos.Distance(startPos, endPos) / speed;
|
||||
|
||||
path.push_back(startPos); //insert the start pos
|
||||
|
||||
//Linearly interpolate between these two points:
|
||||
for (int i = 0; i < numPoints; i++) {
|
||||
NiPoint3 newPoint{ startPos };
|
||||
|
||||
newPoint.x += speed;
|
||||
newPoint.y = newPoint.y + (((endPos.y - startPos.y) / (endPos.x - startPos.x)) * (newPoint.x - startPos.x));
|
||||
|
||||
path.push_back(newPoint);
|
||||
}
|
||||
|
||||
path.push_back(endPos); //finally insert our end pos
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
float sPos[3];
|
||||
float ePos[3];
|
||||
sPos[0] = startPos.x;
|
||||
sPos[1] = startPos.y;
|
||||
sPos[2] = startPos.z;
|
||||
|
||||
ePos[0] = endPos.x;
|
||||
ePos[1] = endPos.y;
|
||||
ePos[2] = endPos.z;
|
||||
|
||||
dtStatus pathFindStatus;
|
||||
dtPolyRef startRef;
|
||||
dtPolyRef endRef;
|
||||
float polyPickExt[3] = { 32.0f, 32.0f, 32.0f };
|
||||
dtQueryFilter filter{};
|
||||
|
||||
//Find our start poly
|
||||
m_navQuery->findNearestPoly(sPos, polyPickExt, &filter, &startRef, 0);
|
||||
|
||||
//Find our end poly
|
||||
m_navQuery->findNearestPoly(ePos, polyPickExt, &filter, &endRef, 0);
|
||||
|
||||
pathFindStatus = DT_FAILURE;
|
||||
int m_nstraightPath = 0;
|
||||
int m_npolys = 0;
|
||||
dtPolyRef m_polys[MAX_POLYS];
|
||||
float m_straightPath[MAX_POLYS * 3];
|
||||
unsigned char m_straightPathFlags[MAX_POLYS];
|
||||
dtPolyRef m_straightPathPolys[MAX_POLYS];
|
||||
int m_straightPathOptions = 0;
|
||||
|
||||
if (startRef && endRef) {
|
||||
m_navQuery->findPath(startRef, endRef, sPos, ePos, &filter, m_polys, &m_npolys, MAX_POLYS);
|
||||
|
||||
if (m_npolys) {
|
||||
// In case of partial path, make sure the end point is clamped to the last polygon.
|
||||
float epos[3];
|
||||
dtVcopy(epos, ePos);
|
||||
if (m_polys[m_npolys - 1] != endRef)
|
||||
m_navQuery->closestPointOnPoly(m_polys[m_npolys - 1], ePos, epos, 0);
|
||||
|
||||
m_navQuery->findStraightPath(sPos, epos, m_polys, m_npolys,
|
||||
m_straightPath, m_straightPathFlags,
|
||||
m_straightPathPolys, &m_nstraightPath, MAX_POLYS, m_straightPathOptions);
|
||||
|
||||
// At this point we have our path. Copy it to the path store
|
||||
int nIndex = 0;
|
||||
for (int nVert = 0; nVert < m_nstraightPath; nVert++) {
|
||||
/*m_PathStore[nPathSlot].PosX[nVert] = StraightPath[nIndex++];
|
||||
m_PathStore[nPathSlot].PosY[nVert] = StraightPath[nIndex++];
|
||||
m_PathStore[nPathSlot].PosZ[nVert] = StraightPath[nIndex++];*/
|
||||
|
||||
NiPoint3 newPoint{ m_straightPath[nIndex++], m_straightPath[nIndex++], m_straightPath[nIndex++] };
|
||||
path.push_back(newPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_npolys = 0;
|
||||
m_nstraightPath = 0;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
80
dPhysics/dpWorld.h
Normal file
80
dPhysics/dpWorld.h
Normal file
@@ -0,0 +1,80 @@
|
||||
#pragma once
|
||||
#include "Singleton.h"
|
||||
#include <vector>
|
||||
|
||||
//Navmesh includes:
|
||||
#include "Recast.h"
|
||||
#include "DetourNavMesh.h"
|
||||
#include "DetourNavMeshBuilder.h"
|
||||
#include "DetourNavMeshQuery.h"
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
static const int NAVMESHSET_MAGIC = 'M' << 24 | 'S' << 16 | 'E' << 8 | 'T'; //'MSET';
|
||||
static const int NAVMESHSET_VERSION = 1;
|
||||
|
||||
struct NavMeshSetHeader {
|
||||
int magic;
|
||||
int version;
|
||||
int numTiles;
|
||||
dtNavMeshParams params;
|
||||
};
|
||||
|
||||
struct NavMeshTileHeader {
|
||||
dtTileRef tileRef;
|
||||
int dataSize;
|
||||
};
|
||||
|
||||
static const int MAX_POLYS = 256;
|
||||
static const int MAX_SMOOTH = 2048;
|
||||
|
||||
class NiPoint3;
|
||||
class dpEntity;
|
||||
class dpGrid;
|
||||
|
||||
class dpWorld : public Singleton<dpWorld> {
|
||||
public:
|
||||
void Initialize(unsigned int zoneID);
|
||||
|
||||
~dpWorld();
|
||||
void RecastCleanup();
|
||||
|
||||
bool LoadNavmeshByZoneID(unsigned int zoneID);
|
||||
dtNavMesh* LoadNavmesh(const char* path);
|
||||
bool ShouldLoadNavmesh(unsigned int zoneID);
|
||||
bool ShouldUseSP(unsigned int zoneID);
|
||||
|
||||
float GetHeightAtPoint(const NiPoint3& location);
|
||||
std::vector<NiPoint3> GetPath(const NiPoint3& startPos, const NiPoint3& endPos, float speed = 10.0f);
|
||||
bool IsLoaded() const { return m_navMesh != nullptr; }
|
||||
|
||||
void StepWorld(float deltaTime);
|
||||
|
||||
void AddEntity(dpEntity* entity);
|
||||
void RemoveEntity(dpEntity* entity);
|
||||
|
||||
private:
|
||||
dpGrid* m_Grid;
|
||||
bool phys_spatial_partitioning = 1;
|
||||
int phys_sp_tilesize = 205;
|
||||
int phys_sp_tilecount = 12;
|
||||
|
||||
std::vector<dpEntity*> m_StaticEntities;
|
||||
std::vector<dpEntity*> m_DynamicEntites;
|
||||
|
||||
//Navmesh stuffs:
|
||||
unsigned char* m_triareas;
|
||||
rcHeightfield* m_solid;
|
||||
rcCompactHeightfield* m_chf;
|
||||
rcContourSet* m_cset;
|
||||
rcPolyMesh* m_pmesh;
|
||||
rcConfig m_cfg;
|
||||
rcPolyMeshDetail* m_dmesh;
|
||||
|
||||
class InputGeom* m_geom;
|
||||
class dtNavMesh* m_navMesh;
|
||||
class dtNavMeshQuery* m_navQuery;
|
||||
unsigned char m_navMeshDrawFlags;
|
||||
rcContext* m_ctx;
|
||||
};
|
35
dPhysics/main.cpp
Normal file
35
dPhysics/main.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
//This file included for reference only
|
||||
|
||||
/*#include <iostream>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include "dpWorld.h"
|
||||
|
||||
#include "NiQuaternion.hpp"
|
||||
#include "NiPoint3.hpp"
|
||||
|
||||
int main() {
|
||||
std::cout << "dPhysics test engine" << std::endl;
|
||||
|
||||
//Test rotation code:
|
||||
NiPoint3 p(1.0f, 0.0f, 0.0f);
|
||||
|
||||
float angle = 45.0f;
|
||||
NiQuaternion q = NiQuaternion::CreateFromAxisAngle(NiPoint3(0.0f, 0.0f, 1.0f), angle);
|
||||
|
||||
NiPoint3 rotated = p.RotateByQuaternion(q);
|
||||
|
||||
std::cout << "OG: " << p.x << ", " << p.y << ", " << p.z << std::endl;
|
||||
std::cout << "Quater: " << q.x << ", " << q.y << ", " << q.z << ", " << q.w << " angle: " << angle << std::endl;
|
||||
std::cout << "Rotated: " << rotated.x << ", " << rotated.y << ", " << rotated.z << std::endl;
|
||||
|
||||
//Test some collisions:
|
||||
dpWorld::GetInstance().Initialize(1000);
|
||||
|
||||
while (true) {
|
||||
dpWorld::GetInstance().StepWorld(1.0f/60.0f);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(16));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}*/
|
Reference in New Issue
Block a user