diff options
author | jjesswan <jessica_wan@brown.edu> | 2024-04-22 21:56:26 -0400 |
---|---|---|
committer | jjesswan <jessica_wan@brown.edu> | 2024-04-22 21:56:26 -0400 |
commit | a556b45abf18f1bd509daaf63b66b7d55e9fd291 (patch) | |
tree | bc9b8a2d184c12aee236e7f9f276a34b84ca552d /engine-ocean/Game/Systems | |
parent | cd7c76017a12bb548036571c1ff13e551369d06d (diff) |
add engine version
Diffstat (limited to 'engine-ocean/Game/Systems')
64 files changed, 4716 insertions, 0 deletions
diff --git a/engine-ocean/Game/Systems/AI/Actions/btaction.cpp b/engine-ocean/Game/Systems/AI/Actions/btaction.cpp new file mode 100644 index 0000000..1b35340 --- /dev/null +++ b/engine-ocean/Game/Systems/AI/Actions/btaction.cpp @@ -0,0 +1,6 @@ +#include "btaction.h" + +BTAction::BTAction() +{ + +} diff --git a/engine-ocean/Game/Systems/AI/Actions/btaction.h b/engine-ocean/Game/Systems/AI/Actions/btaction.h new file mode 100644 index 0000000..180f72c --- /dev/null +++ b/engine-ocean/Game/Systems/AI/Actions/btaction.h @@ -0,0 +1,17 @@ +#ifndef BTACTION_H +#define BTACTION_H +#include "Game/Systems/AI/btnode.h" + +class BTAction : public BTNode +{ +public: + BTAction(); + virtual Status update(float seconds) = 0; + virtual void reset() = 0; + +private: + Status m_status; + +}; + +#endif // BTACTION_H diff --git a/engine-ocean/Game/Systems/AI/Actions/walkaction.cpp b/engine-ocean/Game/Systems/AI/Actions/walkaction.cpp new file mode 100644 index 0000000..6a6d116 --- /dev/null +++ b/engine-ocean/Game/Systems/AI/Actions/walkaction.cpp @@ -0,0 +1,90 @@ +#include "walkaction.h" +#include "Game/Components/PathfindComponent.h" +#include "Game/Components/TransformComponent.h" +#include "Game/GameObjects/GameObject.h" +#include "glm/glm.hpp" +#include <memory> + + +WalkAction::WalkAction(std::string entity_id, + std::map<std::string, BlackboardData>& global_blackboard): + m_global_blackboard(global_blackboard) +{ + + m_entity_id = entity_id; + m_path.clear(); + + m_global_blackboard[m_entity_id].conditionData["isPathfinding"].conditionTrue = false; + m_global_blackboard[m_entity_id].conditionData["atDestination"].conditionTrue = false; + m_global_blackboard[m_entity_id].conditionData["pathfound"].conditionTrue = false; + + + + +} + +void WalkAction::setPath(glm::vec3 entity_pos){ + std::cout << "---------SETTING PATHH" << std::endl; + m_destination = m_global_blackboard["player"].locationData.currPos; + m_path = m_global_blackboard["navmesh"].environment->getComponent<PathfindComponent>()->getPath(glm::vec3(-0.58249, 0, -0.0210782), glm::vec3(19.5371, 0, 1.39167)); +} + +// only activates if the previous conditions are true +Status WalkAction::update(float seconds){ + + glm::vec3 pos = m_global_blackboard[m_entity_id].locationData.currPos; + + // get a path if entity is not pathfinding + if (!m_global_blackboard[m_entity_id].conditionData["isPathfinding"].conditionTrue && + !m_global_blackboard[m_entity_id].conditionData["atDestination"].conditionTrue && + !m_global_blackboard[m_entity_id].conditionData["pathfound"].conditionTrue){ + setPath(pos); + m_global_blackboard[m_entity_id].conditionData["isPathfinding"].conditionTrue = true; + m_global_blackboard[m_entity_id].conditionData["pathfound"].conditionTrue = true; + + } + + if (!m_path.empty()){ + if (m_global_blackboard[m_entity_id].conditionData["onGround"].conditionTrue){ + //std::cout << "on ground" << std::endl; + + std::shared_ptr<ModelTransform> temp_mt = std::make_shared<ModelTransform>(); + temp_mt->setPos(pos); + glm::vec3 v = m_path.back();//glm::vec3(m_path.back().x, pos.y, m_path.back().z); + glm::vec3 dir = glm::normalize(v-temp_mt->getPos()); + temp_mt->translate(dir); + //std::cout << "v: (" << v.x << ", " << v.y << ", " << v.z << ")" << std::endl; + glm::vec3 pos_eps = v + .01f; + glm::vec3 neg_eps = v - .01f; + + // pop if entity within a certain episilon of node + if (neg_eps.x < temp_mt->getPos().x < pos_eps.x && + neg_eps.z < temp_mt->getPos().z < pos_eps.z){ + m_path.pop_back(); + } + + m_global_blackboard[m_entity_id].locationData.setToPos = temp_mt->getPos(); + } + + return Status::RUNNING; + } + + // if reached destination, then walking succeeded + if (pos.x == m_destination.x && pos.z == m_destination.z){ + m_global_blackboard[m_entity_id].conditionData["isPathfinding"].conditionTrue = false; + m_global_blackboard[m_entity_id].conditionData["atDestination"].conditionTrue = true; + std::cout << "-reached-" << std::endl; + return Status::SUCCESS; + + } + + // otherwise + m_global_blackboard[m_entity_id].conditionData["isPathfinding"].conditionTrue = false; + return Status::FAIL; + +} + +void WalkAction::reset(){} +void WalkAction::addChildren(BTNode *node){} + + diff --git a/engine-ocean/Game/Systems/AI/Actions/walkaction.h b/engine-ocean/Game/Systems/AI/Actions/walkaction.h new file mode 100644 index 0000000..2e24864 --- /dev/null +++ b/engine-ocean/Game/Systems/AI/Actions/walkaction.h @@ -0,0 +1,29 @@ +#ifndef WALKACTION_H +#define WALKACTION_H +#include "Game/Components/TransformComponent.h" +#include "Game/GameObjects/GameObject.h" +#include "Game/Systems/AI/btnode.h" +#include "glm/fwd.hpp" +#include <memory> +#include "btaction.h" + +class WalkAction : public BTNode +{ +public: + WalkAction(std::string entity_id, std::map<std::string, BlackboardData>& global_blackboard); + Status update(float seconds) override; + void reset() override; + void setPath(glm::vec3 entity_pos); + void addChildren(BTNode *node) override; + + + +private: + std::vector<glm::vec3> m_path; + glm::vec3 m_destination; + std::map<std::string, BlackboardData>& m_global_blackboard; + std::string m_entity_id; +}; + + +#endif // WALKACTION_H diff --git a/engine-ocean/Game/Systems/AI/Conditions/btcondition.cpp b/engine-ocean/Game/Systems/AI/Conditions/btcondition.cpp new file mode 100644 index 0000000..f4de4af --- /dev/null +++ b/engine-ocean/Game/Systems/AI/Conditions/btcondition.cpp @@ -0,0 +1,6 @@ +#include "btcondition.h" + +BTCondition::BTCondition() +{ + +} diff --git a/engine-ocean/Game/Systems/AI/Conditions/btcondition.h b/engine-ocean/Game/Systems/AI/Conditions/btcondition.h new file mode 100644 index 0000000..6ded57b --- /dev/null +++ b/engine-ocean/Game/Systems/AI/Conditions/btcondition.h @@ -0,0 +1,18 @@ +#ifndef BTCONDITION_H +#define BTCONDITION_H +#include "Game/Systems/AI/btnode.h" + + +class BTCondition : public BTNode +{ +public: + BTCondition(); + virtual Status update(float seconds) = 0; + virtual void reset() = 0; + + +private: + bool m_condition; +}; + +#endif // BTCONDITION_H diff --git a/engine-ocean/Game/Systems/AI/Conditions/proximitycondition.cpp b/engine-ocean/Game/Systems/AI/Conditions/proximitycondition.cpp new file mode 100644 index 0000000..2e40ce7 --- /dev/null +++ b/engine-ocean/Game/Systems/AI/Conditions/proximitycondition.cpp @@ -0,0 +1,43 @@ +#include "proximitycondition.h" +#include "Game/Components/TransformComponent.h" +#include "glm/glm.hpp" +#include <memory> + +ProximityCondition::ProximityCondition(std::string entity_id, + std::map<std::string, BlackboardData>& global_blackboard, + float proximity): + m_global_blackboard(global_blackboard) +{ + m_proximity = proximity; + m_entity_id = entity_id; + // initialize just in case + m_global_blackboard["player"].conditionData["isJumping"].conditionTrue = false; + + +} + +// maybe can check locations from blackboard +// pass blackboard into constructor +// struct: positiondata --> getCurrentPos, setCurrentPos +bool ProximityCondition::checkProximity(){ + // unrooted distance + glm::vec3 aiPos = m_global_blackboard[m_entity_id].locationData.currPos; + glm::vec3 otherPos = m_global_blackboard["player"].locationData.currPos; + float distance = pow(aiPos.x-otherPos.x, 2) + pow(aiPos.y-otherPos.y, 2) + pow(aiPos.z-otherPos.z, 2); + + if (distance <= m_proximity) return true; + return false; +} + +// at every update, check if AIPos is near otherPos +Status ProximityCondition::update(float seconds){ + // while entity is still pathfinding, keep returning success + if (m_global_blackboard[m_entity_id].conditionData["isPathfinding"].conditionTrue) return Status::SUCCESS; + if (checkProximity() && m_global_blackboard["player"].conditionData["isJumping"].conditionTrue) return Status::SUCCESS; + + return Status::FAIL; +} + + +void ProximityCondition::reset(){} +void ProximityCondition::addChildren(BTNode *node){} diff --git a/engine-ocean/Game/Systems/AI/Conditions/proximitycondition.h b/engine-ocean/Game/Systems/AI/Conditions/proximitycondition.h new file mode 100644 index 0000000..e43d178 --- /dev/null +++ b/engine-ocean/Game/Systems/AI/Conditions/proximitycondition.h @@ -0,0 +1,27 @@ +#ifndef PROXIMITYCONDITION_H +#define PROXIMITYCONDITION_H +#include "Game/Components/TransformComponent.h" +#include "btcondition.h" +#include <memory> + + +class ProximityCondition : public BTNode +{ +public: + ProximityCondition(std::string entity_id, + std::map<std::string, BlackboardData>& global_blackboard, + float proximity); + Status update(float seconds) override; + void reset() override; + void addChildren(BTNode *node) override; + + +private: + bool checkProximity(); + float m_proximity; + std::string m_entity_id; + std::map<std::string, BlackboardData>& m_global_blackboard; + +}; + +#endif // PROXIMITYCONDITION_H diff --git a/engine-ocean/Game/Systems/AI/aibehaviorcomponent.cpp b/engine-ocean/Game/Systems/AI/aibehaviorcomponent.cpp new file mode 100644 index 0000000..b43ed07 --- /dev/null +++ b/engine-ocean/Game/Systems/AI/aibehaviorcomponent.cpp @@ -0,0 +1,46 @@ +#include "aibehaviorcomponent.h" +#include "Game/Systems/AI/Actions/walkaction.h" +#include "Game/Systems/AI/Conditions/proximitycondition.h" +#include "Game/Systems/AI/btselector.h" +#include "Game/Systems/AI/btsequence.h" + +AIBehaviorComponent::AIBehaviorComponent(std::string entity_id, + std::map<std::string, BlackboardData>& global_blackboard): + m_global_blackboard(global_blackboard) +{ + m_entity_id = entity_id; + makeBehaviorTree(); + +} + +void AIBehaviorComponent::makeBehaviorTree(){ + // leaves + //std::unique_ptr<BTNode> walk = std::make_unique<WalkAction>(m_entity_id, m_global_blackboard); + BTNode *proximCond = new ProximityCondition(m_entity_id, m_global_blackboard, 20.f); + BTNode *walk = new WalkAction(m_entity_id, m_global_blackboard); + + +// // pathfind sequence + BTNode *pathfindSeq = new BTSequence; + pathfindSeq->addChildren(proximCond); + pathfindSeq->addChildren(walk); + +// // idle sequence + +// // root + m_root = new BTSelector; + m_root->addChildren(pathfindSeq); +} + +// how might i be able to generalize the creation of the tree? +void AIBehaviorComponent::update(float seconds){ + // update root, which updates all its children + //std::cout << "---------in ai system" << std::endl; + + m_status = m_root->update(seconds); +} + +AIBehaviorComponent::~AIBehaviorComponent(){ + delete m_root; +} + diff --git a/engine-ocean/Game/Systems/AI/aibehaviorcomponent.h b/engine-ocean/Game/Systems/AI/aibehaviorcomponent.h new file mode 100644 index 0000000..5a86b88 --- /dev/null +++ b/engine-ocean/Game/Systems/AI/aibehaviorcomponent.h @@ -0,0 +1,27 @@ +#ifndef AIBEHAVIORCOMPONENT_H +#define AIBEHAVIORCOMPONENT_H + + +#include "Game/Components/Component.h" +#include "Game/Systems/AI/btselector.h" +#include "Game/Systems/aisystem.h" +#include <map> +#include <string> +class AIBehaviorComponent : public Component +{ +public: + AIBehaviorComponent(std::string entity_id, + std::map<std::string, BlackboardData>& global_blackboard); + ~AIBehaviorComponent(); + void update(float seconds); + +private: + void makeBehaviorTree(); + + std::string m_entity_id; + std::map<std::string, BlackboardData>& m_global_blackboard; + BTNode *m_root = 0; + Status m_status = Status::SUCCESS; +}; + +#endif // AIBEHAVIORCOMPONENT_H diff --git a/engine-ocean/Game/Systems/AI/btnode.cpp b/engine-ocean/Game/Systems/AI/btnode.cpp new file mode 100644 index 0000000..2740089 --- /dev/null +++ b/engine-ocean/Game/Systems/AI/btnode.cpp @@ -0,0 +1,6 @@ +#include "btnode.h" + +BTNode::BTNode() +{ + +} diff --git a/engine-ocean/Game/Systems/AI/btnode.h b/engine-ocean/Game/Systems/AI/btnode.h new file mode 100644 index 0000000..b025da6 --- /dev/null +++ b/engine-ocean/Game/Systems/AI/btnode.h @@ -0,0 +1,18 @@ +#ifndef BTNODE_H +#define BTNODE_H + + +enum Status{ + SUCCESS, FAIL, RUNNING +}; + +class BTNode +{ +public: + BTNode(); + virtual ~BTNode() = default; + virtual Status update(float seconds) = 0; + virtual void reset() = 0; + virtual void addChildren(BTNode *node) = 0; +}; +#endif // BTNODE_H diff --git a/engine-ocean/Game/Systems/AI/btselector.cpp b/engine-ocean/Game/Systems/AI/btselector.cpp new file mode 100644 index 0000000..9652838 --- /dev/null +++ b/engine-ocean/Game/Systems/AI/btselector.cpp @@ -0,0 +1,29 @@ +#include "btselector.h" +#include <iostream> + +BTSelector::BTSelector() +{ + +} + +void BTSelector::addChildren(BTNode* node){ + m_children.push_back(node); +} + +Status BTSelector::update(float seconds){ + // update each children until one doesnt fail + for (auto node : m_children){ + Status result = node->update(seconds); + + // select this one and return its status --> this node is currently running + if (result != Status::FAIL){ + m_selected_node = node; + return result; + } + } + + // otherwise if all children fail, then fail this selector + return Status::FAIL; +} + +void BTSelector::reset(){} diff --git a/engine-ocean/Game/Systems/AI/btselector.h b/engine-ocean/Game/Systems/AI/btselector.h new file mode 100644 index 0000000..20058ac --- /dev/null +++ b/engine-ocean/Game/Systems/AI/btselector.h @@ -0,0 +1,21 @@ +#ifndef BTSELECTOR_H +#define BTSELECTOR_H +#include "Game/Systems/AI/btnode.h" +#include <vector> + + +class BTSelector : public BTNode +{ +public: + BTSelector(); + Status update(float seconds) override; + void reset() override; + void addChildren(BTNode *node) override; + + +private: + std::vector<BTNode *> m_children; + BTNode *m_selected_node; +}; + +#endif // BTSELECTOR_H diff --git a/engine-ocean/Game/Systems/AI/btsequence.cpp b/engine-ocean/Game/Systems/AI/btsequence.cpp new file mode 100644 index 0000000..ba6169d --- /dev/null +++ b/engine-ocean/Game/Systems/AI/btsequence.cpp @@ -0,0 +1,30 @@ +#include "btsequence.h" +#include <vector> + +BTSequence::BTSequence() +{ + +} + +void BTSequence::addChildren(BTNode *node){ + m_sequence.push_back(node); +} + +Status BTSequence::update(float seconds){ + + for (auto node : m_sequence){ + if (node->update(seconds) == Status::FAIL){ + return Status::RUNNING; + } + // if come across any node that fails + if (node->update(seconds) == Status::FAIL){ + return Status::FAIL; + } + } + + // if no node is fail or running, sequence is completed + return Status::SUCCESS; +} + +void BTSequence::reset(){} + diff --git a/engine-ocean/Game/Systems/AI/btsequence.h b/engine-ocean/Game/Systems/AI/btsequence.h new file mode 100644 index 0000000..5ff230c --- /dev/null +++ b/engine-ocean/Game/Systems/AI/btsequence.h @@ -0,0 +1,20 @@ +#ifndef BTSEQUENCE_H +#define BTSEQUENCE_H +#include "btnode.h" +#include <vector> + + +class BTSequence : public BTNode +{ +public: + BTSequence(); + Status update(float seconds) override; + void reset() override; + void addChildren(BTNode* node) override; + + +private: + std::vector<BTNode *> m_sequence; +}; + +#endif // BTSEQUENCE_H diff --git a/engine-ocean/Game/Systems/CollisionSystems/BVH/bvhtree.cpp b/engine-ocean/Game/Systems/CollisionSystems/BVH/bvhtree.cpp new file mode 100644 index 0000000..46155bc --- /dev/null +++ b/engine-ocean/Game/Systems/CollisionSystems/BVH/bvhtree.cpp @@ -0,0 +1,234 @@ +#include "bvhtree.h" +#include "Game/Components/CollisionComponents/CollisionComponent.h" +#include "Game/Components/CollisionComponents/boundingtriangle.h" +#include "Game/GameObjects/GameObject.h" +#include "Game/Systems/CollisionSystems/ellipsoidtrianglecollisionsystem.h" +#include <map> + +BVHTree::BVHTree(std::map<std::string, std::shared_ptr<GameObject>>& rigid_gameobjects) : + m_rigid_gameobjects(rigid_gameobjects) +{ + initializePrimitiveInfo(); + buildBVH(); + BVHNode one = m_bvhNodes[0]; + BVHNode two = m_bvhNodes[1]; + BVHNode three = m_bvhNodes[2]; + + + std::cout << "primitives size: " << m_primitives.size() << std::endl; +} + +// populate primitive info vector -> primitives = triangles +void BVHTree::initializePrimitiveInfo(){ + for (const auto &go : m_rigid_gameobjects){ + N += go.second->getComponent<CollisionComponent>()->getCollisionShape<BoundingTriangle>()->getTriangleData().size(); + for (const Triangle &tri : go.second->getComponent<CollisionComponent>()->getCollisionShape<BoundingTriangle>()->getTriangleData()){ + // the order might not be accurate.... + BVHPrimitive prim = {tri}; + //std::cout << "centroid: (" << prim.centroid.x << ", " << prim.centroid.y << ", " << prim.centroid.z << ")" << std::endl; + + m_primitives.push_back(prim); + + } + } +} + +void BVHTree::buildBVH(){ + // initialize array of primitive indices + m_primitive_indices = new int[N]; + for (int i=0; i<N; i++){ + m_primitive_indices[i] = i; + } + + m_bvhNodes = new BVHNode[N*2 - 1]; + int rootNodeID = 0; + m_nodesUsed = 1; + + // assign all primitives to root node + BVHNode& root = m_bvhNodes[0]; + root.leftNode = 0; + root.firstPrimitiveIndex = 0; + root.primitiveCount = N; + + updateNodeBounds(rootNodeID); + // subdivide recursively + subdivide(rootNodeID); +} + +void BVHTree::updateNodeBounds(int nodeIndex){ + BVHNode& node = m_bvhNodes[nodeIndex]; + node.bounds.max = glm::vec3(-INFINITY); + node.bounds.min = glm::vec3(INFINITY); + + for (int first = node.firstPrimitiveIndex, i=0; i<node.primitiveCount; i++){ + int leafIndex = m_primitive_indices[first + i]; + BVHPrimitive& leafPrimitive = m_primitives[leafIndex]; + // update node bounds with the most max and most min coordinates + node.bounds = getNodeBounds<Bounds3f>(node.bounds, leafPrimitive.bounds); + } +} + +void BVHTree::subdivide(int nodeIndex){ + BVHNode& node = m_bvhNodes[nodeIndex]; + // terminate recursion + + + if (node.primitiveCount <= 1) return; + + // otherwise determine split axis and position +// // SAHH HEREEEEE +// int bestAxis = 0; +// float bestPos = 0; +// float bestCost = INFINITY; +// for (int axis=0; axis<3; axis++){ +// for (int i=0; i<node.primitiveCount; i++){ +// BVHPrimitive& primitive = m_primitives[m_primitive_indices[node.firstPrimitiveIndex + i]]; +// float candidatePos = primitive.centroid[axis]; +// float cost = evaluateSAH(node, axis, candidatePos); +// if (cost < bestCost){ +// bestPos = candidatePos; +// bestAxis = axis; +// bestCost = cost; +// } + +// } +// } + +// int axis = bestAxis; +// float splitPos = bestPos; + +// glm::vec3 e = node.bounds.max - node.bounds.min; +// float parentArea = (e.x*e.y) + (e.y*e.z) * (e.z*e.x); +// float parentCost = node.primitiveCount*parentArea; +// if (bestCost >= parentCost) return; + + // longest axis split (temporary) + glm::vec3 extent = node.bounds.max - node.bounds.min; + int axis = 0; // initialize to be x axis + if (extent.y > extent.x) axis = 1; + if (extent.z > extent[axis]) axis = 2; + float splitPos = node.bounds.min[axis] + extent[axis] * 0.5f; + + // in-place partition + int i = node.firstPrimitiveIndex; + int j = i + node.primitiveCount - 1; + while (i <= j){ + // if to the left of split axis + if (m_primitives[m_primitive_indices[i]].centroid[axis] < splitPos){ + i++; + } else { + std::swap(m_primitive_indices[i], m_primitive_indices[j--]); + } + } + + // abort split if one of the sides is empty + int leftCount = i - node.firstPrimitiveIndex; + if (leftCount == 0 || leftCount == node.primitiveCount) return; + + // create child nodes + int leftChildID = m_nodesUsed++; + int rightChildID = m_nodesUsed++; + + // left child + m_bvhNodes[leftChildID].firstPrimitiveIndex = node.firstPrimitiveIndex; + m_bvhNodes[leftChildID].primitiveCount = leftCount; + + // right child + m_bvhNodes[rightChildID].firstPrimitiveIndex = i; + m_bvhNodes[rightChildID].primitiveCount = node.primitiveCount - leftCount; + + node.leftNode = leftChildID; + node.primitiveCount = 0; + + // recurse + updateNodeBounds(leftChildID); + updateNodeBounds(rightChildID); + subdivide(leftChildID); + subdivide(rightChildID); +} + +float BVHTree::evaluateSAH(BVHNode& node, int axis, float pos){ + BVHaabb leftBox, rightBox; + leftBox.bounds.min = glm::vec3(INFINITY), rightBox.bounds.min = glm::vec3(INFINITY); + leftBox.bounds.max = glm::vec3(-INFINITY), rightBox.bounds.max = glm::vec3(-INFINITY); + + int leftCount = 0, rightCount = 0; + for (int i=0; i<node.primitiveCount; i++){ + BVHPrimitive& primitive = m_primitives[m_primitive_indices[node.firstPrimitiveIndex + i]]; + if (primitive.centroid[axis] < pos){ + leftCount++; + leftBox.grow(getNodeBounds_Point<Bounds3f>(leftBox.bounds, primitive.triangle.vertexA)); + leftBox.grow(getNodeBounds_Point<Bounds3f>(leftBox.bounds, primitive.triangle.vertexB)); + leftBox.grow(getNodeBounds_Point<Bounds3f>(leftBox.bounds, primitive.triangle.vertexC)); + } else { + rightCount++; + rightBox.grow(getNodeBounds_Point<Bounds3f>(rightBox.bounds, primitive.triangle.vertexA)); + rightBox.grow(getNodeBounds_Point<Bounds3f>(rightBox.bounds, primitive.triangle.vertexB)); + rightBox.grow(getNodeBounds_Point<Bounds3f>(rightBox.bounds, primitive.triangle.vertexC)); + } + } + float cost = leftCount * leftBox.area() + rightCount * rightBox.area(); + return cost > 0 ? cost : INFINITY; +} + + +void BVHTree::intersectBVH(const glm::vec3 posA, const glm::vec3 posB, const int nodeIndex, std::vector<Triangle> &candidates, glm::vec3 ellip_R){ + //std::cout << "-" << std::endl; + + BVHNode& node = m_bvhNodes[nodeIndex]; + if (!intersectsNode(posA, posB, node.bounds.min, node.bounds.max, ellip_R+glm::vec3(.001))){ + return; + } + if (node.isLeaf()){ + // intersect with primitives of the leaf + //std::cout << "candidate size: " << node.primitiveCount << std::endl; + for (int i=0; i<node.primitiveCount; i++){ + Triangle triangle = m_primitives[m_primitive_indices[node.firstPrimitiveIndex + i]].triangle; +// BVHPrimitive prim = m_primitives[m_primitive_indices[node.firstPrimitiveIndex + i]]; + +// if (prim.centroid == glm::vec3(39.42457, 1.84954, -15.7088)){ +// std::cout << "found correct triangle" << std::endl; +// } + //std::cout << "centroid: (" << prim.centroid.x << ", " << prim.centroid.y << ", " << prim.centroid.z << ")" << std::endl; + candidates.push_back(triangle); + + } + } else { + // recurse + intersectBVH(posA, posB, node.leftNode, candidates, ellip_R); + intersectBVH(posA, posB, node.leftNode + 1, candidates, ellip_R); + } +} + +std::vector<Triangle> BVHTree::getBVHDetectedCollisions(glm::vec3 posA, glm::vec3 posB, glm::vec3 ellip_R){ + // keep recursing, starting at root node, and then return final result in candidate_obstacles + std::vector<Triangle> candidate_rigid_tri; + intersectBVH(posA, posB, 0, candidate_rigid_tri, ellip_R); + return candidate_rigid_tri; +} + +// determines if movement ray intersects an AABB via the slab test +bool BVHTree::intersectsNode(glm::vec3 posA, glm::vec3 posB, glm::vec3 min, glm::vec3 max, glm::vec3 ellip_R){ + glm::vec3 object_min = glm::vec3(std::min(posA.x, posB.x), std::min(posA.y, posB.y), std::min(posA.z, posB.z)); + glm::vec3 object_max = glm::vec3(std::max(posA.x, posB.x), std::max(posA.y, posB.y), std::max(posA.z, posB.z)); + object_min -= ellip_R; + object_max += ellip_R; + + bool x_int = object_max.x > min.x && object_min.x < max.x; + bool y_int = object_max.y > min.y && object_min.y < max.y; + bool z_int = object_max.z > min.z && object_min.z < max.z; + + if (x_int && y_int && z_int){ + return true; + } + return false; +} + +BVHTree::~BVHTree(){ + delete[] m_bvhNodes; + delete[] m_primitive_indices; + m_bvhNodes = NULL; + m_primitive_indices = NULL; + +} + diff --git a/engine-ocean/Game/Systems/CollisionSystems/BVH/bvhtree.h b/engine-ocean/Game/Systems/CollisionSystems/BVH/bvhtree.h new file mode 100644 index 0000000..b175e44 --- /dev/null +++ b/engine-ocean/Game/Systems/CollisionSystems/BVH/bvhtree.h @@ -0,0 +1,106 @@ +#ifndef BVHTREE_H +#define BVHTREE_H +#include "Game/Components/CollisionComponents/BoundingTriangle.h" +#include "Game/GameObjects/GameObject.h" +#include "glm/glm.hpp" +#include <map> +#include <vector> + +// primitive = triangle +struct BVHPrimitive{ + + Bounds3f bounds; + glm::vec3 centroid; + Triangle triangle; + + BVHPrimitive(const Triangle &tri): + triangle(tri), + bounds(tri.bounds), + centroid(.333f*(tri.vertexA + tri.vertexB + tri.vertexC)){} +}; + +struct BVHNode{ + // glm::vec3 aabbMin, aabbMax; + Bounds3f bounds; + int leftNode, firstPrimitiveIndex, primitiveCount; + bool isLeaf(){ + return primitiveCount > 0; + } +}; + +struct BVHaabb { + Bounds3f bounds; + + + void grow(Bounds3f newbounds){ + bounds.min = newbounds.min; + bounds.max = newbounds.max; + } + + float area(){ + glm::vec3 e = bounds.max-bounds.min; + return (e.x*e.y) + (e.y*e.z) * (e.z*e.x); + } +}; + + +template <typename T> Bounds3f +getNodeBounds(const Bounds3f &b1, const Bounds3f &b2){ + Bounds3f bounds; + bounds.min = glm::vec3(std::min(b1.min.x, b2.min.x), + std::min(b1.min.y, b2.min.y), + std::min(b1.min.z, b2.min.z)); + bounds.max = glm::vec3(std::max(b1.max.x, b2.max.x), + std::max(b1.max.y, b2.max.y), + std::max(b1.max.z, b2.max.z)); + return bounds; +} + +template <typename T> Bounds3f +getNodeBounds_Point(const Bounds3f &b, const glm::vec3 &p){ + Bounds3f bounds; + bounds.min = glm::vec3(std::min(b.min.x, p.x), + std::min(b.min.y, p.y), + std::min(b.min.z, p.z)); + bounds.max = glm::vec3(std::max(b.max.x, p.x), + std::max(b.max.y, p.y), + std::max(b.max.z, p.z)); + return bounds; +} + +class BVHTree + +{ + BVHNode *m_bvhNodes; + int *m_primitive_indices; +public: + BVHTree(std::map<std::string, std::shared_ptr<GameObject>>& rigid_gameobjects); + ~BVHTree(); + std::vector<Triangle> getBVHDetectedCollisions(glm::vec3 posA, glm::vec3 posB, glm::vec3 ellip_R); + void intersectBVH(glm::vec3 posA, glm::vec3 posB, const int nodeIndex, std::vector<Triangle> &candidates, glm::vec3 ellip_R); + + +private: + float evaluateSAH(BVHNode& node, int axis, float pos); + void initializePrimitiveInfo(); + void buildBVH(); + void updateNodeBounds(int nodeIndex); + void subdivide(int nodeIndex); + bool intersectsNode(glm::vec3 posA, glm::vec3 posB, glm::vec3 min, glm::vec3 max, glm::vec3 ellip_R); + + + //std::unique_ptr<EllipsoidTriangleCollisionSystem> m_ellipsoid_triangle_collision_system; + + std::vector<BVHPrimitive> m_primitives; + std::map<std::string, std::shared_ptr<GameObject>>& m_rigid_gameobjects; + + + //std::vector<BVHNode> m_bvhNodes; + //std::vector<int> m_primitive_indices; + int m_nodesUsed = 1; + int N = 0; + + // std::pair<std::vector<CollisionData>, glm::vec3> m_detected_collisions; +}; + +#endif // BVHTREE_H diff --git a/engine-ocean/Game/Systems/CollisionSystems/UniformGrid/uniformgrid.cpp b/engine-ocean/Game/Systems/CollisionSystems/UniformGrid/uniformgrid.cpp new file mode 100644 index 0000000..822129b --- /dev/null +++ b/engine-ocean/Game/Systems/CollisionSystems/UniformGrid/uniformgrid.cpp @@ -0,0 +1,127 @@ +#include "uniformgrid.h" +#include "Game/Components/CollisionComponents/BoundingDynamicMesh.h" +#include "Game/Components/CollisionComponents/BoundingTriangle.h" +#include "Game/Components/CollisionComponents/CollisionComponent.h" +#include "Game/Components/CollisionComponents/CylinderCollider.h" +#include <algorithm> + +UniformGrid::UniformGrid(std::map<std::string, std::shared_ptr<GameObject>>& dynamic_gameobjects): + m_dynamic_gameobjects(dynamic_gameobjects) +{ + initializeGrid(); +} + +void UniformGrid::initializeGrid(){ + // grid square: size it based on the largest entity + + // set default square size to be same dimensions as player + m_square_dimensions = ceil(m_dynamic_gameobjects.at("player")->getComponent<CollisionComponent>()->getCollisionShape<BoundingDynamicMesh>()->getCylinder().aabbDimensions); + + + // find largest shape dimension in order to set grid square dimensions + for (const auto &go : m_dynamic_gameobjects){ + if (go.second->hasComponent<CollisionComponent>()){ + glm::vec3 shape_dimensions = go.second->getComponent<CollisionComponent>()->getCollisionShape<BoundingDynamicMesh>()->getCylinder().aabbDimensions; + // find the largest Bounds3f for a shape + if (glm::all(glm::greaterThan(shape_dimensions, m_square_dimensions))){ + m_square_dimensions = ceil(shape_dimensions); + } + } + } + + // initialize grid based on where entities are initially placed + for (const auto &go : m_dynamic_gameobjects){ + updateUniformGrid(go); + } +} + +std::string UniformGrid::vecToString(glm::vec3 v){ + std::string square_bound = std::to_string(v.x) + std::to_string(v.y) + std::to_string(v.z); + return square_bound; +} + + +void UniformGrid::populateContainingCell(glm::vec3 bound_corner, std::string entity_id){ + glm::vec3 factor = floor(bound_corner/m_square_dimensions); + glm::vec3 container_min = factor*m_square_dimensions; + + for (auto &square : m_grid_map){ + //if the containing square is already in map, then append entity id to its associate list + if (square.first == vecToString(container_min)){ + m_grid_map.at(square.first).insert(entity_id); + return; + } + } + // if not yet in map, add to map + std::set<std::string> ids; + ids.insert(entity_id); + m_grid_map.insert(std::pair(vecToString(container_min), ids)); +} + +void UniformGrid::updateUniformGrid(const std::pair<std::string, std::shared_ptr<GameObject>> &go){ + if (go.second->hasComponent<CollisionComponent>()){ + Bounds3f shapeBounds = go.second->getComponent<CollisionComponent>()->getCollisionShape<BoundingDynamicMesh>()->getCylinder().bounds; + // if bounds min is within an interval of square dimensions, then occupy map with that entity + glm::vec3 shape_dimensions = go.second->getComponent<CollisionComponent>()->getCollisionShape<BoundingDynamicMesh>()->getCylinder().aabbDimensions; + + + // check all 6 aabb corners + float x_val = shapeBounds.min.x; + for (int x=0; x<2; x++){ + float y_val = shapeBounds.min.y; + for (int y=0; y<2; y++){ + float z_val = shapeBounds.min.z; + for (int z=0; z<2; z++){ + glm::vec3 corner = glm::vec3(x_val,y_val,z_val); + populateContainingCell(corner, go.first); + z_val += shape_dimensions.z; + } + y_val += shape_dimensions.y; + } + x_val += shape_dimensions.x; + } + } +} + +// only for entities that moved: +void UniformGrid::moveEntityGridPosition(const std::pair<std::string, std::shared_ptr<GameObject>> &go){ + // remove previous cells that entity occupied + for (auto &cell : m_grid_map){ + cell.second.erase(go.first); + } + + // if there are no more ids associated with that cell, remove it from map + for (auto cell = m_grid_map.cbegin(), next_i = cell; cell != m_grid_map.cend(); cell = next_i){ + ++next_i; + if (cell->second.empty()){ + m_grid_map.erase(cell); + } + } + + // then update based on new pos + updateUniformGrid(go); +} + +void UniformGrid::updateAllGrid(){ + // initialize grid based on where entities are initially placed + for (const auto &go : m_dynamic_gameobjects){ + moveEntityGridPosition(go); + } + + //std::cout << "new ugrid size: " << m_grid_map.size() << std::endl; +} + +std::set<std::set<std::string>> UniformGrid::detectPossibleCollisions(){ + // store list of names of objects that are near e/o + // uses set there are no duplicate inner sets of the same objects + std::set<std::set<std::string>> candidate_collision_sets; + + for (const auto &cell : m_grid_map){ + if (cell.second.size() > 1){ + candidate_collision_sets.insert(cell.second); + } + } + + // then for each inner set, detect collisions between the contained objects + return candidate_collision_sets; +} diff --git a/engine-ocean/Game/Systems/CollisionSystems/UniformGrid/uniformgrid.h b/engine-ocean/Game/Systems/CollisionSystems/UniformGrid/uniformgrid.h new file mode 100644 index 0000000..c4e5aaa --- /dev/null +++ b/engine-ocean/Game/Systems/CollisionSystems/UniformGrid/uniformgrid.h @@ -0,0 +1,67 @@ +#ifndef UNIFORMGRID_H +#define UNIFORMGRID_H + + +#include "Game/Components/CollisionComponents/BoundingTriangle.h" +#include "Game/GameObjects/GameObject.h" +#include <set> +#include <vector> + +struct GridSquare { + Bounds3f bounds; + glm::vec3 centroid = (bounds.max - bounds.min)/ 2.f; + + +// bool operator<( const GridSquare & s ) const { +// return glm::all(glm::lessThan(this->bounds.min,s.bounds.min)); // for example +// } + + +}; + +//bool operator<(const GridSquare& l, const GridSquare& r) +//{ +// return glm::all(glm::lessThan(l.bounds.min, r.bounds.min)); +//} + +//bool operator==(const GridSquare& l, const GridSquare& r) +//{ +// return l.bounds.min == r.bounds.min; +//} + +//bool operator<(const glm::vec3& l, const glm::vec3& r) +//{ +// return glm::all(glm::lessThan(l, r));; +//} + +//bool operator==(const glm::vec3& l, const glm::vec3& r) +//{ +// return glm::all(glm::equal(l, r));; +//} + + +class UniformGrid +{ +public: + UniformGrid(std::map<std::string, std::shared_ptr<GameObject>>& dynamic_gameobjects); + std::set<std::set<std::string>> detectPossibleCollisions(); + void updateAllGrid(); + + + +private: + void populateContainingCell(glm::vec3 bound_corner, std::string entity_id); + void updateUniformGrid(const std::pair<std::string, std::shared_ptr<GameObject>> &go); + void moveEntityGridPosition(const std::pair<std::string, std::shared_ptr<GameObject>> &go); + void initializeGrid(); + std::string vecToString(glm::vec3 v); + + + std::map<std::string, std::shared_ptr<GameObject>>& m_dynamic_gameobjects; + // maps square corner to set of strings + std::map<std::string, std::set<std::string>> m_grid_map; + glm::vec3 m_square_dimensions; + +}; + +#endif // UNIFORMGRID_H diff --git a/engine-ocean/Game/Systems/CollisionSystems/accelerationsystem.cpp b/engine-ocean/Game/Systems/CollisionSystems/accelerationsystem.cpp new file mode 100644 index 0000000..c81be68 --- /dev/null +++ b/engine-ocean/Game/Systems/CollisionSystems/accelerationsystem.cpp @@ -0,0 +1,6 @@ +#include "accelerationsystem.h" + +AccelerationSystem::AccelerationSystem() +{ + +} diff --git a/engine-ocean/Game/Systems/CollisionSystems/accelerationsystem.h b/engine-ocean/Game/Systems/CollisionSystems/accelerationsystem.h new file mode 100644 index 0000000..dd47731 --- /dev/null +++ b/engine-ocean/Game/Systems/CollisionSystems/accelerationsystem.h @@ -0,0 +1,11 @@ +#ifndef ACCELERATIONSYSTEM_H +#define ACCELERATIONSYSTEM_H + + +class AccelerationSystem +{ +public: + AccelerationSystem(); +}; + +#endif // ACCELERATIONSYSTEM_H diff --git a/engine-ocean/Game/Systems/CollisionSystems/collisionsystem.cpp b/engine-ocean/Game/Systems/CollisionSystems/collisionsystem.cpp new file mode 100644 index 0000000..40816de --- /dev/null +++ b/engine-ocean/Game/Systems/CollisionSystems/collisionsystem.cpp @@ -0,0 +1,183 @@ +#include "collisionsystem.h" +#include "Game/Components/CollisionComponents/BoundingDynamicMesh.h" +#include "Game/Components/CollisionComponents/CollisionComponent.h" +#include "Game/Components/CollisionComponents/CylinderCollider.h" +#include "Game/Components/TransformComponent.h" + +CollisionSystem::CollisionSystem(std::map<std::string, std::shared_ptr<GameObject>>& gameobjects, + std::map<std::string, std::shared_ptr<GameObject>>& dynamic_gameobjects, + std::map<std::string, std::shared_ptr<GameObject>>& rigid_gameobjects, + std::map<int, Input>& input_map, + std::map<std::string, BlackboardData>& global_blackboard) : + m_gameobjects(gameobjects), + m_dynamic_gameobjects(dynamic_gameobjects), + m_rigid_gameobjects(rigid_gameobjects), + m_uniform_grid_system(std::make_unique<UniformGrid>(dynamic_gameobjects)), + m_global_blackboard(global_blackboard) +{ + +} + +TransformComponent* CollisionSystem::getTransform(std::shared_ptr<GameObject> &go){ + return go->getComponent<TransformComponent>(); + +} + +CollisionComponent* CollisionSystem::getCollisionComp(std::shared_ptr<GameObject> &go){ + return go->getComponent<CollisionComponent>(); +} + +glm::vec2 CollisionSystem::calculateCircleMVT(const Cylinder &a, const Cylinder &b){ + float len = glm::length(a.point - b.point); + + glm::vec2 mtv = ((b.point - a.point)/len) * (a.radius+b.radius - len); + return mtv; +} + +float CollisionSystem::calculateLineMVT(const Cylinder &a, const Cylinder &b){ + float aRight = b.max - a.min; + float aLeft = a.max - b.min; + if ((aLeft < 0) || (aRight < 0)){ + return -1.f; + } + if (aRight < aLeft){ + return aRight; + } + return -aLeft; +} + +glm::vec3 CollisionSystem::collideCylinderCylinder(const std::shared_ptr<GameObject> &a_go, const std::shared_ptr<GameObject> &b_go){ + Cylinder a = a_go->getComponent<CollisionComponent>()->getCollisionShape<BoundingDynamicMesh>()->getCylinder(); + Cylinder b = b_go->getComponent<CollisionComponent>()->getCollisionShape<BoundingDynamicMesh>()->getCylinder(); + + //std::cout << "a max: " << a.max << std::endl; + //std::cout << "b max: " << b.max << std::endl; + + + + float len_squared = pow(a.point[0]-b.point[0],2) + pow(a.point[1]-b.point[1],2); + float radius_sum = pow(a.radius + b.radius, 2); + + // check if circles overlap + if (len_squared < radius_sum){ + //std::cout << "CIRCLES OVERLAP" << std::endl; + // check if lines overlap + if ((a.min < b.max) && (b.min < a.max)){ + std::cout << "COLLIDINGGG" << std::endl; + // calculate both circle and line mtvs, and then return the shortest mtv + glm::vec2 circle_mtv = calculateCircleMVT(a, b); + float circle_len = glm::length(circle_mtv); + + float line_mtv = calculateLineMVT(a, b); + + if (abs(circle_len) < abs(line_mtv)){ + return glm::vec3(circle_mtv[0], 0.f, circle_mtv[1]); + } else { + return glm::vec3(0.f, line_mtv, 0.f); + } + } + } + + // return translation of 0 if there is no collision + return glm::vec3(0.f); +} + + +bool CollisionSystem::checkCollisionShape(const std::shared_ptr<GameObject> &go, std::string shape_type){ + // add more collision shapes here + if (shape_type == "cylinder"){ + return (go->getComponent<CollisionComponent>()->hasCollisionShape<CylinderCollider>()); + } + return false; +} + +void CollisionSystem::resolveCollision(std::shared_ptr<GameObject> &go, + glm::vec3 mtv, + float deltaTime, + std::string go_name){ + glm::vec3 potential_pos = go->getComponent<TransformComponent>()->getPos(); + go->getComponent<TransformComponent>()->old_pos = potential_pos; + go->getComponent<TransformComponent>()->movingLaterally = false; + + // translate, and also update the collision cylinder point + float a = go->getComponent<CollisionComponent>()->getAcceleration(); + float v = go->getComponent<CollisionComponent>()->getReboundVel(); + float delta_x = mtv.x + v*deltaTime + .5*a*pow(deltaTime, 2); + float delta_z = mtv.z + v*deltaTime + .5*a*pow(deltaTime, 2); + + // update estimated_final_pos, so that tri-ellip collision can see if the new pos collides with environment + glm::vec3 translationDir = glm::vec3(delta_x, mtv.y, delta_z); + if (delta_x != 0 || delta_z != 0){ + go->getComponent<TransformComponent>()->movingLaterally = true; + } + + std::shared_ptr<ModelTransform> temp_mt = go->getComponent<TransformComponent>()->getMT(); + temp_mt->translate(translationDir); + potential_pos = temp_mt->getPos(); + + + m_global_blackboard[go_name].locationData.setToPos = potential_pos; + go->getComponent<TransformComponent>()->estimated_final_pos = potential_pos; +} + +void CollisionSystem::detectCylinderCollisions(std::shared_ptr<GameObject> &a, + std::shared_ptr<GameObject> &b, + float deltaTime, + std::string a_name, std::string b_name){ + // if its a cyl-cyl collision + glm::vec3 mtv(0.f); + + mtv = collideCylinderCylinder(a, b); + if (mtv != glm::vec3(0.f)){ + resolveCollision(b, 1.f*mtv, deltaTime, b_name); + resolveCollision(a, -1.f*mtv, deltaTime, a_name); + } + +} + +bool CollisionSystem::areCollidable(const std::shared_ptr<GameObject> &a, const std::shared_ptr<GameObject> &b){ + return (a->hasComponent<CollisionComponent>() && b->hasComponent<CollisionComponent>()); +} + + +// DYNAMIC-DYNAMIC COLLISIONS: CYLINDER +void CollisionSystem::dynamicDynamicCollisions(double deltaTime){ + std::set<std::set<std::string>> candidate_collison_set = m_uniform_grid_system->detectPossibleCollisions(); + + // std::cout << "all collisions size : " << candidate_collison_set.size() << std::endl; + + for (const std::set<std::string> &set : candidate_collison_set){ + + // std::cout << "collision group size : " << set.size() << std::endl; + + std::vector<std::string> coll_group; + for (const std::string &name : set){ + coll_group.push_back(name); + } + + for (int i=0; i < coll_group.size(); i++){ + auto a = m_dynamic_gameobjects.at(coll_group[i]); + for (int j=i+1; j < coll_group.size(); j++){ + auto b = m_dynamic_gameobjects.at(coll_group[j]); + // if a is not the same GO as b, and at least has collide components and thus is collidable, + if ((a != b) && (areCollidable(a, b))){ + //std::cout << "collide: " << coll_group[i] << " + " << coll_group[j] << std::endl; + detectCylinderCollisions(a, b, deltaTime, coll_group[i], coll_group[j]); + } + } + } + } +} + + +void CollisionSystem::update(double deltaTime){ + // update uniform grid before detecting with it + m_uniform_grid_system->updateAllGrid(); + dynamicDynamicCollisions(deltaTime); +} + +void CollisionSystem::draw(){} +void CollisionSystem::scrollEvent(double distance){} +void CollisionSystem::mousePosEvent(double xpos, double ypos){} + + diff --git a/engine-ocean/Game/Systems/CollisionSystems/collisionsystem.h b/engine-ocean/Game/Systems/CollisionSystems/collisionsystem.h new file mode 100644 index 0000000..5c635ba --- /dev/null +++ b/engine-ocean/Game/Systems/CollisionSystems/collisionsystem.h @@ -0,0 +1,53 @@ +#ifndef COLLISIONSYSTEM_H +#define COLLISIONSYSTEM_H +#include "Game/Components/CollisionComponents/CollisionComponent.h" +#include "Game/Components/CollisionComponents/CylinderCollider.h" +#include "Game/Components/TransformComponent.h" +#include "Game/GameWorld.h" +#include "Game/Systems/CollisionSystems/UniformGrid/uniformgrid.h" +#include "Game/Systems/CollisionSystems/ellipsoidtrianglecollisionsystem.h" +#include "Game/Systems/system.h" + +class CollisionSystem : public System +{ +public: + CollisionSystem(std::map<std::string, std::shared_ptr<GameObject>>& gameobjects, + std::map<std::string, std::shared_ptr<GameObject>>& dynamic_gameobjects, + std::map<std::string, std::shared_ptr<GameObject>>& rigid_gameobjects, + std::map<int, Input>& input_map, + std::map<std::string, BlackboardData>& global_blackboard); + void draw() override; + void update(double deltaTime) override; + void scrollEvent(double distance) override; + void mousePosEvent(double xpos, double ypos) override; +private: + bool areCollidable(const std::shared_ptr<GameObject> &a, const std::shared_ptr<GameObject> &b); + void detectCylinderCollisions(std::shared_ptr<GameObject> &a, std::shared_ptr<GameObject> &b, float deltaTime, + std::string a_name, std::string b_name); + void resolveCollision(std::shared_ptr<GameObject> &go, glm::vec3 translation, float deltaTime, std::string go_name); + bool checkCollisionShape(const std::shared_ptr<GameObject> &go, std::string shape_type); + glm::vec3 projectMotion(const glm::vec3 &initial_pos, float deltaTime); + + TransformComponent* getTransform(std::shared_ptr<GameObject> &go); + CollisionComponent* getCollisionComp(std::shared_ptr<GameObject> &go); + + void rigidDynamicCollisions(double deltaTime); + void dynamicDynamicCollisions(double deltaTime); + + + // cylinder-cylinder + glm::vec3 collideCylinderCylinder(const std::shared_ptr<GameObject> &a_go, const std::shared_ptr<GameObject> &b_go); + float calculateLineMVT(const Cylinder &a, const Cylinder &b); + glm::vec2 calculateCircleMVT(const Cylinder &a, const Cylinder &b); + + + std::map<std::string, std::shared_ptr<GameObject>>& m_gameobjects; + std::map<std::string, std::shared_ptr<GameObject>>& m_dynamic_gameobjects; + std::map<std::string, std::shared_ptr<GameObject>>& m_rigid_gameobjects; + + std::unique_ptr<UniformGrid> m_uniform_grid_system; + std::map<std::string, BlackboardData>& m_global_blackboard; +}; + + +#endif // COLLISIONSYSTEM_H diff --git a/engine-ocean/Game/Systems/CollisionSystems/ellipsoidtrianglecollisionsystem.cpp b/engine-ocean/Game/Systems/CollisionSystems/ellipsoidtrianglecollisionsystem.cpp new file mode 100644 index 0000000..52b8916 --- /dev/null +++ b/engine-ocean/Game/Systems/CollisionSystems/ellipsoidtrianglecollisionsystem.cpp @@ -0,0 +1,370 @@ +#include "ellipsoidtrianglecollisionsystem.h" +#include "Game/Components/CollisionComponents/BoundingTriangle.h" +#include "Game/Components/CollisionComponents/CollisionComponent.h" +#include "Game/Components/CollisionComponents/boundingellipsoid.h" +#include "Game/Components/DrawComponent.h" +#include "Game/Components/TransformComponent.h" +#include "Game/GameObjects/GameObject.h" +#include <memory> + +EllipsoidTriangleCollisionSystem::EllipsoidTriangleCollisionSystem(std::map<std::string, std::shared_ptr<GameObject>>& rigid_gameobjects) : + m_rigid_gameobjects(rigid_gameobjects), + m_bvh_system(std::make_unique<BVHTree>(rigid_gameobjects)) +{ +} + + +Triangle EllipsoidTriangleCollisionSystem::convertTriangleToSphereSpace(const glm::vec3 &orig_R, const Triangle &orig_triangle){ + Triangle transformed_triangle; + + // divide vertex pos by Rs + transformed_triangle.vertexA = orig_triangle.vertexA / orig_R; + transformed_triangle.vertexB = orig_triangle.vertexB / orig_R; + transformed_triangle.vertexC = orig_triangle.vertexC / orig_R; + + transformed_triangle.edge1 = transformed_triangle.vertexB - transformed_triangle.vertexA; + transformed_triangle.edge2 = transformed_triangle.vertexC - transformed_triangle.vertexA; + + // does the normal stay the same across transformations? + transformed_triangle.normal = glm::normalize(glm::cross(transformed_triangle.edge1, transformed_triangle.edge2)); + + return transformed_triangle; +} + + +glm::vec3 EllipsoidTriangleCollisionSystem::getIntersectionPt(const glm::vec3 &ray_p, const glm::vec3 &ray_d, const float t){ + return ray_p + t*ray_d; +} + +float EllipsoidTriangleCollisionSystem::solveQuadratic(float a, float b, float c){ + float discriminant = pow(b,2) - 4*a*c; + + // check for imaginary numbers + if (discriminant >= 0){ + float pos_quad = (-b + sqrt(discriminant)) / (2*a); + float neg_quad = (-b - sqrt(discriminant)) / (2*a); + + // if the mininum is a neg number, then return the larger of the two + if (fmin(pos_quad, neg_quad) < ZERO){ + return fmax(pos_quad, neg_quad); + } else { + return fmin(pos_quad, neg_quad); + } + } + return -1; +} + + +float EllipsoidTriangleCollisionSystem::raycastSphere(const glm::vec3 &ray_p, const glm::vec3 &ray_d, const glm::vec3 posA, const glm::vec3 posB){ + // shift ray and ellipsoid to center first in order to use regular implicit sphere equation + glm::vec3 p = ray_p - posA; + glm::vec3 d = ray_d - posA; + + float a = (d.x*d.x) + (d.y*d.y) + (d.z*d.z); + float b = (2.f*p.x*d.x) + (2.f*p.y*d.y) + (2.f*p.z*d.z); + float c = (p.x*p.x) + (p.y*p.y) + (p.z*p.z) - 1.f; + + return solveQuadratic(a,b,c); +} + +float EllipsoidTriangleCollisionSystem::raycastTriangle(const glm::vec3 &ray_p, const glm::vec3 &ray_d, const Triangle &triangle){ + float t = (glm::dot((triangle.vertexA - ray_p), triangle.normal))/(glm::dot(ray_d, triangle.normal)); + return t; +} + +glm::vec3 EllipsoidTriangleCollisionSystem::getTriangleEdge(const glm::vec3 v1, const glm::vec3 v2){ + return v2 - v1; +} + +bool EllipsoidTriangleCollisionSystem::isSameDirection(const glm::vec3 edge1, const glm::vec3 edge2, const glm::vec3 n){ + if (glm::dot(glm::cross(edge1, edge2), n) > 0){ + return true; + } + return false; +} + +void EllipsoidTriangleCollisionSystem::makeCollisionData(const float t, const glm::vec3 triangle_n, + const glm::vec3 intersection_pos, + const glm::vec3 point_of_contact, + const bool isVertex){ + CollisionData data; + data.t = t; + data.triangle_n = triangle_n; + data.intersection_pos = intersection_pos; + data.point_of_contact = point_of_contact; + data.isVertex = isVertex; + m_collisionData.push_back(data); +} + + +bool EllipsoidTriangleCollisionSystem::detectSphere_with_Interior(const Triangle &triangle, const glm::vec3 posA, const glm::vec3 posB){ + // calculate point on sphere closest to the plane and cast a ray + glm::vec3 ray_p = posA-triangle.normal; // closest point on unit sphere + glm::vec3 ray_d = (posB-posA); + + glm::vec3 n = triangle.normal; + glm::vec3 v = triangle.vertexA; + float plane_const = -1 * (n.x*v.x + n.y*v.y + n.z*v.z); + + // pdf equation + float t = (1-(glm::dot(n, posA) + plane_const)) / (glm::dot(n, (posB-posA))); + if (t < ZERO || t > ONE){ + return false; + } + + // the point P on the sphere closest to plane surface + glm::vec3 P_edge = getIntersectionPt(ray_p, ray_d, t); + + + // determine if point is on triangle plane + // determine if point is also within triangle bounds + glm::vec3 AB = getTriangleEdge(triangle.vertexA, triangle.vertexB); + glm::vec3 BC = getTriangleEdge(triangle.vertexB, triangle.vertexC); + glm::vec3 CA = getTriangleEdge(triangle.vertexC, triangle.vertexA); + + glm::vec3 AP = getTriangleEdge(triangle.vertexA, P_edge); + glm::vec3 BP = getTriangleEdge(triangle.vertexB, P_edge); + glm::vec3 CP = getTriangleEdge(triangle.vertexC, P_edge); + + if (isSameDirection(AB, AP, triangle.normal) && + isSameDirection(BC, BP, triangle.normal) && + isSameDirection(CA, CP, triangle.normal)){ + + // the actual center of sphere + glm::vec3 P_centered = getIntersectionPt(posA, posB-posA, t); + makeCollisionData(t, triangle.normal, P_centered, P_edge, false); + return true; + } + return false; +} + +// do this for each vertex pair AB, BC, CA +bool EllipsoidTriangleCollisionSystem::detectSphere_with_Edge(const glm::vec3 &v1, const glm::vec3 &v2, + const glm::vec3 &triangle_normal, + const glm::vec3 &posA, const glm::vec3 &posB){ + glm::vec3 C = v1; + glm::vec D = v2; + + float a = pow(glm::length(glm::cross(posB-posA, D-C)),2); + float b = glm::dot(2.f*glm::cross(posB-posA, D-C), glm::cross(posA-C, D-C)); + float c = pow(glm::length(glm::cross(posA-C, D-C)),2) - pow(glm::length(D-C),2); + + float t = solveQuadratic(a,b,c); + + // discard a negative t --> indicates no collision with edge + if (t < ZERO || t > ONE){ + return false; + } + + glm::vec3 P = getIntersectionPt(posA, posB-posA, t); + + glm::vec3 AP = P-C; + glm::vec3 AB = D-C; + glm::vec3 normal = AP - (glm::dot(AB,AP)/glm::dot(AB,AB))*AB; + + // determine if intersection pt is between C and D + if ( (0 < glm::dot(P-C,D-C)) && (glm::dot(P-C,D-C) < pow(glm::length(D-C),2)) ){ + makeCollisionData(t, normal, P, P, false); + return true; + } + + return false; +} + +// needs to be done with each vertex +bool EllipsoidTriangleCollisionSystem::detectSphere_with_Vertex(const glm::vec3 &v, const glm::vec3 triangle_normal, const glm::vec3 &posA, const glm::vec3 &posB){ + glm::vec3 ray_p = v; + //glm::vec3 ray_d = v-(posB-posA); + + // convert to origin space: + glm::vec3 vel = posB-posA; + glm::vec3 v_origined = v - posA; + + float a = glm::dot(vel,vel); + float b = 2*(glm::dot(vel, -v_origined)); + float c = pow(glm::length(v_origined),2) - 1.f; + + float t = solveQuadratic(a,b,c); + if (t < ZERO || t > ONE){ + return false; + } + + // where the center is when the sphere hits vertex + glm::vec3 center_pt_origined = t*vel; + // convert back to non-origin space + glm::vec3 center_pt = center_pt_origined + posA; + glm::vec3 normal = center_pt - v; + makeCollisionData(t, normal, center_pt, v, true); + return true; +} + +CollisionData EllipsoidTriangleCollisionSystem::getNearestCollision(const std::vector<CollisionData> &collisionData, const Triangle &sphere_tri){ + CollisionData nearestCollision; + nearestCollision.t = INFINITY; + + for (const CollisionData &data : collisionData){ + if (data.t < nearestCollision.t){ + // set nearest collision to datapoint + nearestCollision = data; + } + } + return nearestCollision; +} + +// SPHERE SPACE +CollisionData EllipsoidTriangleCollisionSystem::detectTriangleCollision(const Triangle &sphere_tri, const glm::vec3 &posA, const glm::vec3 &posB){ + + m_collisionData.clear(); + // if interior is true, no need to check rest. get the only collision data + if (detectSphere_with_Interior(sphere_tri, posA, posB)){ + return m_collisionData[0]; + } + + // otherwise check rest: + + // detect edge with all 3 edges + // | operator executes regardless of if left hand side is true or not + if ( + detectSphere_with_Edge(sphere_tri.vertexA, sphere_tri.vertexB, sphere_tri.normal, posA, posB) | + detectSphere_with_Edge(sphere_tri.vertexB, sphere_tri.vertexC, sphere_tri.normal, posA, posB) | + detectSphere_with_Edge(sphere_tri.vertexC, sphere_tri.vertexA, sphere_tri.normal, posA, posB) ){ + return getNearestCollision(m_collisionData, sphere_tri); + } + + // detect vertex with all 3 vertices + detectSphere_with_Vertex(sphere_tri.vertexA, sphere_tri.normal, posA, posB); + detectSphere_with_Vertex(sphere_tri.vertexB, sphere_tri.normal, posA, posB); + detectSphere_with_Vertex(sphere_tri.vertexC, sphere_tri.normal, posA, posB); + + return getNearestCollision(m_collisionData, sphere_tri); +} + +CollisionData EllipsoidTriangleCollisionSystem::revertDataToOriginalSpace(const CollisionData &sphere_datum, const glm::vec3 &ellip_R, const glm::vec3 orig_triangle_n){ + CollisionData reverted_datum = sphere_datum; + reverted_datum.intersection_pos = reverted_datum.intersection_pos*ellip_R; + reverted_datum.point_of_contact = reverted_datum.point_of_contact*ellip_R; + + return reverted_datum; +} + +void EllipsoidTriangleCollisionSystem::setEllipsoidDimensions(const glm::vec3 &dimensions){ + m_ellipsoid_dimensions = dimensions; +} + +CollisionData EllipsoidTriangleCollisionSystem::collideWithWorld(const glm::vec3 &posA, const glm::vec3 &posB){ + // makes ellipsoids + Ellipsoid ellipsoidA; + ellipsoidA.R = m_ellipsoid_dimensions; + ellipsoidA.center_pos = posA; + + + Ellipsoid ellipsoidB; + ellipsoidB.R = m_ellipsoid_dimensions; + ellipsoidB.center_pos = posB; + + // convert ellipsoid centers to sphere space + glm::vec3 sphere_posA = ellipsoidA.center_pos/m_ellipsoid_dimensions; + glm::vec3 sphere_posB = ellipsoidB.center_pos/m_ellipsoid_dimensions; + + CollisionData nearestCollision; + nearestCollision.t = INFINITY; + + + std::vector<Triangle> candidates = m_bvh_system->getBVHDetectedCollisions(posA, posB, ellipsoidA.R); + // std::cout << "candidate size: " << candidates.size() << std::endl; + + +// for (auto &rigid_go : candidates){ +// //std::cout << "candidate shape name: " << rigid_go->getComponent<DrawComponent>()->getShapeName() << std::endl; +// if (rigid_go->hasComponent<CollisionComponent>()){ +// if (rigid_go->getComponent<CollisionComponent>()->hasCollisionShape<BoundingTriangle>()){ +// // for every triangle in game obj, transform to sphere space and collide with ellipsoid (aka sphere) + for (int i=0; i< candidates.size(); i++){ + Triangle triangle = candidates[i]; +// std::cout<< "triangle i: " << i << ", v1: (" << triangle.vertexA.x << ", " << triangle.vertexA.y << ", " << triangle.vertexA.z << "), v2: (" +// << triangle.vertexB.x << ", " << triangle.vertexB.y << ", " << triangle.vertexB.z << "), v3: (" +// << triangle.vertexC.x << ", " << triangle.vertexC.y << ", " << triangle.vertexC.z << ")" << std::endl; + + // transform triangle to sphere space and detect collision data (in sphere space) for this specific triangle + Triangle sphere_triangle = convertTriangleToSphereSpace(m_ellipsoid_dimensions, triangle); + CollisionData sphere_collisionDatum = detectTriangleCollision(sphere_triangle, sphere_posA, sphere_posB); + CollisionData ellip_collisionDatum = revertDataToOriginalSpace(sphere_collisionDatum, m_ellipsoid_dimensions, triangle.normal); + + // keep track of nearest Collision + if (ellip_collisionDatum.t < nearestCollision.t && ellip_collisionDatum.t <= ONE && ellip_collisionDatum.t >= ZERO){ + nearestCollision = ellip_collisionDatum; + } + // repeat for the other triangles + } + //} + //} + // repeat for all gameobjects + //} + + return nearestCollision; +} + +glm::vec3 EllipsoidTriangleCollisionSystem::doNudge(glm::vec3 curr_pos, CollisionData collision){ + glm::vec3 nudge = m_scale_normals_mt*collision.triangle_n; + + glm::vec3 pos_nudged = collision.intersection_pos + nudge * .01f; + int MAX_NUDGES = 3; + for (int i=0; i<MAX_NUDGES; i++){ + CollisionData nudge_collision = collideWithWorld(curr_pos, pos_nudged); + if (nudge_collision.t == INFINITY){ + curr_pos = pos_nudged; + break; + } else { + if (glm::length((m_scale_normals_mt*nudge_collision.triangle_n) - nudge) < .0001 || glm::length((m_scale_normals_mt*nudge_collision.triangle_n) + nudge) < .0001){ + nudge = -m_scale_normals_mt*nudge_collision.triangle_n; + } else { + nudge = m_scale_normals_mt*nudge_collision.triangle_n; + } + pos_nudged = nudge_collision.intersection_pos + nudge * .01f; + } + } + return curr_pos; + +} + +glm::mat3 EllipsoidTriangleCollisionSystem::makeConversionMT(glm::vec3 initial_center_pos, glm::vec3 dimensions){ + m_conversion_mt = std::make_shared<ModelTransform>(); + m_conversion_mt->translate(-initial_center_pos); + m_conversion_mt->scale(dimensions); + m_scale_normals_mt = glm::transpose((glm::inverse(glm::mat3(m_conversion_mt->getModelMatrix())))); + m_scale_normals_mt = glm::mat4(1.f); + return m_scale_normals_mt; +} + +// in sphere space +std::pair<std::vector<CollisionData>, glm::vec3> EllipsoidTriangleCollisionSystem::mtvSlide(glm::vec3 initial_pos, glm::vec3 final_pos, glm::vec3 dimensions){ + setEllipsoidDimensions(dimensions); + glm::mat3 normals_scale = makeConversionMT(initial_pos, dimensions); + + std::vector<CollisionData> collisions; + glm::vec3 curr_pos = initial_pos; + glm::vec3 next_pos = final_pos; + int MAX_TRANSLATIONS = 3; + for (int i=0; i<MAX_TRANSLATIONS; i++){ + CollisionData c = collideWithWorld(initial_pos, next_pos); + + // if no collisions [0,1] found, then continue on regular path + if (c.t == INFINITY){ + //std::cout << "--mtv-- " << next_pos.x << "," << next_pos.y << "," << next_pos.z << std::endl; + return std::pair<std::vector<CollisionData>, glm::vec3>(collisions, next_pos); + } else { + // nudge along the collision data + //std::cout << "COLLIDING......" << std::endl; + curr_pos = doNudge(curr_pos, c); + glm::vec3 d = next_pos - curr_pos; + glm::vec3 d_corrected = (d - glm::dot(d, normals_scale*c.triangle_n)) * (normals_scale*c.triangle_n); + next_pos = curr_pos + d_corrected; + collisions.push_back(c); + } + + } + + return std::pair<std::vector<CollisionData>, glm::vec3>(collisions, curr_pos); +} + + diff --git a/engine-ocean/Game/Systems/CollisionSystems/ellipsoidtrianglecollisionsystem.h b/engine-ocean/Game/Systems/CollisionSystems/ellipsoidtrianglecollisionsystem.h new file mode 100644 index 0000000..8a7d102 --- /dev/null +++ b/engine-ocean/Game/Systems/CollisionSystems/ellipsoidtrianglecollisionsystem.h @@ -0,0 +1,69 @@ +#ifndef ELLIPSOIDTRIANGLECOLLISIONSYSTEM_H +#define ELLIPSOIDTRIANGLECOLLISIONSYSTEM_H +#include "Game/Components/CollisionComponents/BoundingEllipsoid.h" +#include "Game/Components/CollisionComponents/BoundingTriangle.h" +#include "Game/GameObjects/GameObject.h" +#include "Game/Systems/CollisionSystems/BVH/bvhtree.h" +#include "glm/glm.hpp" +#include <vector> + +struct CollisionData{ + bool hasCollided = true; // only false when there is no collision data to return + bool isVertex = false; + float t = INFINITY; + glm::vec3 triangle_n; // normal of the triangle its colliding with + glm::vec3 intersection_pos; + glm::vec3 point_of_contact; +}; + +class EllipsoidTriangleCollisionSystem +{ +public: + EllipsoidTriangleCollisionSystem(std::map<std::string, std::shared_ptr<GameObject>>& rigid_gameobjects); + + + std::pair<std::vector<CollisionData>, glm::vec3> mtvSlide(glm::vec3 initial_pos, glm::vec3 final_pos, glm::vec3 dimensions); + +private: + CollisionData collideWithWorld(const glm::vec3 &posA, const glm::vec3 &posB); + void setEllipsoidDimensions(const glm::vec3 &dimensions); + glm::vec3 doNudge(glm::vec3 curr_pos, CollisionData collision); + CollisionData revertDataToOriginalSpace(const CollisionData &sphere_datum, const glm::vec3 &ellip_R, const glm::vec3 orig_triangle_n); + CollisionData detectTriangleCollision(const Triangle &sphere_tri, const glm::vec3 &posA, const glm::vec3 &posB); + CollisionData getNearestCollision(const std::vector<CollisionData> &collisionData, const Triangle &sphere_tri); + glm::mat3 makeConversionMT(glm::vec3 initial_center_pos, glm::vec3 dimensions); + + + bool detectSphere_with_Vertex(const glm::vec3 &v, const glm::vec3 triangle_normal, const glm::vec3 &posA, const glm::vec3 &posB); + bool detectSphere_with_Edge(const glm::vec3 &v1, const glm::vec3 &v2, + const glm::vec3 &triangle_normal, + const glm::vec3 &posA, const glm::vec3 &posB); + void makeCollisionData(const float t, const glm::vec3 triangle_n, const glm::vec3 intersection_pos, const glm::vec3 point_of_contact, const bool isVertex); + bool detectSphere_with_Interior(const Triangle &triangle, const glm::vec3 posA, const glm::vec3 posB); + + + bool isSameDirection(const glm::vec3 edge1, const glm::vec3 edge2, const glm::vec3 n); + glm::vec3 getTriangleEdge(const glm::vec3 v1, const glm::vec3 v2); + float raycastSphere(const glm::vec3 &ray_p, const glm::vec3 &ray_d, const glm::vec3 posA, const glm::vec3 posB); + float raycastTriangle(const glm::vec3 &ray_p, const glm::vec3 &ray_d, const Triangle &triangle); + float solveQuadratic(float a, float b, float c); + glm::vec3 getIntersectionPt(const glm::vec3 &ray_p, const glm::vec3 &ray_d, const float t); + Triangle convertTriangleToSphereSpace(const glm::vec3 &orig_R, const Triangle &orig_triangle); + + std::vector<CollisionData> m_collisionData; + std::map<std::string, std::shared_ptr<GameObject>>& m_rigid_gameobjects; + + glm::vec3 m_ellipsoid_dimensions = glm::vec3(1.0); + + float ZERO = -.0001f; + float ONE = 1.0001f; + + std::shared_ptr<ModelTransform> m_conversion_mt; + glm::mat3 m_scale_normals_mt = glm::mat3(1.f); + + std::vector<std::shared_ptr<GameObject>> m_candidate_obstacles; + std::unique_ptr<BVHTree> m_bvh_system; + +}; + +#endif // ELLIPSOIDTRIANGLECOLLISIONSYSTEM_H diff --git a/engine-ocean/Game/Systems/CollisionSystems/environmentcollisiondetectionsystem.cpp b/engine-ocean/Game/Systems/CollisionSystems/environmentcollisiondetectionsystem.cpp new file mode 100644 index 0000000..3470dd8 --- /dev/null +++ b/engine-ocean/Game/Systems/CollisionSystems/environmentcollisiondetectionsystem.cpp @@ -0,0 +1,93 @@ +#include "environmentcollisiondetectionsystem.h" +#include "Game/Components/CollisionComponents/BoundingDynamicMesh.h" +#include "Game/Components/CollisionComponents/CollisionComponent.h" +#include "Game/Components/TransformComponent.h" +#include "Game/GameObjects/GameObject.h" +#include "Game/Systems/CollisionSystems/ellipsoidtrianglecollisionsystem.h" +#include <utility> + +EnvironmentCollisionDetectionSystem::EnvironmentCollisionDetectionSystem( + std::map<std::string, std::shared_ptr<GameObject>>& dynamic_gameobjects, + std::map<std::string, std::shared_ptr<GameObject>>& rigid_gameobjects, + std::map<std::string, std::vector<std::shared_ptr<GameObject>>>& lootables, + std::map<std::string, BlackboardData>& global_blackboard) : + m_dynamic_gameobjects(dynamic_gameobjects), + m_ellipsoid_triangle_collision_system(std::make_unique<EllipsoidTriangleCollisionSystem>(rigid_gameobjects)), + m_global_blackboard(global_blackboard), + m_lootables(lootables) +{ + +} + +TransformComponent* EnvironmentCollisionDetectionSystem::getTransform(std::shared_ptr<GameObject> &go){ + return go->getComponent<TransformComponent>(); + +} + +CollisionComponent* EnvironmentCollisionDetectionSystem::getCollisionComp(std::shared_ptr<GameObject> &go){ + return go->getComponent<CollisionComponent>(); +} + +void EnvironmentCollisionDetectionSystem::detectCollisionWithEnvironment(double deltaTime){ + for (auto &go : m_dynamic_gameobjects){ + collideDynamic(go.first, go.second); + + } +} + + +void EnvironmentCollisionDetectionSystem::collideDynamic(std::string go_name, std::shared_ptr<GameObject> go){ + if (!go->hasComponent<CollisionComponent>()){ + return; + } + + std::pair<std::vector<CollisionData>, glm::vec3> pair = + m_ellipsoid_triangle_collision_system->mtvSlide(getTransform(go)->old_pos, + //getTransform(go.second)->estimated_final_pos, + m_global_blackboard[go_name].locationData.setToPos, + getCollisionComp(go)->getCollisionShape<BoundingDynamicMesh>()->getEllipsoidDimensions()); + + // assume not on ground first; then determine if we are touching ground + getTransform(go)->onGround = false; + m_global_blackboard[go_name].conditionData["onGround"].conditionTrue = false; + for (const CollisionData &collision : pair.first){ + if (glm::dot(glm::vec3(0,1,0), collision.triangle_n) > 0.f){ + getTransform(go)->onGround = true; + m_global_blackboard[go_name].conditionData["onGround"].conditionTrue = true; + + } + } + + if (getTransform(go)->onGround){ + if (getTransform(go)->yVelocity < 0){ + getTransform(go)->yVelocity = 0.f; + getTransform(go)->gravity = 0.f; + } + } + + if (!getTransform(go)->onGround){ + getTransform(go)->gravity = -25.f; + } + + + if (!getTransform(go)->movingLaterally && + getTransform(go)->onGround){ + getTransform(go)->setPos(getTransform(go)->old_pos); + } else { + getTransform(go)->setPos(pair.second); + } + + getCollisionComp(go)->getCollisionShape<BoundingDynamicMesh>()->updateCenterPos(getTransform(go)->getPos()); +} + + +void EnvironmentCollisionDetectionSystem::update(double deltaTime){ + detectCollisionWithEnvironment(deltaTime); +} + +void EnvironmentCollisionDetectionSystem::draw(){} +void EnvironmentCollisionDetectionSystem::scrollEvent(double distance){} +void EnvironmentCollisionDetectionSystem::mousePosEvent(double xpos, double ypos){} + + + diff --git a/engine-ocean/Game/Systems/CollisionSystems/environmentcollisiondetectionsystem.h b/engine-ocean/Game/Systems/CollisionSystems/environmentcollisiondetectionsystem.h new file mode 100644 index 0000000..8beca39 --- /dev/null +++ b/engine-ocean/Game/Systems/CollisionSystems/environmentcollisiondetectionsystem.h @@ -0,0 +1,43 @@ +#ifndef ENVIRONMENTCOLLISIONDETECTIONSYSTEM_H +#define ENVIRONMENTCOLLISIONDETECTIONSYSTEM_H + + +#include "Game/Components/CollisionComponents/CollisionComponent.h" +#include "Game/Components/TransformComponent.h" +#include "Game/Systems/CollisionSystems/ellipsoidtrianglecollisionsystem.h" +#include <memory> +#include "Game/Systems/system.h" + +class EnvironmentCollisionDetectionSystem /*: public System*/ +{ +public: + EnvironmentCollisionDetectionSystem( + std::map<std::string, std::shared_ptr<GameObject>>& dynamic_gameobjects, + std::map<std::string, std::shared_ptr<GameObject>>& rigid_gameobjects, + std::map<std::string, std::vector<std::shared_ptr<GameObject>>>& lootables, + std::map<std::string, BlackboardData>& global_blackboard); + void draw() ; + void update(double deltaTime); + void scrollEvent(double distance); + void mousePosEvent(double xpos, double ypos) ; + + +private: + void detectCollisionWithEnvironment(double deltaTime); + CollisionComponent* getCollisionComp(std::shared_ptr<GameObject> &go); + TransformComponent* getTransform(std::shared_ptr<GameObject> &go); + + void collideDynamic(std::string go_name, std::shared_ptr<GameObject> go); + + + std::map<std::string, std::shared_ptr<GameObject>>& m_dynamic_gameobjects; + std::unique_ptr<EllipsoidTriangleCollisionSystem> m_ellipsoid_triangle_collision_system; + //std::unique_ptr<BVHTree> m_bvh_system; + std::vector<std::shared_ptr<GameObject>> all_rigid_go; + std::map<std::string, BlackboardData>& m_global_blackboard; + + std::map<std::string, std::vector<std::shared_ptr<GameObject>>>& m_lootables; + +}; + +#endif // ENVIRONMENTCOLLISIONDETECTIONSYSTEM_H diff --git a/engine-ocean/Game/Systems/Inventory/inventoryitem.cpp b/engine-ocean/Game/Systems/Inventory/inventoryitem.cpp new file mode 100644 index 0000000..44f20ef --- /dev/null +++ b/engine-ocean/Game/Systems/Inventory/inventoryitem.cpp @@ -0,0 +1,33 @@ +#include "inventoryitem.h" + +InventoryItem::InventoryItem(const char* worldLabelFile, const char* inventoryLabelFile, + glm::vec2 worldLabelScale, + glm::vec2 inventoryLabelScale, + std::set<std::string>& m_shownScreens) +{ + TextureData worldLabelTex = Global::graphics.loadTextureFromFile(worldLabelFile); + TextureData inventoryLabelTex = Global::graphics.loadTextureFromFile(inventoryLabelFile); + + m_world_label = std::make_shared<UIDisplay>(worldLabelTex, glm::vec2(0.f), worldLabelScale, m_shownScreens, AspectRatio::LAND_FIT); + m_inventory_label = std::make_shared<UIDisplay>(inventoryLabelTex, glm::vec2(0.f), inventoryLabelScale, m_shownScreens, AspectRatio::LAND_FIT); +} + +void InventoryItem::updateWorldLabelPos(glm::vec2 pos){ + m_world_label->setPos(pos); +} + +glm::mat4 InventoryItem::getWorldLabelTransformationMat(){ + return m_world_label->getTransformationMat(); +} + +GLuint InventoryItem::getWorldLabelTexID(){ + return m_world_label->getTexID(); +} + +float InventoryItem::getWorldLabelTexAspect(){ + return m_world_label->getTextureScaleAspect(); +} + +std::shared_ptr<UIDisplay> InventoryItem::getUIDisplay(){ + return m_world_label; +} diff --git a/engine-ocean/Game/Systems/Inventory/inventoryitem.h b/engine-ocean/Game/Systems/Inventory/inventoryitem.h new file mode 100644 index 0000000..efa093b --- /dev/null +++ b/engine-ocean/Game/Systems/Inventory/inventoryitem.h @@ -0,0 +1,31 @@ +#ifndef INVENTORYITEM_H +#define INVENTORYITEM_H + + +#include "Game/Systems/UI/UITextures/uidisplay.h" +class InventoryItem +{ +public: + InventoryItem(const char* worldLabelFile, const char* inventoryLabelFile, + glm::vec2 worldLabelScale, + glm::vec2 inventoryLabelScale, + std::set<std::string>& m_shownScreens); + void updateWorldLabelPos(glm::vec2 pos); + glm::mat4 getWorldLabelTransformationMat(); + GLuint getWorldLabelTexID(); + float getWorldLabelTexAspect(); + std::shared_ptr<UIDisplay> getUIDisplay(); + + + + + +private: + std::shared_ptr<UIDisplay> m_world_label; + std::shared_ptr<UIDisplay> m_inventory_label; + + int m_inventoryCount = 0; + +}; + +#endif // INVENTORYITEM_H diff --git a/engine-ocean/Game/Systems/Inventory/inventorysystem.cpp b/engine-ocean/Game/Systems/Inventory/inventorysystem.cpp new file mode 100644 index 0000000..46f5558 --- /dev/null +++ b/engine-ocean/Game/Systems/Inventory/inventorysystem.cpp @@ -0,0 +1,206 @@ +#include "inventorysystem.h" +#include "Game/Components/CollisionComponents/CollisionComponent.h" +#include "Game/Components/drawcomponent.h" +#include "Game/Components/transformcomponent.h" +#include "Game/GameObjects/GameObject.h" +#include "Game/GameWorld.h" +#include <memory> + +InventorySystem::InventorySystem(std::map<std::string, std::shared_ptr<GameObject>>& dynamic_gameobjects, + std::map<std::string, std::shared_ptr<GameObject>>& all_gameobjects, + std::map<std::string, std::vector<std::shared_ptr<GameObject>>>& lootables, + std::map<int, Input>& input_map, + std::shared_ptr<Camera>& camera, + std::set<std::string>& m_shownScreens): + m_dynamic_gameobjects(dynamic_gameobjects), + m_input_map(input_map), + m_camera(camera), + m_lootables(lootables), + m_shownScreens(m_shownScreens), + m_all_gameobjects(all_gameobjects) +{ + int m_bamboo_count = 0; + m_screenVAO = Global::graphics.makeVAO(m_quadPos); + + //m_texID = Global::graphics.loadTextureFromFile("/Users/jesswan/Desktop/cs1950u/cs1950u-jjesswan/Resources/Images/enterbutton.png").textureID; + + + initializeInventory("mushroom", + "/Users/jesswan/Desktop/cs1950u/cs1950u-jjesswan/Resources/Images/mushroom_loot.png", + "/Users/jesswan/Desktop/cs1950u/cs1950u-jjesswan/Resources/Images/mushroom_loot.png"); + initializeInventory("amantia", + "/Users/jesswan/Desktop/cs1950u/cs1950u-jjesswan/Resources/Images/amantia_loot.png", + "/Users/jesswan/Desktop/cs1950u/cs1950u-jjesswan/Resources/Images/amantia_loot.png"); + + TextureData sparkle = Global::graphics.loadTextureFromFile("/Users/jesswan/Desktop/cs1950u/cs1950u-jjesswan/Resources/Images/sparkle.png"); + m_sparkle = std::make_shared<UIDisplay>(sparkle, glm::vec2(0.f), glm::vec2(.05f), m_shownScreens, AspectRatio::LAND_FIT); + +} + +InventorySystem::~InventorySystem(){ + glDeleteVertexArrays(1, &m_screenVAO); +} + +void InventorySystem::initializeInventory(std::string itemName, const char* label_filename, const char* inventory_filename){ + std::shared_ptr<InventoryItem> inventoryItem = std::make_shared<InventoryItem>(label_filename, inventory_filename, glm::vec2(.2f), glm::vec2(.5f), m_shownScreens); + m_inventoryItems.insert({itemName, inventoryItem}); +} + + +void InventorySystem::addToInventory(std::shared_ptr<GameObject> &go){ + // if G is pressed down, remove go from screen and add it to the inventory + if (m_input_map.at(GLFW_KEY_P).isActive){ + if (go->hasComponent<DrawComponent>()){ + go->removeComponent<DrawComponent>(); + go->removeComponent<CollisionComponent>(); + m_bamboo_count += 1; + } + } +} + +bool InventorySystem::withinPlayerReach(const std::shared_ptr<GameObject> &go){ + glm::vec3 player_pos = m_dynamic_gameobjects.at("player")->getComponent<TransformComponent>()->getPos(); + + // be careful --> the below line will crash if its attempting to get an object with multiple + // transforms, like the ground! + glm::vec3 go_pos = go->getComponent<TransformComponent>()->getPos(); + + float vicinity = 3.f; + // is player and game object are close in either x or z dir + if ((abs(player_pos.x - go_pos.x) < vicinity) + && (abs(player_pos.z - go_pos.z) < vicinity) + && (abs(player_pos.y - go_pos.y) < vicinity)){ + return true; + } + return false; +} + +void InventorySystem::drawUIText(){ + Global::graphics.drawUIText(Global::graphics.getFont("opensans"), std::to_string(m_bamboo_count) + " bamboo collected.", glm::ivec2(20, 90), AnchorPoint::TopLeft, Global::graphics.getFramebufferSize().x, 0.2f, 0.1f, glm::vec3(1, 1, 1)); + Global::graphics.drawUIText(Global::graphics.getFont("opensans"), "Collect 20 bamboos to clear the game.", glm::ivec2(20, 110), AnchorPoint::TopLeft, Global::graphics.getFramebufferSize().x, 0.2f, 0.1f, glm::vec3(1, 1, 1)); + + if (m_bamboo_count >= 20){ + Global::graphics.drawUIText(Global::graphics.getFont("opensans"), "You Won! Press 'R' to restart.", glm::ivec2(200, 300), AnchorPoint::TopLeft, Global::graphics.getFramebufferSize().x, 0.4f, 0.4f, glm::vec3(1, 1, 1)); + } +} + + + + +void InventorySystem::draw(){ + // bind shader + Global::graphics.bindShader("inventory"); + + // for window resizing + Global::graphics.setCameraData(m_camera); + + // enable alpha blending + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + // disable depth testing + glDisable(GL_DEPTH_TEST); + + // for every item in inventory, compute screen coordinates and draw if near player and on the screen + glm::mat4 proj = m_camera->getProjection(); + glm::mat4 view = m_camera->getView(); + + // if home page is rendered + if (m_shownScreens.contains("home")){ + glBindVertexArray(m_screenVAO); + glEnableVertexAttribArray(0); + + for (auto &lootID : m_lootables){ + for (auto &lootObject : lootID.second){ + glm::vec3 pos = lootObject->getComponent<TransformComponent>()->getMT()->getPos(); + glm::vec4 projected = proj*view*glm::vec4(pos, 1.f); + glm::vec2 screen = glm::vec2(projected.x/projected.w, projected.y/projected.w); + + // if visible on screen + if ((-1.f < screen.x && screen.x < 1.f) && (-1.f < screen.y && screen.y < 1.f)){ + + + + // update texture pos, and draw associated texture + if (withinPlayerReach(lootObject)){ + // update pos + m_inventoryItems[lootID.first]->updateWorldLabelPos(screen + m_offset); + + // get uniforms + glm::mat4 transMat = m_inventoryItems[lootID.first]->getWorldLabelTransformationMat(); + GLuint texID = m_inventoryItems[lootID.first]->getWorldLabelTexID(); + float texAspect = m_inventoryItems[lootID.first]->getWorldLabelTexAspect(); + + glActiveTexture(GL_TEXTURE6); + glBindTexture(GL_TEXTURE_2D, texID); + glm::vec2 texScale = glm::vec2(1.f, texAspect); + glUniform2f(glGetUniformLocation(Global::graphics.getShaderID("inventory"), "textureScale"), texScale.x, texScale.y); + glUniformMatrix4fv(glGetUniformLocation(Global::graphics.getShaderID("inventory"), "transform"), 1, GL_FALSE, glm::value_ptr(transMat[0])); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + } + // draw sparkle + else { + // update pos + m_sparkle->setPos(screen); + + // get uniforms + glm::mat4 transMat = m_sparkle->getTransformationMat(); + GLuint texID = m_sparkle->getTexID(); + float texAspect = m_sparkle->getTextureScaleAspect(); + + glActiveTexture(GL_TEXTURE6); + glBindTexture(GL_TEXTURE_2D, texID); + glm::vec2 texScale = glm::vec2(1.f/texAspect, 1.f); + glUniform2f(glGetUniformLocation(Global::graphics.getShaderID("inventory"), "textureScale"), texScale.x, texScale.y); + glUniformMatrix4fv(glGetUniformLocation(Global::graphics.getShaderID("inventory"), "transform"), 1, GL_FALSE, glm::value_ptr(transMat[0])); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + } + } + + } + + } + + glDisableVertexAttribArray(0); + glBindVertexArray(0); + } + glDisable(GL_BLEND); + glEnable(GL_DEPTH_TEST); +} + + +void InventorySystem::update(double deltaTime){ + + // remove from m_lootables to remove labels and drawing + for (auto &lootGroup : m_lootables){ + for (auto itr = lootGroup.second.begin(); itr != lootGroup.second.end();){ + auto& loot = *itr; + if (withinPlayerReach(loot) && m_input_map.at(GLFW_KEY_P).isActive){ + loot->removeComponent<DrawComponent>(); + itr = lootGroup.second.erase(itr); + } else { + ++itr; + } + } + } + + // remove game object entirely based on if its not drawn +// for(auto it = m_all_gameobjects.begin(); it != m_all_gameobjects.end();){ +// if (!it->second->hasComponent<DrawComponent>()){ +// it = m_all_gameobjects.erase(it); +// } +// } +} + +void InventorySystem::onWindowResize(int width, int height){ + for (auto &lootID : m_lootables){ + m_inventoryItems[lootID.first]->getUIDisplay()->setWindowPos(width, height); + + } +} + +void InventorySystem::scrollEvent(double distance){} +void InventorySystem::mousePosEvent(double xpos, double ypos){} + diff --git a/engine-ocean/Game/Systems/Inventory/inventorysystem.h b/engine-ocean/Game/Systems/Inventory/inventorysystem.h new file mode 100644 index 0000000..4305f68 --- /dev/null +++ b/engine-ocean/Game/Systems/Inventory/inventorysystem.h @@ -0,0 +1,78 @@ +#ifndef INVENTORYSYSTEM_H +#define INVENTORYSYSTEM_H + + +#include "Game/GameWorld.h" +#include <map> +#include "inventoryitem.h" + +//struct InventoryItem{ +// TextureData lootLabelUI; +// TextureData lootInventoryUI; +// int inventoryCount = 0; + +// InventoryItem(TextureData label, TextureData inventory): +// lootLabelUI(label), +// lootInventoryUI(inventory) +// {} +//}; + +class InventorySystem : public System +{ +public: + InventorySystem(std::map<std::string, std::shared_ptr<GameObject>>& gameobjects, + std::map<std::string, std::shared_ptr<GameObject>>& all_gameobjects, + std::map<std::string, std::vector<std::shared_ptr<GameObject>>>& lootables, + std::map<int, Input>& input_map, + std::shared_ptr<Camera>& camera, + std::set<std::string>& m_shownScreens); + ~InventorySystem(); + void draw() override; + void update(double deltaTime) override; + void scrollEvent(double distance) override; + void mousePosEvent(double xpos, double ypos) override; + void drawUIText(); + void onWindowResize(int width, int height); + + + +private: + void addToInventory(std::shared_ptr<GameObject> &go); + bool withinPlayerReach(const std::shared_ptr<GameObject> &go); + void initializeInventory(std::string itemName, const char* label_filename, const char* inventory_filename); + + + + + + int m_bamboo_count = 0; + std::map<std::string, std::shared_ptr<GameObject>>& m_dynamic_gameobjects; + + // maps loot id to game objects/associated textures + std::map<std::string, std::vector<std::shared_ptr<GameObject>>>& m_lootables; // holds position + std::map<std::string, std::shared_ptr<InventoryItem>> m_inventoryItems; + + std::map<int, Input>& m_input_map; + std::shared_ptr<Camera>& m_camera; + + //std::set<std::string> m_inventoryScreens; // stores ids of all scenes in which inventory is rendered + std::set<std::string>& m_shownScreens; + + std::vector<float> m_quadPos = { + -1.0f, 1.0f, + -1.0f, -1.0f, + 1.0f, 1.0f, + 1.0f, -1.0f + }; + + GLuint m_screenVAO; + GLuint m_texID; + + glm::vec2 m_offset = glm::vec2(.2f); + + std::shared_ptr<UIDisplay> m_sparkle; + + std::map<std::string, std::shared_ptr<GameObject>>& m_all_gameobjects; +}; + +#endif // INVENTORYSYSTEM_H diff --git a/engine-ocean/Game/Systems/Pathfinding/aimovementsystem.cpp b/engine-ocean/Game/Systems/Pathfinding/aimovementsystem.cpp new file mode 100644 index 0000000..8abbec8 --- /dev/null +++ b/engine-ocean/Game/Systems/Pathfinding/aimovementsystem.cpp @@ -0,0 +1,35 @@ +#include "aimovementsystem.h" +#include "Game/Components/TransformComponent.h" +#include "Game/Components/PathfindComponent.h" +#include "Game/Systems/AI/aibehaviorcomponent.h" + +AIMovementSystem::AIMovementSystem(std::map<std::string, std::shared_ptr<GameObject>>& dynamic_gameobjects, + std::map<std::string, std::shared_ptr<GameObject>>& rigid_gameobjects) : + m_dynamic_gameobjects(dynamic_gameobjects), + m_rigid_gameobjects(rigid_gameobjects) + +{ + // m_path = m_rigid_gameobjects.at("navmesh")->getComponent<PathfindComponent>()->getPath(glm::vec3(-0.58249, 0, -0.0210782), glm::vec3(19.5371, 0, 1.39167)); + +} + +TransformComponent* AIMovementSystem::getTransform(std::shared_ptr<GameObject> &go){ + return go->getComponent<TransformComponent>(); +} + + +void AIMovementSystem::update(double deltaTime){ + for (auto &go : m_dynamic_gameobjects){ + if (go.second->hasComponent<AIBehaviorComponent>()){ + go.second->getComponent<AIBehaviorComponent>()->update(deltaTime); + } + } +} + + + + +void AIMovementSystem::draw(){} +void AIMovementSystem::scrollEvent(double distance){} +void AIMovementSystem::mousePosEvent(double xpos, double ypos){} + diff --git a/engine-ocean/Game/Systems/Pathfinding/aimovementsystem.h b/engine-ocean/Game/Systems/Pathfinding/aimovementsystem.h new file mode 100644 index 0000000..e880e44 --- /dev/null +++ b/engine-ocean/Game/Systems/Pathfinding/aimovementsystem.h @@ -0,0 +1,34 @@ +#ifndef AIMOVEMENTSYSTEM_H +#define AIMOVEMENTSYSTEM_H +#include "Game/Components/TransformComponent.h" +#include "Game/Systems/Pathfinding/pathfinder.h" +#include "Game/Systems/system.h" + + +class AIMovementSystem : public System +{ +public: + AIMovementSystem(std::map<std::string, std::shared_ptr<GameObject>>& dynamic_gameobjects, + std::map<std::string, std::shared_ptr<GameObject>>& rigid_gameobjects); + void draw() override; + void update(double deltaTime) override; + void scrollEvent(double distance) override; + void mousePosEvent(double xpos, double ypos) override; + +private: + float gravitySimulation(float &initial_v, double deltaTime, float snapshot_time, float gravity); + TransformComponent* getTransform(std::shared_ptr<GameObject> &go); + + std::map<std::string, std::shared_ptr<GameObject>>& m_dynamic_gameobjects; + std::map<std::string, std::shared_ptr<GameObject>>& m_rigid_gameobjects; + + + float horiz_velocity = .005f; + float snapshot_time = 0.f; + + std::vector<glm::vec3> m_path; + + //std::shared_ptr<Pathfinder> m_pathfinder; +}; + +#endif // AIMOVEMENTSYSTEM_H diff --git a/engine-ocean/Game/Systems/Pathfinding/pathfinder.cpp b/engine-ocean/Game/Systems/Pathfinding/pathfinder.cpp new file mode 100644 index 0000000..35d67d2 --- /dev/null +++ b/engine-ocean/Game/Systems/Pathfinding/pathfinder.cpp @@ -0,0 +1,321 @@ +#include "pathfinder.h" +#include "glm/glm.hpp" +#include "glm/gtx/hash.hpp" + +#include <iostream> +#include <set> +#include <vector> + +// there can be multiple pathfinders for multiple environemnt meshes +// pathfinding/environment component for an environment mesh? +Pathfinder::Pathfinder(std::vector<glm::vec3> vertices, std::vector<glm::ivec3> triangles): + m_vertices(vertices), + m_triangles(triangles) +{ + initializeEdges(); +} + +// for entire scene, generate data structure that holds nav mesh +void Pathfinder::initializeEdges(){ + + std::map<std::pair<int,int>, TriangleEdge> temp_data; + + // indices of vertex pairs + std::vector<std::pair<int,int>> range = {std::make_pair(0, 1), + std::make_pair(1, 2), + std::make_pair(2, 0)}; + + // make edges for all triangles and populate temp_data map + for (glm::vec3 triangle : m_triangles){ + std::vector<std::pair<int,int>> keys; + for (auto index_pair : range ){ + // make ordered pair of the index of vertex held by triangle , for all edges + std::pair<int, int> key = makeOrderedPair(triangle[index_pair.first], triangle[index_pair.second]); + keys.push_back(key); + + if (temp_data.count(key) != 0){ + temp_data.at(key).count ++; + } else { + // make a new entry that populates the midpoint + TriangleEdge edge = {m_vertices[key.first], m_vertices[key.second]}; + temp_data.insert(std::make_pair(key, edge)); + } + } + + for (auto key : keys){ + // add the two neighbors for each key + for (auto neighbor_id : keys){ + if (key != neighbor_id){ + temp_data.at(key).adjacentEdges.insert(neighbor_id); + } + } + } + } + + std::set<std::pair<int,int>> interiorEdges; + + // remove exterior edges from adjacent lists + for (auto &entry : temp_data){ + // make new adjacency list + std::set<std::pair<int,int>> edited_adj_list; + + for (const auto adjNode : entry.second.adjacentEdges){ + // if adjacent node is an interior node, add to new adj list + if (temp_data.at(adjNode).count > 1){ + edited_adj_list.insert(adjNode); + } + } + + // reassign adj list + entry.second.adjacentEdges.swap(edited_adj_list); + } + + // add interior edges + for (const auto &entry : temp_data){ + if (entry.second.count > 1){ + ANode node; + node.adjacentNodes = entry.second.adjacentEdges; + node.pos = entry.second.midpoint; + m_navdata.insert(std::make_pair(entry.first, node)); + } + } + + +} + + +std::pair<int, int> Pathfinder::makeOrderedPair(int i, int j){ + std::pair<int, int> pair = std::pair(std::min(i,j), std::max(i,j)); + return pair; +} + +// traverse navmeshgraph +// A --> start ; B --> destination +std::vector<glm::vec3> Pathfinder::findPath(const glm::vec3 A, const glm::vec3 B){ + // start and end indices are double pair because no existing edge has two of the same vertices + m_startNodePos = A; + m_endNodePos = B; + + // initialize empty path + std::vector<glm::vec3> empty; + empty.push_back(glm::vec3(0.f)); + + // initialize start and ending node + ANode startNode, endNode; + startNode.pos = m_startNodePos; + endNode.pos = m_endNodePos; + startNode.Gcost = 0; + m_navdata.insert(std::pair(startNodeID, startNode)); + m_navdata.insert(std::pair(endNodeID, endNode)); + + + // calculate distance to end point for each item in map + for (auto &node : m_navdata){ + node.second.Hcost = getDistance(node.second.pos, B); + updateFCost(node.first); + } + + // determine if one or both start/end points are in navmesh or not + std::pair<bool, std::set<std::pair<int, int>>> validStartPoint = findEnclosingTriangle(A); + std::pair<bool, std::set<std::pair<int, int>>> validEndPoint = findEnclosingTriangle(B); + + // if neither or both points can be found in navmesh, end early + if (!validStartPoint.first || !validEndPoint.first){ + std::cout << "-- a point is not in navmesh" << std::endl; + return empty; + } + + // if both points are on same triangle, then return only the destination + if (validStartPoint.second == validEndPoint.second){ + std::vector<glm::vec3> destination; + destination.push_back(B); + std::cout << "-- SAME TRIANGLE" << std::endl; + return destination; + } + + std::set<std::pair<int,int>> open; + std::set<std::pair<int,int>> closed; + + // initialize open list + // check if id is a valid interior node + for (const auto &id : validStartPoint.second){ + if (m_navdata.contains(id)){ + m_navdata[id].previousV = startNodeID; + m_navdata[id].Gcost = getDistance(m_navdata[id].pos, m_startNodePos); + updateFCost(id); + open.insert(id); + } + } + + // calculate paths with Astar + bool reachable = traverseAStar(startNodeID, validEndPoint.second, open, closed); + + if (reachable){ + std::cout << "reachable!" << std::endl; + return getNavigablePath(startNodeID, endNodeID); + } + + // otherwise, return glm::vec3(0) so that entity doesnt move + std::cout << "not reachable" << std::endl; + return empty; +} + +void Pathfinder::updateFCost(const std::pair<int,int> &node){ + m_navdata[node].Fcost = m_navdata[node].Gcost + m_navdata[node].Hcost; + +} + +bool Pathfinder::traverseAStar(const std::pair<int,int> &currNodeID, const std::set<std::pair<int,int>> &endpointNodeIDs, + std::set<std::pair<int,int>> &open, std::set<std::pair<int,int>> &closed){ + + glm::vec3 currNodePos = m_navdata.at(currNodeID).pos; + + // base case: if looking at nodes that are way too far from entity start pos, then just end + if (getDistance(currNodePos, m_startNodePos) > m_maxDistance){ + std::cout << "-- DISTANCE TOO FAR" << std::endl; + return false; + } + + float lowestF = INFINITY; + float lowestH = INFINITY; + std::pair<int,int> C; // where C is the next node to visit + + for (const std::pair<int,int> &nodeID : open){ + // calculate G, which also populates F + // --------------- do i also need to check if the previous f cost was less than the updated f cost? + float new_Gcost = getDistance(m_navdata[nodeID].pos, currNodePos) + m_navdata[currNodeID].Gcost; + m_navdata[nodeID].Gcost = new_Gcost; + updateFCost(nodeID); + + if (m_navdata[nodeID].Fcost == lowestF){ + // go by lowest H if F costs are equal + if (m_navdata[nodeID].Hcost < lowestH){ + C = nodeID; + lowestF = m_navdata[nodeID].Fcost; + lowestH = m_navdata[nodeID].Hcost; + } + } + + if (m_navdata[nodeID].Fcost < lowestF){ + C = nodeID; + lowestF = m_navdata[nodeID].Fcost; + lowestH = m_navdata[nodeID].Hcost; + } + } + + // move C from open to closed list + open.erase(C); + closed.insert(C); + + //if C is equal to any of the endpoint indices, then path is found + for (const std::pair<int,int> &id : endpointNodeIDs){ + if (C == id){ + m_navdata[endNodeID].previousV = C; + return true; + } + } + + // for neighbor N of C + for (const std::pair<int,int> &N: m_navdata[C].adjacentNodes){ + if (closed.contains(N)) continue; + + // calculate potential F cost of C-->N + float new_Gcost = getDistance(m_navdata[N].pos, m_navdata[C].pos) + m_navdata[C].Gcost; + float new_Fcost = new_Gcost + m_navdata[N].Hcost; + + if (new_Fcost < m_navdata[N].Fcost || !open.contains(N)){ + // update N's G and F cost + m_navdata[N].Gcost = new_Gcost; + updateFCost(N); + // set previous node of N to be C + m_navdata[N].previousV = C; + + // add N to open if its not already in it + open.insert(N); + } + } + + // if there are still open nodes, visit the next one (recurse) + if (!open.empty()){ + traverseAStar(C, endpointNodeIDs, open, closed); + } + + // otherwise no more open nodes, thus a* has finished + return true; +} + + +// distance is the un-squarerooted distance between two points +float Pathfinder::getDistance(glm::vec3 a, glm::vec3 b){ + return pow(a.x-b.x, 2) + pow(a.y-b.y, 2) + pow(a.z-b.z, 2); +} + + +// referenced from https://blackpawn.com/texts/pointinpoly/default.html +bool Pathfinder::sameSide(glm::vec3 p1, glm::vec3 p2, glm::vec3 a, glm::vec3 b){ + glm::vec3 cp1 = glm::cross(b-a, p1-a); + glm::vec3 cp2 = glm::cross(b-a, p2-a); + + glm::vec3 a1 = glm::vec3(cp1.x, cp1.y + .001, cp1.z); + glm::vec3 a2 = glm::vec3(cp1.x, cp1.y - .001, cp1.z); + + glm::vec3 b1 = glm::vec3(cp2.x, cp2.y + .001, cp2.z); + glm::vec3 b2 = glm::vec3(cp2.x, cp2.y - .001, cp2.z); + + + if (glm::dot(a1, b1) >= 0 || glm::dot(a2, b2) >= 0 + || glm::dot(a1, b2) >= 0 || glm::dot(a2, b1) >= 0) return true; + return false; +} + +bool Pathfinder::pointInTriangle(glm::vec3 p, glm::vec3 v1, glm::vec3 v2, glm::vec3 v3){ + if (sameSide(p,v1, v2,v3) && sameSide(p,v2, v1,v3) && sameSide(p,v3, v1,v2)) return true; + return false; +} + +std::pair<bool, std::set<std::pair<int, int>>> Pathfinder::findEnclosingTriangle(glm::vec3 point){ + + // for each triangle in navmesh, get its vertices + for (glm::vec3 vertex_triple : m_triangles){ + // if point is in any navmesh triangle, then return the 3 nodes associated with that tri + if (pointInTriangle(point, m_vertices[vertex_triple[0]], m_vertices[vertex_triple[1]], m_vertices[vertex_triple[2]])){ + return std::make_pair(true, getEnclosingTriangleEdges(vertex_triple)); + } + } + + // if no triangles contained the point, return false + return std::make_pair(false, getEnclosingTriangleEdges(glm::vec3(0.f))); +} + +// gets node indices +std::set<std::pair<int, int>> Pathfinder::getEnclosingTriangleEdges(glm::vec3 indices){ + std::set<std::pair<int, int>> ids; + ids.insert(makeOrderedPair(indices[0], indices[1])); + ids.insert(makeOrderedPair(indices[1], indices[2])); + ids.insert(makeOrderedPair(indices[2], indices[0])); + + return ids; +} + +std::vector<glm::vec3> Pathfinder::getNavigablePath(const std::pair<int,int> &start, const std::pair<int,int> &end){ + // start at B, and use previousV pointers to get back to A + std::vector<glm::vec3> path; + path.push_back(m_navdata[end].pos); + + std::pair<int,int> currV = end; + + while (currV != start){ + // add current point to path + glm::vec3 pos = m_navdata[m_navdata[currV].previousV].pos; + path.push_back(pos); + + // reassign variable to where currV points, and enter loop again + currV = m_navdata[currV].previousV; + } + + // path where the very last entry is the start point, and the very first entry is the end point + return path; + +} + + diff --git a/engine-ocean/Game/Systems/Pathfinding/pathfinder.h b/engine-ocean/Game/Systems/Pathfinding/pathfinder.h new file mode 100644 index 0000000..d9abbd6 --- /dev/null +++ b/engine-ocean/Game/Systems/Pathfinding/pathfinder.h @@ -0,0 +1,83 @@ +#ifndef PATHFINDER_H +#define PATHFINDER_H + +#include "glm/fwd.hpp" +#include "glm/gtx/hash.hpp" + + +#include <map> +#include <set> +#include <vector> + +struct ANode{ + glm::vec3 pos; + // A* related: + float Hcost = 0; // distance to destination + float Gcost = 0; // distance from start to current node (accumulative G) + float Fcost = Gcost+Hcost; + std::pair<int,int> previousV; + std::set<std::pair<int,int>> adjacentNodes; +}; + +struct TriangleEdge{ + glm::vec3 midpoint; + std::set<std::pair<int,int>> adjacentEdges; + + TriangleEdge(glm::vec3 v1, glm::vec3 v2): + midpoint(.5f*(v2+v1)) + {} + // num of times it appears in the navgraph + int count = 1; +}; + +class Pathfinder +{ +public: + Pathfinder(std::vector<glm::vec3> vertices, std::vector<glm::ivec3> triangles); + std::vector<glm::vec3> findPath(const glm::vec3 A, const glm::vec3 B); + + + +private: + + void updateFCost(const std::pair<int,int> &node); + + + + + bool traverseAStar(const std::pair<int,int> &currNodeID, const std::set<std::pair<int,int>> &endpointNodeIDs, + std::set<std::pair<int,int>> &open, std::set<std::pair<int,int>> &closed); + + std::set<std::pair<int, int>> getEnclosingTriangleEdges(glm::vec3 indices); + + void initializeEdges(); + float getDistance(glm::vec3 a, glm::vec3 b); + std::vector<glm::vec3> getNavigablePath(const std::pair<int,int> &start, const std::pair<int,int> &end); + std::pair<bool, std::set<std::pair<int, int>>> findEnclosingTriangle(glm::vec3 point); + bool pointInTriangle(glm::vec3 p, glm::vec3 v1, glm::vec3 v2, glm::vec3 v3); + bool sameSide(glm::vec3 p1, glm::vec3 p2, glm::vec3 a, glm::vec3 b); + + std::set<std::vector<float>> getAdjacentNodes(const std::set<std::pair<int,int>> &id_pairs, std::map<std::pair<int,int>, TriangleEdge> &temp_data); + + + std::pair<int, int> makeOrderedPair(int i, int j); + + // maps pos (midpoint of a edge) to node + std::map<std::pair<int,int>, ANode> m_navdata; + std::vector<glm::vec3> m_vertices; + std::vector<glm::ivec3> m_triangles; + glm::vec3 m_startNodePos; + glm::vec3 m_endNodePos; + + std::pair<int,int> startNodeID = std::make_pair(0,0); + std::pair<int,int> endNodeID = std::make_pair(1,1); + + + float m_maxDistance = 500; // 20 meters max distance that an ai can pathfind + + + + +}; + +#endif // PATHFINDER_H diff --git a/engine-ocean/Game/Systems/UI/ButtonAction/buttonaction.cpp b/engine-ocean/Game/Systems/UI/ButtonAction/buttonaction.cpp new file mode 100644 index 0000000..c7314f6 --- /dev/null +++ b/engine-ocean/Game/Systems/UI/ButtonAction/buttonaction.cpp @@ -0,0 +1,6 @@ +#include "buttonaction.h" + +ButtonAction::ButtonAction() +{ + +} diff --git a/engine-ocean/Game/Systems/UI/ButtonAction/buttonaction.h b/engine-ocean/Game/Systems/UI/ButtonAction/buttonaction.h new file mode 100644 index 0000000..d6c1674 --- /dev/null +++ b/engine-ocean/Game/Systems/UI/ButtonAction/buttonaction.h @@ -0,0 +1,13 @@ +#ifndef BUTTONACTION_H +#define BUTTONACTION_H + + +class ButtonAction +{ +public: + ButtonAction(); + virtual void activate() = 0; + virtual void deactivate() = 0; +}; + +#endif // BUTTONACTION_H diff --git a/engine-ocean/Game/Systems/UI/ButtonAction/showwindowaction.cpp b/engine-ocean/Game/Systems/UI/ButtonAction/showwindowaction.cpp new file mode 100644 index 0000000..e6b9797 --- /dev/null +++ b/engine-ocean/Game/Systems/UI/ButtonAction/showwindowaction.cpp @@ -0,0 +1,33 @@ +#include "showwindowaction.h" +#include "Game/Systems/UI/uisystem.h" +#include <map> + +ShowWindowAction::ShowWindowAction(std::map<std::string, std::shared_ptr<UIScreen>>& all_screens, + std::set<std::string>& shownScreens, + const std::string screenName): + m_screens(all_screens), + m_shownScreens(shownScreens) +{ + m_screenName = screenName; +} + + + +void ShowWindowAction::activate(){ + std::cout << "activated window show!!!" << std::endl; + + // add screen to be rendered, and also set it be the only one active + //m_screens[m_screenName] = m_screen; +// for (auto &screen : m_screens){ +// screen.second->isActive = false; +// } + +// m_screens[m_screenName]->isActive = true; + m_shownScreens.insert(m_screenName); + m_screens[m_screenName]->isActive = true; + +} + +void ShowWindowAction::deactivate(){ + m_shownScreens.erase(m_screenName); +} diff --git a/engine-ocean/Game/Systems/UI/ButtonAction/showwindowaction.h b/engine-ocean/Game/Systems/UI/ButtonAction/showwindowaction.h new file mode 100644 index 0000000..781f093 --- /dev/null +++ b/engine-ocean/Game/Systems/UI/ButtonAction/showwindowaction.h @@ -0,0 +1,21 @@ +#ifndef SHOWWINDOWACTION_H +#define SHOWWINDOWACTION_H +#include "Game/Systems/UI/uisystem.h" +#include <map> +#include "buttonaction.h" + +class ShowWindowAction : public ButtonAction +{ +public: + ShowWindowAction(std::map<std::string, std::shared_ptr<UIScreen>>& screens, + std::set<std::string>& shownScreens, + const std::string screenName); + void activate() override; + void deactivate() override; +private: + std::set<std::string>& m_shownScreens; + std::string m_screenName; + std::map<std::string, std::shared_ptr<UIScreen>>& m_screens; +}; + +#endif // SHOWWINDOWACTION_H diff --git a/engine-ocean/Game/Systems/UI/UITextures/UIButton.h b/engine-ocean/Game/Systems/UI/UITextures/UIButton.h new file mode 100644 index 0000000..8cf02ee --- /dev/null +++ b/engine-ocean/Game/Systems/UI/UITextures/UIButton.h @@ -0,0 +1,94 @@ +#ifndef UIBUTTON_H +#define UIBUTTON_H +#include "Game/Systems/UI/ButtonAction/buttonaction.h" +#include "Game/Systems/UI/UITextures/uidisplay.h" +#include "Graphics/global.h" +#include <GLFW/glfw3.h> +#include <set> +#include "uitexture.h" + +enum CornerPosition { + TOPLEFT, + TOPRIGHT, + BOTTOMLEFT, + BOTTOMRIGHT, + NONE +}; + +class UIButton : public UITexture +{ +public: + UIButton(TextureData tex, glm::vec2 pos, glm::vec2 scale, std::set<std::string>& shownScreens, + bool isCloseButton = false, CornerPosition corner = NONE, AspectRatio ratio = LAND_FIT); + ~UIButton(); + void draw() override; + GLuint getTexID() override; + glm::vec2 getPos() override; + glm::vec2 getScale() override; + float getTextureRatio() override; + AspectRatio getAspectRatio(); + + + + void setWindowPos(int width, int height) override; + glm::vec2 getWindowPos(); + + int getHeight() override; + int getWidth() override; + Bounds2f getBounds() override; + float getTextureScaleAspect() override; + + void addButtonAction(std::shared_ptr<ButtonAction> &action); + bool onButtonPress(); + void setWindowToClose(std::string windowID); + + void setParentDisplay(std::string parent); + std::string getParentDisplay(); + + CornerPosition getCornerPos(); + bool hasCornerPos = false; + void setToCorner(const CornerPosition corner, int width, int height); + + void setTransformationMat(glm::vec2 translation, glm::vec2 scale); + glm::mat4 getTransformationMat(); + + + +private: + glm::mat4 getScaleMatrix(glm::vec2 scale); + int getScreenHeight(); + int getScreenWidth(); + void setTexID(GLuint &newTexID); + + + TextureData m_tex; + glm::vec2 m_pos; + glm::vec2 m_scale; + Bounds2f m_bounds; + glm::vec2 m_windowPos; // width, height + float m_windowHeight = 480.f; + float m_windowWidth = 640.f; + float m_toScreenScale = 1.f; + + int m_screenImageHeight; + int m_screenImageWidth; + float m_tex_aspectRatio = 1.f; + bool m_isCloseButton = false; + std::string m_attachedWindow; + + std::vector<std::shared_ptr<ButtonAction>> m_actions; + std::set<std::string>& m_shownScreens; + + CornerPosition m_cornerPos; + std::string m_parentDisplay; + + glm::mat4 m_transformationMat; + float m_textureAspect = 1.f; + + AspectRatio m_aspectRatio; + + + +}; + +#endif // UIButton_H diff --git a/engine-ocean/Game/Systems/UI/UITextures/uibutton.cpp b/engine-ocean/Game/Systems/UI/UITextures/uibutton.cpp new file mode 100644 index 0000000..031be2f --- /dev/null +++ b/engine-ocean/Game/Systems/UI/UITextures/uibutton.cpp @@ -0,0 +1,202 @@ +#include "UIButton.h" +#include "Game/Systems/UI/UITextures/UIDisplay.h" +#include <set> + +UIButton::UIButton(TextureData tex, glm::vec2 pos, glm::vec2 scale, std::set<std::string>& shownScreens, + bool isCloseButton, CornerPosition corner, AspectRatio ratio): + m_tex(tex), + m_shownScreens(shownScreens), + m_aspectRatio(ratio) +{ + + // set variables + m_isCloseButton = isCloseButton; + m_pos = pos; + m_scale = scale; + m_tex_aspectRatio = static_cast<float>(m_tex.height)/static_cast<float>(m_tex.width); + + setToCorner(corner, 640, 480); + setWindowPos(640, 480); + setTransformationMat(m_pos, scale); + +} +UIButton::~UIButton(){ + glDeleteTextures(1, &m_tex.textureID); +} + + +AspectRatio UIButton::getAspectRatio(){ + return m_aspectRatio; +} + +glm::mat4 UIButton::getScaleMatrix(glm::vec2 scale) { + glm::mat4 M = glm::mat4(1.f); + M[0][0] = scale.x; //* (m_screenImageHeight/m_screenImageWidth); + M[1][1] = scale.y; //* (m_tex_aspectRatio); + M[2][2] = 1.f; + return M; +} + +void UIButton::setTransformationMat(glm::vec2 translation, glm::vec2 scale){ + glm::mat4 transMat = glm::mat4(1.f); + transMat[3] = glm::vec4(translation.x, translation.y, 0.f, 1.f); + glm::mat4 scaleMat = getScaleMatrix(glm::vec2(scale)); + m_transformationMat = transMat*scaleMat; +} + +glm::mat4 UIButton::getTransformationMat(){ + return m_transformationMat; +} + +float UIButton::getTextureRatio(){ + return m_tex_aspectRatio; +} + + +void UIButton::draw(){} + +GLuint UIButton::getTexID(){ + return m_tex.textureID; +} + +glm::vec2 UIButton::getPos(){ + return m_pos; +} + +glm::vec2 UIButton::getScale(){ + return m_scale; +} + +//void UIButton::setTexID(GLuint &newTexID){ +// m_tex.textureID = newTexID; +//} + +void UIButton::setWindowPos(int width, int height){ + m_windowHeight = static_cast<float>(height); + m_windowWidth = static_cast<float>(width); + + // find where on window it is, for bound checking + float xpos = .5f*(m_pos.x + 1.f)*m_windowWidth; + float ypos = (1.f-.5f*(m_pos.y + 1.f))*m_windowHeight; + m_windowPos = glm::vec2(xpos, ypos); + + // set everything according to window dimensions -- this is for bound checking + m_toScreenScale = m_windowHeight*m_scale.y; + m_screenImageHeight = m_toScreenScale; + m_screenImageWidth = m_toScreenScale * m_tex_aspectRatio; + + float windowRatio = m_windowHeight/m_windowWidth; + m_textureAspect = windowRatio / m_tex_aspectRatio; + + // calculate window bounds + glm::vec2 halfDimensions = glm::vec2(m_screenImageWidth, m_screenImageHeight)*.5f; + m_bounds.max = glm::vec2(m_windowPos.x + halfDimensions.x, m_windowPos.y - halfDimensions.y); + m_bounds.min = glm::vec2(m_windowPos.x - halfDimensions.x, m_windowPos.y + halfDimensions.y); +} + +float UIButton::getTextureScaleAspect(){ + return m_textureAspect; +} + +void UIButton::setParentDisplay(std::string parent){ + m_parentDisplay = parent; + +} + +std::string UIButton::getParentDisplay(){ + return m_parentDisplay; + +} + +CornerPosition UIButton::getCornerPos(){ + return m_cornerPos; + +} + +void UIButton::setToCorner(const CornerPosition corner, int width, int height){ + + m_toScreenScale = static_cast<float>(height)*m_scale.y; + m_screenImageHeight = m_toScreenScale; + m_screenImageWidth = m_toScreenScale * m_tex_aspectRatio; + + // in texture space + float xtrans = m_screenImageWidth/(.5f*static_cast<float>(width)); + float ytrans = m_screenImageHeight/(.5f*static_cast<float>(height)); + + // find where window pos should be + switch(corner){ + case TOPLEFT: + m_pos = glm::vec2(-1+xtrans, 1-ytrans); + + break; + case TOPRIGHT: + m_pos = glm::vec2(1-xtrans, 1-ytrans); + + break; + case BOTTOMLEFT: + m_pos = glm::vec2(-1+xtrans, -1+ytrans); + + break; + case BOTTOMRIGHT: + m_pos = glm::vec2(1-xtrans, -1+ytrans); + break; + default: + break; + } +} + +glm::vec2 UIButton::getWindowPos(){ + return m_windowPos; +} + +int UIButton::getHeight(){ + return m_tex.height; +} +int UIButton::getWidth(){ + return m_tex.width; +} + +int UIButton::getScreenHeight(){ + return m_screenImageHeight; +} +int UIButton::getScreenWidth(){ + return m_screenImageWidth; +} + +// remember that origin is top left corner!! +Bounds2f UIButton::getBounds(){ + + return m_bounds; +} + +void UIButton::addButtonAction(std::shared_ptr<ButtonAction> &action){ + m_actions.push_back(action); +} + +//////*/ for close button only +void UIButton::setWindowToClose(std::string windowID){ + if (m_isCloseButton){ + m_attachedWindow = windowID; + } + +} + +bool UIButton::onButtonPress(){ + // if button is a close button, then deactivate everything + if (m_isCloseButton){ + std::cout << "shownWindowSize: " << m_shownScreens.size() << std::endl; + //m_shownScreens.erase(m_attachedWindow); + std::cout << "new shownWindowSize: " << m_shownScreens.size() << std::endl; + std::cout << "CLOSE WINDOW: " << m_attachedWindow << std::endl; + return true; + + + } else { + for (auto &action : m_actions){ + action->activate(); + } + return false; + } + +} + diff --git a/engine-ocean/Game/Systems/UI/UITextures/uidisplay.cpp b/engine-ocean/Game/Systems/UI/UITextures/uidisplay.cpp new file mode 100644 index 0000000..2ddaf62 --- /dev/null +++ b/engine-ocean/Game/Systems/UI/UITextures/uidisplay.cpp @@ -0,0 +1,130 @@ +#include "uidisplay.h" +#include <set> + +UIDisplay::UIDisplay(TextureData tex, glm::vec2 pos, glm::vec2 scale, + std::set<std::string>& shownScreens, + AspectRatio ratio): + m_tex(tex), + m_shownScreens(shownScreens), + m_aspectRatio(ratio) +{ + + m_pos = (pos); + m_scale = (scale); + m_tex_aspectRatio = static_cast<float>(m_tex.height)/static_cast<float>(m_tex.width); + setTransformationMat(pos, scale); +// std::cout << "tex aspect ratio:" << m_tex_aspectRatio << std::endl; + +// std::cout << "aspect ratio w: " << m_tex.width << std::endl; +// std::cout << "aspect ratio h: " << m_tex.height << std::endl; + + + setWindowPos(640, 480); +// std::cout << "screen image height: " << m_screenImageHeight << std::endl; +// std::cout << "screen image width: " << m_screenImageWidth << std::endl; + +} + +UIDisplay::~UIDisplay(){ + glDeleteTextures(1, &m_tex.textureID); +} + +void UIDisplay::draw(){} + +GLuint UIDisplay::getTexID(){ + return m_tex.textureID; +} + +glm::vec2 UIDisplay::getPos(){ + return m_pos; +} + +glm::vec2 UIDisplay::getScale(){ + return m_scale; +} + +AspectRatio UIDisplay::getAspectRatio(){ + return m_aspectRatio; +} + +void UIDisplay::setWindowPos(int width, int height){ + float xpos = .5f*(m_pos.x + 1.f)*static_cast<float>(width); + float ypos = (1.f-.5f*(m_pos.y + 1.f))*static_cast<float>(height); + m_windowPos = glm::vec2(xpos, ypos); + + // set everything according to window dimensions + m_toScreenScale = static_cast<float>(height)*m_scale.y; + m_screenImageHeight = m_toScreenScale; + m_screenImageWidth = m_toScreenScale * m_tex_aspectRatio; + + float windowRatio = static_cast<float>(height)/static_cast<float>(width); + m_textureAspect = windowRatio / m_tex_aspectRatio; +} + +void UIDisplay::setPos(glm::vec2 pos){ + m_pos = pos; + setTransformationMat(m_pos, m_scale); +} +void UIDisplay::setScale(glm::vec2 scale){ + m_scale = scale; + setTransformationMat(m_pos, m_scale); +} + +float UIDisplay::getTextureScaleAspect(){ + return m_textureAspect; +} + +glm::vec2 UIDisplay::getWindowPos(){ + return m_windowPos; +} + +int UIDisplay::getHeight(){ + return m_tex.height; +} +int UIDisplay::getWidth(){ + return m_tex.width; +} + +int UIDisplay::getScreenHeight(){ + return m_screenImageHeight; +} +int UIDisplay::getScreenWidth(){ + return m_screenImageWidth; +} + +glm::mat4 UIDisplay::getScaleMatrix(glm::vec2 scale) { + glm::mat4 M = glm::mat4(1.f); + M[0][0] = scale.x;//* (m_screenImageHeight/m_screenImageWidth); + M[1][1] = scale.y; //* (m_tex_aspectRatio); + M[2][2] = 1.f; + return M; +} + +void UIDisplay::setTransformationMat(glm::vec2 translation, glm::vec2 scale){ + glm::mat4 transMat = glm::mat4(1.f); + transMat[3] = glm::vec4(translation.x, translation.y, 0.f, 1.f); + + glm::mat4 scaleMat = getScaleMatrix(glm::vec2(scale)); + + m_transformationMat = transMat*scaleMat; +} + +glm::mat4 UIDisplay::getTransformationMat(){ + return m_transformationMat; +} + +// remember that origin is top left corner!! +Bounds2f UIDisplay::getBounds(){ + glm::vec2 halfDimensions = glm::vec2(m_screenImageWidth, m_screenImageHeight)*.5f; + m_bounds.max = glm::vec2(m_windowPos.x + halfDimensions.x, m_windowPos.y - halfDimensions.y); + m_bounds.min = glm::vec2(m_windowPos.x - halfDimensions.x, m_windowPos.y + halfDimensions.y); + return m_bounds; +} + +float UIDisplay::getTextureRatio(){ + return m_tex_aspectRatio; +} + + + + diff --git a/engine-ocean/Game/Systems/UI/UITextures/uidisplay.h b/engine-ocean/Game/Systems/UI/UITextures/uidisplay.h new file mode 100644 index 0000000..4b739cc --- /dev/null +++ b/engine-ocean/Game/Systems/UI/UITextures/uidisplay.h @@ -0,0 +1,67 @@ +#ifndef UIDISPLAY_H +#define UIDISPLAY_H +#include "uitexture.h" +#include <set> + + + +class UIDisplay : public UITexture +{ +public: + UIDisplay(TextureData tex, glm::vec2 pos, glm::vec2 scale, std::set<std::string>& shownScreens, + AspectRatio ratio = FIT_SCREEN); + ~UIDisplay(); + void draw() override; + GLuint getTexID() override; + glm::vec2 getPos() override; + glm::vec2 getScale() override; + + void setWindowPos(int width, int height) override; + glm::vec2 getWindowPos(); + + int getHeight() override; + int getWidth() override; + Bounds2f getBounds() override; + float getTextureRatio() override; + float getTextureScaleAspect() override; + void setPos(glm::vec2 pos); + void setScale(glm::vec2 scale); + + AspectRatio getAspectRatio(); + + + // glm::vec2 getCornerPos(CornerPosition corner, glm::vec2 elementDimensions); + void setTransformationMat(glm::vec2 translation, glm::vec2 scale); + glm::mat4 getTransformationMat(); + +private: + int getScreenHeight(); + int getScreenWidth(); + void setTexID(GLuint &newTexID); + glm::mat4 getScaleMatrix(glm::vec2 scale); + + + + TextureData m_tex; + glm::vec2 m_pos; + glm::vec2 m_scale; + Bounds2f m_bounds; + glm::vec2 m_windowPos; // width, height + int m_windowHeight = 480.f; + int m_windowWidth = 640.f; + float m_toScreenScale = 1.f; + + int m_screenImageHeight; + int m_screenImageWidth; + float m_tex_aspectRatio = 1.f; + bool m_isCloseButton = false; + std::string m_attachedWindow; + + std::set<std::string>& m_shownScreens; + glm::mat4 m_transformationMat; + float m_textureAspect = 1.f; + + AspectRatio m_aspectRatio; +}; + +#endif // UIDISPLAY_H diff --git a/engine-ocean/Game/Systems/UI/UITextures/uitexture.cpp b/engine-ocean/Game/Systems/UI/UITextures/uitexture.cpp new file mode 100644 index 0000000..8680baf --- /dev/null +++ b/engine-ocean/Game/Systems/UI/UITextures/uitexture.cpp @@ -0,0 +1,6 @@ +#include "uitexture.h" + +UITexture::UITexture() +{ + +} diff --git a/engine-ocean/Game/Systems/UI/UITextures/uitexture.h b/engine-ocean/Game/Systems/UI/UITextures/uitexture.h new file mode 100644 index 0000000..53e429d --- /dev/null +++ b/engine-ocean/Game/Systems/UI/UITextures/uitexture.h @@ -0,0 +1,39 @@ +#ifndef UITEXTURE_H +#define UITEXTURE_H +#include "Graphics/global.h" +#include <GLFW/glfw3.h> + +struct Bounds2f{ + glm::vec2 min; + glm::vec2 max; +}; + +enum AspectRatio { + LAND_FIT, + LAND_FILL, + PORTRAIT_FIT, + PORTRAIT_FILL, + FIT_SCREEN +}; + +class UITexture +{ +public: + UITexture(); + virtual void draw() = 0; + virtual GLuint getTexID() = 0; + virtual glm::vec2 getPos() = 0; + virtual glm::vec2 getScale() = 0; + virtual int getHeight() = 0; + virtual int getWidth() = 0; + virtual Bounds2f getBounds() = 0; + virtual void setWindowPos(int width, int height) = 0; + virtual float getTextureRatio() = 0; + virtual float getTextureScaleAspect() = 0; + + + + +}; + +#endif // UITEXTURE_H diff --git a/engine-ocean/Game/Systems/UI/uielement.cpp b/engine-ocean/Game/Systems/UI/uielement.cpp new file mode 100644 index 0000000..bb7d91f --- /dev/null +++ b/engine-ocean/Game/Systems/UI/uielement.cpp @@ -0,0 +1,6 @@ +#include "uielement.h" + +UIElement::UIElement() +{ + +} diff --git a/engine-ocean/Game/Systems/UI/uielement.h b/engine-ocean/Game/Systems/UI/uielement.h new file mode 100644 index 0000000..c2c3a2e --- /dev/null +++ b/engine-ocean/Game/Systems/UI/uielement.h @@ -0,0 +1,40 @@ +#ifndef UIELEMENT_H +#define UIELEMENT_H + + +#include "Game/Systems/UI/UITextures/uitexture.h" +#include "Game/TypeMap.h" +#include <memory> +class UIElement +{ +public: + UIElement(); + + template <typename T> + void addComponent(std::unique_ptr<T> &&component){ + m_components.put<T>(std::forward<std::unique_ptr<T>>(component)); + } + + template <typename T> + bool hasComponent(){ + return m_components.contains<T>(); + } + + template <class T> + T* getComponent(){ + auto comp = m_components.find<T>(); + assert(comp != m_components.end()); + return static_cast<T*>(comp->second.get()); + } + + template <class T> + void removeComponent(){ + m_components.remove<T>(); + } + +private: + + TypeMap<std::unique_ptr<UITexture>> m_components; +}; + +#endif // UIELEMENT_H diff --git a/engine-ocean/Game/Systems/UI/uisystem.cpp b/engine-ocean/Game/Systems/UI/uisystem.cpp new file mode 100644 index 0000000..09aff49 --- /dev/null +++ b/engine-ocean/Game/Systems/UI/uisystem.cpp @@ -0,0 +1,383 @@ +#include "uisystem.h" +#include "Game/GameWorld.h" +#include "Game/Systems/UI/ButtonAction/showwindowaction.h" +#include "Game/Systems/UI/UITextures/UIButton.h" +#include "Game/Systems/UI/UITextures/uidisplay.h" +#define GLFW_POINTING_HAND_CURSOR + +UISystem::UISystem(std::shared_ptr<Camera> camera, + std::map<int, Input>& input_map, + std::set<std::string>& shownScreens): + m_camera(camera), + m_input_map(input_map), + m_shownScreens(shownScreens) +{ + + + initializeStartScreen(); + initializeScreenMap(); + //m_pointerCursor = glfwCreateStandardCursor(GLFW_POINTING_HAND_CURSOR); + +} + +UISystem::~UISystem(){ + for (auto &screenID : m_all_screens){ + glDeleteVertexArrays(1, &screenID.second->screenVAOID); + } +} + +void UISystem::initializeScreenMap(){ + initializeProfileScreen(); + initializeInventory(); + initializeScreen(); + + m_shownScreens.insert("home"); + +} + +void UISystem::initializeScreen(){ + std::shared_ptr<UIScreen> home = std::make_shared<UIScreen>(); + home->isActive = true; + home->screenVAOID = Global::graphics.makeVAO(m_quadPos); + m_quad_numVertices = 4; + + float topLevel = .85f; + + makeButtonElement(home, "profileButton", "/Users/jesswan/Desktop/cs1950u/cs1950u-jjesswan/Resources/Images/profileicon.png", glm::vec2(.85f,topLevel), glm::vec2(.05f), AspectRatio::LAND_FILL); + std::shared_ptr<ButtonAction> showProfile = std::make_shared<ShowWindowAction>(m_all_screens, m_shownScreens, "profile"); + home->screenElements.at("profileButton")->getComponent<UIButton>()->addButtonAction(showProfile); + + makeButtonElement(home, "inventoryButton", "/Users/jesswan/Desktop/cs1950u/cs1950u-jjesswan/Resources/Images/inventoryicon.png", glm::vec2(.75f, topLevel), glm::vec2(.05f), AspectRatio::LAND_FILL); + std::shared_ptr<ButtonAction> showInv = std::make_shared<ShowWindowAction>(m_all_screens, m_shownScreens, "inventory"); + home->screenElements.at("inventoryButton")->getComponent<UIButton>()->addButtonAction(showInv); + + makeButtonElement(home, "questsButton", "/Users/jesswan/Desktop/cs1950u/cs1950u-jjesswan/Resources/Images/questicon.png", glm::vec2(.65f, topLevel), glm::vec2(.05f), AspectRatio::LAND_FILL); + makeButtonElement(home, "settingsButton", "/Users/jesswan/Desktop/cs1950u/cs1950u-jjesswan/Resources/Images/settings_icon.png", glm::vec2(-.85f, topLevel), glm::vec2(.05f), AspectRatio::LAND_FILL); + makeDisplayElement(home, "healthbar", "/Users/jesswan/Desktop/cs1950u/cs1950u-jjesswan/Resources/Images/healthbar.png", glm::vec2(0.f, -.5f), glm::vec2(.4f), AspectRatio::LAND_FIT); + +// TextureData reflectTex; +// reflectTex.textureID = Global::graphics.getReflectionTexture(); +// reflectTex.width = Global::graphics.REFLECTION_W; +// reflectTex.height = Global::graphics.REFLECTION_H; +// makeDisplayElement(home, "reflection", reflectTex, glm::vec2(.5f, -.5f), glm::vec2(.3f), AspectRatio::LAND_FIT); + +// TextureData refractTex; +// refractTex.textureID = Global::graphics.getRefractionTexture(); +// refractTex.width = Global::graphics.REFRACTION_W; +// refractTex.height = Global::graphics.REFRACTION_H; +// makeDisplayElement(home, "refract", refractTex, glm::vec2(-.5f, -.5f), glm::vec2(.3f), AspectRatio::LAND_FIT); + + + + + home->elementsDepthOrder.push_back("profileButton"); + home->elementsDepthOrder.push_back("inventoryButton"); + home->elementsDepthOrder.push_back("questsButton"); + home->elementsDepthOrder.push_back("settingsButton"); + home->elementsDepthOrder.push_back("healthbar"); + // home->elementsDepthOrder.push_back("reflection"); + // home->elementsDepthOrder.push_back("refract"); + + + + m_all_screens.insert({"home", home}); +} + +void UISystem::initializeProfileScreen(){ + + std::shared_ptr<UIScreen> profile = std::make_shared<UIScreen>(); + profile->isActive = false; + profile->screenVAOID = Global::graphics.makeVAO(m_quadPos); + m_quad_numVertices = 4; + + float topLevel = .85f; + + // eventually should be display, not button + makeDisplayElement(profile, "bg", "/Users/jesswan/Desktop/cs1950u/cs1950u-jjesswan/Resources/Images/inventory_bg.png", glm::vec2(0.f), glm::vec2(1.0f)); + makeDisplayElement(profile, "profileDisplay", "/Users/jesswan/Desktop/cs1950u/cs1950u-jjesswan/Resources/Images/mouse_profile.png", glm::vec2(0.f), glm::vec2(1.0f), AspectRatio::LAND_FIT); + makeButtonElement(profile, "closeButton", "/Users/jesswan/Desktop/cs1950u/cs1950u-jjesswan/Resources/Images/close_icon.png", glm::vec2(0.f), glm::vec2(.05f), AspectRatio::LAND_FILL, true, CornerPosition::TOPRIGHT); + profile->screenElements.at("closeButton")->getComponent<UIButton>()->setWindowToClose("profile"); + + profile->elementsDepthOrder.push_back("bg"); + profile->elementsDepthOrder.push_back("profileDisplay"); + profile->elementsDepthOrder.push_back("closeButton"); + + m_all_screens.insert({"profile", profile}); +} + +void UISystem::initializeInventory(){ + std::shared_ptr<UIScreen> inv = std::make_shared<UIScreen>(); + inv->isActive = false; + inv->screenVAOID = Global::graphics.makeVAO(m_quadPos); + m_quad_numVertices = 4; + + makeDisplayElement(inv, "bg", "/Users/jesswan/Desktop/cs1950u/cs1950u-jjesswan/Resources/Images/inventory_bg.png", glm::vec2(0.f), glm::vec2(1.0f)); + makeDisplayElement(inv, "inventoryDisplay", "/Users/jesswan/Desktop/cs1950u/cs1950u-jjesswan/Resources/Images/inventory_page.png", glm::vec2(0.f), glm::vec2(1.0f), AspectRatio::LAND_FIT); + makeButtonElement(inv, "closeButton", "/Users/jesswan/Desktop/cs1950u/cs1950u-jjesswan/Resources/Images/close_icon.png", glm::vec2(0.f), glm::vec2(.05f), AspectRatio::LAND_FILL, true, CornerPosition::TOPRIGHT); + inv->screenElements.at("closeButton")->getComponent<UIButton>()->setWindowToClose("inventory"); + inv->elementsDepthOrder.push_back("bg"); + inv->elementsDepthOrder.push_back("inventoryDisplay"); + inv->elementsDepthOrder.push_back("closeButton"); + + m_all_screens.insert({"inventory", inv}); +} + +void UISystem::initializeStartScreen(){ + + std::shared_ptr<UIScreen> start = std::make_shared<UIScreen>(); + start->isActive = true; + start->screenVAOID = Global::graphics.makeVAO(m_quadPos); + + float topLevel = .85f; + + // eventually should be display, not button + makeDisplayElement(start, "startDisplay", "/Users/jesswan/Desktop/cs1950u/cs1950u-jjesswan/Resources/Images/title.png", glm::vec2(0.f), glm::vec2(1.0f), AspectRatio::LAND_FIT); + makeButtonElement(start, "startButton", "/Users/jesswan/Desktop/cs1950u/cs1950u-jjesswan/Resources/Images/enterbutton.png", glm::vec2(0.f, -.57f), glm::vec2(.19f), AspectRatio::LAND_FIT, true); + start->screenElements.at("startButton")->getComponent<UIButton>()->setWindowToClose("start"); + + start->elementsDepthOrder.push_back("startDisplay"); + start->elementsDepthOrder.push_back("startButton"); + + m_all_screens.insert({"start", start}); + m_shownScreens.insert("start"); +} + +glm::vec2 UISystem::drawAspect(AspectRatio aspectType, float textureAspect){ + switch(aspectType){ + case FIT_SCREEN: + return glm::vec2(1.f); + break; + case LAND_FIT:case PORTRAIT_FILL: + return glm::vec2(1.f, textureAspect); + break; + case LAND_FILL: case PORTRAIT_FIT: + return glm::vec2(1.f/textureAspect, 1.f); + break; + } +} + + + +void UISystem::makeButtonElement(std::shared_ptr<UIScreen> &screen, + std::string elementName, const char* filename, + const glm::vec2 pos, const glm::vec2 scale, + AspectRatio aspectRatio, + bool isCloseButton, + CornerPosition corner){ + TextureData tex = Global::graphics.loadTextureFromFile(filename); + + std::shared_ptr<UIElement> uiElement = std::make_shared<UIElement>(); + + uiElement->addComponent<UIButton>(std::make_unique<UIButton>(tex, pos, scale, m_shownScreens, isCloseButton, corner, aspectRatio)); + screen->screenElements.insert({elementName, uiElement}); +} + +void UISystem::makeDisplayElement(std::shared_ptr<UIScreen> &screen, + std::string elementName, const char* filename, + const glm::vec2 pos, const glm::vec2 scale, + AspectRatio aspectRatio){ + TextureData tex = Global::graphics.loadTextureFromFile(filename); + + std::shared_ptr<UIElement> uiElement = std::make_shared<UIElement>(); + uiElement->addComponent<UIDisplay>(std::make_unique<UIDisplay>(tex, pos, scale, m_shownScreens, aspectRatio)); + screen->screenElements.insert({elementName, uiElement}); +} + +void UISystem::makeDisplayElement(std::shared_ptr<UIScreen> &screen, + std::string elementName, TextureData &tex, + const glm::vec2 pos, const glm::vec2 scale, + AspectRatio aspectRatio){ + + std::shared_ptr<UIElement> uiElement = std::make_shared<UIElement>(); + uiElement->addComponent<UIDisplay>(std::make_unique<UIDisplay>(tex, pos, scale, m_shownScreens, aspectRatio)); + screen->screenElements.insert({elementName, uiElement}); +} + +void UISystem::renderScreen(){ + // bind shader + Global::graphics.bindShader("ui"); + + // for window resizing + Global::graphics.setCameraData(m_camera); + + // enable alpha blending + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + // disable depth testing + glDisable(GL_DEPTH_TEST); + + for (auto &screenID : m_shownScreens){ + // bind quad + glBindVertexArray(m_all_screens[screenID]->screenVAOID); + glEnableVertexAttribArray(0); + + // loop through inventory ui elements + + // loop through ui elements + for (auto &gui: m_all_screens[screenID]->elementsDepthOrder){ + if (m_all_screens[screenID]->screenElements[gui]->hasComponent<UIDisplay>()){ + GLuint texID; + if (gui == "reflection"){ + //std::cout << "rendering reflection" << std::endl; + texID = Global::graphics.getReflectionTexture(); + } else if (gui == "refract"){ + texID = Global::graphics.getRefractionTexture(); + } else { + texID = m_all_screens[screenID]->screenElements[gui]->getComponent<UIDisplay>()->getTexID(); + } + glm::mat4 transMat = m_all_screens[screenID]->screenElements[gui]->getComponent<UIDisplay>()->getTransformationMat(); + float texAspect = m_all_screens[screenID]->screenElements[gui]->getComponent<UIDisplay>()->getTextureScaleAspect(); + AspectRatio ratio = m_all_screens[screenID]->screenElements[gui]->getComponent<UIDisplay>()->getAspectRatio(); + + glActiveTexture(GL_TEXTURE5); + glBindTexture(GL_TEXTURE_2D, texID); + + glm::vec2 texScale = drawAspect(ratio, texAspect); + + glUniform2f(glGetUniformLocation(Global::graphics.getShaderID("ui"), "textureScale"), texScale.x, texScale.y); + glUniformMatrix4fv(glGetUniformLocation(Global::graphics.getShaderID("ui"), "transform"), 1, GL_FALSE, glm::value_ptr(transMat[0])); + glDrawArrays(GL_TRIANGLE_STRIP, 0, m_quad_numVertices); + } + if (m_all_screens[screenID]->screenElements[gui]->hasComponent<UIButton>()){ + GLuint texID = m_all_screens[screenID]->screenElements[gui]->getComponent<UIButton>()->getTexID(); + glm::mat4 transMat = m_all_screens[screenID]->screenElements[gui]->getComponent<UIButton>()->getTransformationMat(); + float texAspect = m_all_screens[screenID]->screenElements[gui]->getComponent<UIButton>()->getTextureScaleAspect(); + AspectRatio ratio = m_all_screens[screenID]->screenElements[gui]->getComponent<UIButton>()->getAspectRatio(); + + glActiveTexture(GL_TEXTURE5); + glBindTexture(GL_TEXTURE_2D, texID); + + glm::vec2 texScale = drawAspect(ratio, texAspect); + + glUniform2f(glGetUniformLocation(Global::graphics.getShaderID("ui"), "textureScale"), texScale.x, texScale.y); + glUniformMatrix4fv(glGetUniformLocation(Global::graphics.getShaderID("ui"), "transform"), 1, GL_FALSE, glm::value_ptr(transMat[0])); + glDrawArrays(GL_TRIANGLE_STRIP, 0, m_quad_numVertices); + + + } + + } + glDisableVertexAttribArray(0); + glBindVertexArray(0); + } + glDisable(GL_BLEND); + glEnable(GL_DEPTH_TEST); +} + + + +void UISystem::draw(){ + renderScreen(); +} + +glm::mat4 UISystem::getScaleMatrix(glm::vec2 scale) { + glm::mat4 M = glm::mat4(1.f); + M[0][0] = scale.x * (m_aspectRatio); + M[1][1] = scale.y; + M[2][2] = 0.f; + return M; +} + +glm::mat4 UISystem::makeTransformationMat(glm::vec2 translation, glm::vec2 scale){ + glm::mat4 transMat = glm::mat4(1.f); + transMat[3] = glm::vec4(translation.x, translation.y, 0.f, 1.f); + + glm::mat4 scaleMat = getScaleMatrix(glm::vec2(scale)); + + return transMat*scaleMat; +} + +void UISystem::update(double deltaTime){ + // allow for one more update cycle before deactivating click + if (m_input_map[GLFW_MOUSE_BUTTON_LEFT].isClicked){ + // check if clicked inside gui + // only act on the front-most gui being clicked + std::set<std::string>::iterator it = m_shownScreens.begin(); + while(it != m_shownScreens.end()) { + std::set<std::string>::iterator current = it++; + std::string screenID = *current; + // render all active screens + if (m_all_screens[screenID]->isActive){ + // loop through ui elements + for (auto &gui: m_all_screens[screenID]->screenElements){ + if (gui.second->hasComponent<UIButton>()){ + bool isInside = false; + Bounds2f buttonBounds = gui.second->getComponent<UIButton>()->getBounds(); + isInside = checkInsideGUI(m_mousepos, buttonBounds); + if (isInside){ + bool toClose = gui.second->getComponent<UIButton>()->onButtonPress(); + if (toClose){ + m_shownScreens.erase(current); + + } + } + } + } + } + } + + if (m_input_map[GLFW_MOUSE_BUTTON_LEFT].checkClickTime > 1){ + m_input_map[GLFW_MOUSE_BUTTON_LEFT].isClicked = false; + } else { + m_input_map[GLFW_MOUSE_BUTTON_LEFT].checkClickTime ++; + } + } +} + +bool UISystem::checkInsideGUI(glm::vec2 mouseClickPos, Bounds2f guiBounds){ + if (mouseClickPos.x < guiBounds.max.x + && mouseClickPos.x > guiBounds.min.x + && mouseClickPos.y > guiBounds.max.y + && mouseClickPos.y < guiBounds.min.y){ + return true; + } + + return false; +} + + +void UISystem::scrollEvent(double distance){} + +void UISystem::mousePosEvent(double xpos, double ypos){ + m_mousepos = glm::vec2(xpos, ypos); + + for (auto &screenID : m_shownScreens){ + Global::graphics.bindShader("ui"); + //glUniform1i(glGetUniformLocation(Global::graphics.getShaderID("ui"), "hovering"), false); + // render all active screens + if (m_all_screens[screenID]->isActive){ + // loop through ui elements + for (auto &gui: m_all_screens[screenID]->screenElements){ + if (gui.second->hasComponent<UIButton>()){ + bool isInside; + Bounds2f buttonBounds = gui.second->getComponent<UIButton>()->getBounds(); + isInside = checkInsideGUI(m_mousepos, buttonBounds); + if (isInside){ + //glfwSetCursor(window, m_pointerCursor); + // hover effect + //glUniform1i(glGetUniformLocation(Global::graphics.getShaderID("ui"), "hovering"), true); + } + } + } + } + } +} + +void UISystem::onWindowResize(int width, int height){ + m_windowWidth = width; + m_windowHeight = height; + m_aspectRatio = static_cast<float>(m_windowHeight)/static_cast<float>(m_windowWidth); + + for (auto &screen : m_all_screens){ + // loop through ui elements + for (auto &gui: screen.second->screenElements){ + if (gui.second->hasComponent<UIButton>()){ + gui.second->getComponent<UIButton>()->setWindowPos(width, height); + } + if (gui.second->hasComponent<UIDisplay>()){ + gui.second->getComponent<UIDisplay>()->setWindowPos(width, height); + } + } + } +} diff --git a/engine-ocean/Game/Systems/UI/uisystem.h b/engine-ocean/Game/Systems/UI/uisystem.h new file mode 100644 index 0000000..1094861 --- /dev/null +++ b/engine-ocean/Game/Systems/UI/uisystem.h @@ -0,0 +1,89 @@ +#ifndef UISYSTEM_H +#define UISYSTEM_H +#include "Game/GameWorld.h" +#include "Game/Systems/UI/UITextures/UIButton.h" +#include "Game/Systems/UI/UITextures/uitexture.h" +#include "Game/Systems/UI/uielement.h" +#include "Graphics/global.h" +#include <GLFW/glfw3.h> +#include "Game/Systems/system.h" + +struct UIScreen{ + bool isActive = false; + GLuint screenVAOID; + std::map<std::string, std::shared_ptr<UIElement>> screenElements; + std::vector<std::string> elementsDepthOrder; +}; + + +class UISystem : public System +{ +public: + UISystem(std::shared_ptr<Camera> camera, + std::map<int, Input>& input_map, + std::set<std::string>& shownScreens); + ~UISystem(); + + void draw() override; + void update(double deltaTime) override; + void scrollEvent(double distance) override; + void mousePosEvent(double xpos, double ypos) override; + void onWindowResize(int width, int height); + GLuint makeVAO(std::vector<float> positions); + +private: + + int m_quad_numVertices = 4; + + void initializeScreenMap(); + void initializeScreen(); + void initializeStartScreen(); + void initializeInventory(); + + + void renderScreen(); + glm::mat4 makeTransformationMat(glm::vec2 translation, glm::vec2 scale); + glm::mat4 getScaleMatrix(glm::vec2 scale); + bool checkInsideGUI(glm::vec2 mouseClickPos, Bounds2f guiBounds); + void initializeProfileScreen(); + + + void makeButtonElement(std::shared_ptr<UIScreen> &screen, std::string elementName, const char* filename, const glm::vec2 pos, const glm::vec2 scale, AspectRatio aspectRatio, bool isCloseButton = false, CornerPosition corner = NONE); + void makeDisplayElement(std::shared_ptr<UIScreen> &screen, std::string elementName, const char* filename, const glm::vec2 pos, const glm::vec2 scale, AspectRatio aspectRatio = FIT_SCREEN); + void makeDisplayElement(std::shared_ptr<UIScreen> &screen, std::string elementName, TextureData &tex, const glm::vec2 pos, const glm::vec2 scale, AspectRatio aspectRatio = FIT_SCREEN); + + + + std::shared_ptr<Camera> m_camera; + float m_windowWidth = 640; + float m_windowHeight = 480; + float m_aspectRatio = 480.f/640.f; + + glm::vec2 m_mousepos = glm::vec2(0.f); + std::map<int, Input>& m_input_map; + + std::map<std::string, std::shared_ptr<UIScreen>> m_all_screens; + + GLFWcursor* m_pointerCursor; + + std::shared_ptr<ButtonAction> showProfileAction; + + std::set<std::string>& m_shownScreens; + float m_showStartScreen = true; + + + float dim = 1.0f; + std::vector<float> m_quadPos = { + -1.0f, 1.0f, + -1.0f, -1.0f, + 1.0f, 1.0f, + 1.0f, -1.0f + }; + + glm::vec2 drawAspect(AspectRatio aspectType, float textureAspect); + + + +}; + +#endif // UISYSTEM_H diff --git a/engine-ocean/Game/Systems/aisystem.cpp b/engine-ocean/Game/Systems/aisystem.cpp new file mode 100644 index 0000000..e3328ed --- /dev/null +++ b/engine-ocean/Game/Systems/aisystem.cpp @@ -0,0 +1,6 @@ +#include "aisystem.h" + +AISystem::AISystem() +{ + +} diff --git a/engine-ocean/Game/Systems/aisystem.h b/engine-ocean/Game/Systems/aisystem.h new file mode 100644 index 0000000..fe0b958 --- /dev/null +++ b/engine-ocean/Game/Systems/aisystem.h @@ -0,0 +1,36 @@ +#ifndef AISYSTEM_H +#define AISYSTEM_H + + +// where is player? +#include "Game/GameObjects/GameObject.h" +#include <map> +#include <memory> +#include <glm/glm.hpp> + + +struct PosData{ + glm::vec3 currPos; + glm::vec3 setToPos; +}; + +// did player jump? did player slash? +struct ConditionData{ + bool conditionTrue = false; +}; + +// BLACKBOARD +struct BlackboardData{ + PosData locationData; + std::map<std::string, ConditionData> conditionData; + std::shared_ptr<GameObject> environment; +}; + + +class AISystem +{ +public: + AISystem(); +}; + +#endif // AISYSTEM_H diff --git a/engine-ocean/Game/Systems/camerasystem.cpp b/engine-ocean/Game/Systems/camerasystem.cpp new file mode 100644 index 0000000..7951e35 --- /dev/null +++ b/engine-ocean/Game/Systems/camerasystem.cpp @@ -0,0 +1,210 @@ +#include "camerasystem.h" +#include "Game/Components/TransformComponent.h" +#include "Game/GameWorld.h" +#include <memory> +#include <math.h> + + +CameraSystem::CameraSystem(std::map<std::string, std::shared_ptr<GameObject>>& gameobjects, + std::shared_ptr<Camera>& camera, + std::map<int, Input>& input_map): + m_gameobjects(gameobjects), + m_camera(camera), + m_input_map(input_map), + first_person(false), + third_person(true), + m_player(m_gameobjects["player"]) +{ + camera->setPos(glm::vec3(0.f, 15.f, 0.f)); + camera->setLook(glm::vec3(-28.6761,5.40863,46.5823)); + + +} + +glm::vec3 CameraSystem::calculateIdealOffset(glm::vec3 initial){ + //glm::quat playerRotation = glm::quat( +// float y_rotate = m_player->getComponent<TransformComponent>()->getYRotationAngle(); +// glm::quat playerRot = glm::angleAxis(y_rotate, glm::vec3(0.f, 1.f, 0.f)); + glm::vec4 idealOffset = glm::vec4(initial, 0.f); + + glm::vec4 rotatedOffset = m_player->getComponent<TransformComponent>()->getMT()->getRotation() * idealOffset * .2f; + glm::vec3 rotated = glm::vec3(rotatedOffset) + m_player->getComponent<TransformComponent>()->getPos(); + + return rotated; +} + +void CameraSystem::update(double deltaTime){ + if (m_gameobjects["player"]->getComponent<TransformComponent>()->movingLaterally){ + glm::vec3 player_pos = m_gameobjects["player"]->getComponent<TransformComponent>()->getPos(); + + + glm::vec4 movement = m_distance*glm::vec4(1.f,0.f,0.f, 0.f); + glm::vec4 transl = m_gameobjects["player"]->getComponent<TransformComponent>()->getMT()->getRotation()*movement; + transl.y = 0.f; + + glm::vec3 offset = glm::vec3(0.f, 5.f, 0.f); + glm::vec3 newPos = player_pos + glm::vec3(transl) + offset; + + // arcballRotation(newPos.x, newPos.z); + m_camera->setPos(newPos); + + if (glm::cross(m_camera->getUp(), player_pos) != glm::vec3(0.f)){ + m_camera->setLook(player_pos); + } + } +} + + +void CameraSystem::draw(){ + Global::graphics.bindShader("phong"); + Global::graphics.setCameraData(m_camera); +} + +void CameraSystem::firstPersonMode(){ + first_person = true; + third_person = false; + m_camera->setPos(m_gameobjects["player"]->getComponent<TransformComponent>()->getPos()); +} + +void CameraSystem::thirdPersonMode(glm::vec3 curr_cam_pos){ + first_person = false; + third_person = true; + //camera->setPos(curr_cam_pos); +} + +void CameraSystem::scrollEvent(double distance){ + // calculateZoom(distance); +// m_distance += distance; +// if (m_distance < 5.f) m_distance = 5.f; +// if (m_distance > 40.f) m_distance = 40.f; +} + +void CameraSystem::mousePosEvent(double xpos, double ypos){ + + // if right mouse button is pressed, calculate change of the new input x and y pos + if (m_input_map[GLFW_MOUSE_BUTTON_LEFT].isHeld){ + float deltaX = xpos - prev_mouse_pos.x; + float deltaY = ypos - prev_mouse_pos.y; + + // upate prev mouse pos + prev_mouse_pos = glm::vec2(xpos, ypos); + + arcballRotation(deltaX, deltaY); + + } +} + +void CameraSystem::calculateZoom(float scrollDistance){ + float zoomLevel = scrollDistance*.1f; + m_distanceFromPlayer -= zoomLevel; +} + +void CameraSystem::calculatePitch(double deltaY){ + if (m_input_map[GLFW_MOUSE_BUTTON_LEFT].isHeld){ + float pitchChange = deltaY*.1f; + m_pitch -= pitchChange; + } +} + +void CameraSystem::calculateAngleAroundPlayer(double deltaX){ + if (m_input_map[GLFW_MOUSE_BUTTON_LEFT].isHeld){ + float angleChange = deltaX*.3f; + m_angleAroundPlayer -= angleChange; + } +} + +void CameraSystem::arcballRotation(float deltaX, float deltaY){ + glm::vec4 cam_pos = glm::vec4(m_camera->getPos(), 1.f); + glm::vec4 pivot = glm::vec4(m_player->getComponent<TransformComponent>()->getPos(), 1.f); + + // calculate rotation amount given mouse movement + float deltaAngleX = (2*M_PI / static_cast<float>(m_camera->getWidth())); + float deltaAngleY = (M_PI / static_cast<float>(m_camera->getHeight())); + + float xAngle = deltaX * deltaAngleX; + float yAngle = deltaY * deltaAngleY; + + // handles when camera dir is same as up vector + float cosAngle = glm::dot(m_camera->getViewDirection(), m_camera->getUp()); + float sign = 1.f; + if (yAngle < 0) sign = -1.f; + if (cosAngle * sign > .99f) yAngle = 0; + + // rotate camera around pivtor point + glm::mat4 rotX(1.f); + rotX = glm::rotate(rotX, xAngle, m_camera->getUp()); + cam_pos = (rotX * (cam_pos - pivot)) + pivot; + + glm::mat4 rotY(1.f); + rotY = glm::rotate(rotY, yAngle, m_camera->getRight()); + glm::vec3 final_pos = (rotY * (cam_pos - pivot)) + pivot; + + // update camera view + m_camera->setPos(final_pos); + +} + +void CameraSystem::setCameraPos(){ +// float horiz_distance = m_distanceFromPlayer*glm::cos(glm::radians(m_pitch)); +// float vert_distance = m_distanceFromPlayer*glm::sin(glm::radians(m_pitch)); + +// glm::vec3 player_pos = m_gameobjects["player"]->getComponent<TransformComponent>()->getPos(); +// float y_camPos = player_pos.y + vert_distance; + +// float theta = glm::radians(m_angleAroundPlayer); +// float offsetX = horiz_distance*glm::cos((theta)); +// float offsetZ = horiz_distance*glm::sin((theta)); + +// glm::vec3 camPos = glm::vec3(player_pos.x - offsetX, y_camPos, player_pos.z - offsetZ); + + // m_yaw = glm::radians(180-theta); + +// glm::vec3 lookVector = camPos + +// glm::vec3(glm::cos(pitch)*glm::sin(m_yaw), glm::sin(pitch), glm::cos(pitch)*glm::cos(m_yaw)); + + +// glm::vec4 distanceCamToPlayer = glm::vec4(camPos - player_pos, 0.f); +// glm::vec4 look = m_gameobjects["player"]->getComponent<TransformComponent>()->getMT()->getRotation() * distanceCamToPlayer; + +// //glm::vec3 idealOffset = (glm::vec3(-7.f, 5.f, -10.f)) + m_player->getComponent<TransformComponent>()->getPos(); +// glm::vec3 idealLookat = calculateIdealOffset(glm::vec3(0.f, -2.f, 40.f)); + +// m_currPos = idealOffset; +// m_currLook = idealLookat; + +// glm::vec3 direction; +// direction.x = cos(glm::radians(m_yaw)) * cos(glm::radians(m_pitch)); +// direction.y = sin(glm::radians(m_pitch)); +// direction.z = sin(glm::radians(m_yaw)) * cos(glm::radians(m_pitch)); + +// // calculate look +// glm::vec4 rotatedPlayerPoint = m_player->getComponent<TransformComponent>()->getMT()->getRotation()* glm::vec4(player_pos, 1.f); +// float distanceX = glm::distance(rotatedPlayerPoint.x, player_pos.x); +// float distanceY = glm::distance(rotatedPlayerPoint.y, player_pos.y); +// float distanceZ = glm::distance(rotatedPlayerPoint.z, player_pos.z); + + + + //camPos = camPos + glm::vec3(distanceX, distanceY, distanceZ); + //glm::vec3 newLook = camPos - player_pos; + + + + // glm::vec4 lookToPlayer = glm::vec4(camPos - player_pos, 0.f); + // glm::vec4 rotatedLook = m_player->getComponent<TransformComponent>()->getMT()->getRotation() * lookToPlayer; + // float distLookToPlayer = glm::distance(glm::vec3(rotatedLook), player_pos); + + + // m_camera->setPos(camPos); + //m_camera->translate(m_currPos); + //m_camera->rotate(m_yaw, glm::vec3(0.f, 1.f, 0.f)); + //m_camera->setLook(newLook); + + + // m_camera->setPos(glm::vec3(x_camPos, y_camPos, z_camPos)); + + + + +} + diff --git a/engine-ocean/Game/Systems/camerasystem.h b/engine-ocean/Game/Systems/camerasystem.h new file mode 100644 index 0000000..3ee6928 --- /dev/null +++ b/engine-ocean/Game/Systems/camerasystem.h @@ -0,0 +1,74 @@ +#ifndef CAMERASYSTEM_H +#define CAMERASYSTEM_H + + +#include "Game/GameWorld.h" +#include "Graphics/camera.h" +#include <memory> +#include "system.h" + +class CameraSystem : public System +{ +public: + CameraSystem(std::map<std::string, std::shared_ptr<GameObject>>& gameobjects, + std::shared_ptr<Camera>& camera, + std::map<int, Input>& input_map); + void draw() override; + void update(double deltaTime) override; + void scrollEvent(double distance) override; + void mousePosEvent(double xpos, double ypos) override; + +private: + void firstPersonMode(); + void thirdPersonMode(glm::vec3 curr_cam_pos); + + void setCameraPos(); + void calculateAngleAroundPlayer(double deltaX); + void calculatePitch(double deltaY); + void calculateZoom(float scrollDistance); + + glm::vec3 calculateIdealOffset(glm::vec3 initial); + void arcballRotation(float deltaX, float deltaY); + float m_distance = -10.f; + + + + + + + + + + + std::shared_ptr<Camera>& m_camera; + std::map<std::string, std::shared_ptr<GameObject>>& m_gameobjects; + std::map<int, Input>& m_input_map; + bool first_person = false; + bool third_person = true; + float first_person_offset = 0.01f; + glm::vec2 prev_mouse_pos = glm::vec2(0.f); + float horiz_velocity = .005f; + + std::shared_ptr<GameObject> m_player; + float m_distanceFromPlayer = 50.f; + float m_angleAroundPlayer = 0.f; + float m_pitch = 20.f; + float m_yaw = 0.f; + + bool m_isInverted = false; + float m_angle = 0.f; + float m_height = 0.f; + int m_invertY = 1; + + glm::vec3 m_currPos; + glm::vec3 m_currLook; + + float m_currentTurnSpeed = 0.f; + + float TURN_SPEED = 2.f; + float snapshot_time = 0.f; + + +}; + +#endif // CAMERASYSTEM_H diff --git a/engine-ocean/Game/Systems/charactercontrollersystem.cpp b/engine-ocean/Game/Systems/charactercontrollersystem.cpp new file mode 100644 index 0000000..c5e7174 --- /dev/null +++ b/engine-ocean/Game/Systems/charactercontrollersystem.cpp @@ -0,0 +1,132 @@ +#include "charactercontrollersystem.h" +#include "Game/Components/CollisionComponents/CollisionComponent.h" +#include "Game/Components/CollisionComponents/CylinderCollider.h" +#include "Game/Components/TransformComponent.h" +#include "Game/Systems/CollisionSystems/ellipsoidtrianglecollisionsystem.h" +#include <glm/gtx/quaternion.hpp> + +CharacterControllerSystem::CharacterControllerSystem(std::map<std::string, std::shared_ptr<GameObject>>& gameobjects, + std::shared_ptr<Camera>& camera_param, + std::map<int, Input>& input_map, + std::map<std::string, BlackboardData>& global_blackboard): + m_gameobjects(gameobjects), + camera(camera_param), + m_input_map(input_map), + m_global_blackboard(global_blackboard) +{ +} + +void CharacterControllerSystem::draw(){ +} + +TransformComponent* CharacterControllerSystem::getPlayerTransform(){ + return m_gameobjects.at("player")->getComponent<TransformComponent>(); +} + +glm::vec3 CharacterControllerSystem::getPlayerPos(){ + return m_gameobjects.at("player")->getComponent<TransformComponent>()->getPos(); +} + + +float CharacterControllerSystem::jumpPlayer(float &initial_v, double deltaTime, float snapshot_time, float gravity){ + float t = deltaTime-snapshot_time; + float delta_y = initial_v*t + (.5f)*gravity*t*t; + return delta_y; +} + +bool CharacterControllerSystem::movePlayerLaterally(glm::vec3 dir, glm::vec3 perp, glm::vec3 &m_pos, float dt){ + glm::vec3 translationDir; + bool moving_laterally = false; + std::shared_ptr<ModelTransform> temp_mt = getPlayerTransform()->getMT(); + + if (m_input_map[GLFW_KEY_W].isActive){ + m_currentSpeed = RUN_SPEED; + moving_laterally = true; + } else if (m_input_map[GLFW_KEY_S].isActive){ + m_currentSpeed = -RUN_SPEED; + moving_laterally = true; + } else { + m_currentSpeed = 0.f; + } + + + glm::mat4 rot = glm::mat4(1.f); + // player turning + if (m_input_map[GLFW_KEY_D].isActive){ + m_currentTurnSpeed = -TURN_SPEED; + moving_laterally = true; + } else if (m_input_map[GLFW_KEY_A].isActive){ + m_currentTurnSpeed = TURN_SPEED; + moving_laterally = true; + } else { + m_currentTurnSpeed = 0.f; + } + + // rotate player + getPlayerTransform()->getMT()->rotate(m_currentTurnSpeed*dt, glm::vec3(0.f, 1.f, 0.f)); + + + //translate player based on rotation + float distance = m_currentSpeed*dt; + glm::vec4 movement = distance*glm::vec4(1.f,0.f,0.f, 0.f); + glm::vec4 transl = getPlayerTransform()->getMT()->getRotation()*movement; + + translationDir = glm::vec3(transl.x, 0.f, transl.z); + temp_mt->translate(translationDir); + glm::vec3 potential_pos = temp_mt->getPos(); + m_pos = potential_pos; + + + return moving_laterally; +} + +void CharacterControllerSystem::handlePlayerMovement(double deltaTime){ + // timing + float dt = deltaTime - snapshot_time; + snapshot_time = deltaTime; + + // get player positions + m_pos = getPlayerPos(); + getPlayerTransform()->old_pos = m_pos; + + // movement + getPlayerTransform()->movingLaterally = false; + float speedFactor = horiz_velocity * deltaTime; + glm::vec3 look = camera->getLook(); + glm::vec3 dir = glm::normalize(glm::vec3(look.x, 0.f, look.z)) * speedFactor; + glm::vec3 perp = glm::vec3(look.z, 0.f, -look.x) * speedFactor; + + // updates m_pos + if (movePlayerLaterally(dir, perp, m_pos, dt)){ + getPlayerTransform()->movingLaterally = true; + } + + if (m_input_map[GLFW_KEY_SPACE].isActive && (getPlayerTransform()->onGround)){ + getPlayerTransform()->gravity = -25.f; + getPlayerTransform()->yVelocity = 20.f; + getPlayerTransform()->onGround = false; + //if (!gravity) gravity = true; + m_global_blackboard["player"].conditionData["isJumping"].conditionTrue = true; + } else if (!m_input_map[GLFW_KEY_SPACE].isActive && getPlayerTransform()->onGround){ + m_global_blackboard["player"].conditionData["isJumping"].conditionTrue = false; + } + + // PLAYER-SPECIFIC GRAVITY (comment these two lines to keep player still) + //if (gravity){ + m_pos.y += jumpPlayer(getPlayerTransform()->yVelocity, dt, 0, getPlayerTransform()->gravity); + getPlayerTransform()->yVelocity = getPlayerTransform()->yVelocity + getPlayerTransform()->gravity*(dt); + //} + + // store m_pos as estimated final_pos + m_global_blackboard["player"].locationData.setToPos = m_pos; + + getPlayerTransform()->estimated_final_pos = m_pos; +} + + +void CharacterControllerSystem::update(double deltaTime){ + handlePlayerMovement(deltaTime); +} + +void CharacterControllerSystem::scrollEvent(double distance){} +void CharacterControllerSystem::mousePosEvent(double xpos, double ypos){} diff --git a/engine-ocean/Game/Systems/charactercontrollersystem.h b/engine-ocean/Game/Systems/charactercontrollersystem.h new file mode 100644 index 0000000..04dd02f --- /dev/null +++ b/engine-ocean/Game/Systems/charactercontrollersystem.h @@ -0,0 +1,62 @@ +#ifndef CHARACTERCONTROLLERSYSTEM_H +#define CHARACTERCONTROLLERSYSTEM_H + + +#include "Game/Components/TransformComponent.h" +#include "Game/GameObjects/GameObject.h" +#include "Game/GameWorld.h" +#include "Game/Systems/CollisionSystems/ellipsoidtrianglecollisionsystem.h" +#include <map> + + + +class CharacterControllerSystem : public System +{ +public: + CharacterControllerSystem(std::map<std::string, std::shared_ptr<GameObject>>& gameobjects, + std::shared_ptr<Camera>& camera_param, + std::map<int, Input>& input_map, + std::map<std::string, BlackboardData>& global_blackboard); + void draw() override; + void update(double deltaTime) override; + void scrollEvent(double distance) override; + void mousePosEvent(double xpos, double ypos) override; + +private: + bool movePlayerLaterally(glm::vec3 dir, glm::vec3 perp, glm::vec3 &m_pos, float dt); + void handlePlayerMovement(double deltaTime); + TransformComponent* getPlayerTransform(); + + std::map<std::string, std::shared_ptr<GameObject>>& m_gameobjects; + std::shared_ptr<Camera>& camera; + std::map<int, Input>& m_input_map; + + void movePlayer(glm::vec3 direction, int key); + void setPlayerPos(glm::vec3 new_pos); + glm::vec3 getPlayerPos(); + float jumpPlayer(float &initial_v, double deltaTime, float snapshot_time, float gravity); + + + glm::vec3 m_pos = glm::vec3(0.f); + float horiz_velocity = .005f; + float snapshot_time = 0.f; + + std::map<std::string, BlackboardData>& m_global_blackboard; + + float m_currentSpeed = 0.f; + float m_currentTurnSpeed = 0.f; + + float RUN_SPEED = 8.f; + float TURN_SPEED = 2.f; + + glm::quat m_quat; + glm::vec3 m_axis; + + float HALFPI = 0.78539f; + bool gravity = false; + + + +}; + +#endif // CHARACTERCONTROLLERSYSTEM_H diff --git a/engine-ocean/Game/Systems/drawsystem.cpp b/engine-ocean/Game/Systems/drawsystem.cpp new file mode 100644 index 0000000..ad6e17b --- /dev/null +++ b/engine-ocean/Game/Systems/drawsystem.cpp @@ -0,0 +1,60 @@ +#include "drawsystem.h" +#include "Game/Components/DrawComponent.h" +#include "Game/Components/TransformComponent.h" + +DrawSystem::DrawSystem(std::map<std::string, std::shared_ptr<GameObject>>& gameobjects): + m_gameobjects(gameobjects) +{ + + +} + +void DrawSystem::update(double deltaTime){} +// store all game objects and get draw components, draw + +void DrawSystem::draw(){ + Global::graphics.bindShader("phong"); + //std::cout << "IN DRAW SYSTEM" << std::endl; + + for (auto &pair: m_gameobjects){ + if (pair.second->hasComponent<DrawComponent>()){ + + DrawComponent *draw_comp = pair.second->getComponent<DrawComponent>(); + TransformComponent *transform_comp = pair.second->getComponent<TransformComponent>(); + if (transform_comp->hasMultipleMT()){ + if (draw_comp->objHasMaterial()){ + for (const std::shared_ptr<ModelTransform> &mt : transform_comp->getAllMT()){ + Global::graphics.drawShape(draw_comp->getShape(), mt, draw_comp->getMaterial()); + } + } else { + for (const std::shared_ptr<ModelTransform> &mt : transform_comp->getAllMT()){ + Global::graphics.drawShape(draw_comp->getShape(), mt); + } + } + } else { + // case: object is seperated by material + if (draw_comp->objHasMultipleShapes()){ + for (auto &shape : draw_comp->getShapesWithMaterials()){ + if (shape->hasMaterial()){ + Global::graphics.drawShape(shape, transform_comp->getMT(), shape->getShapeMaterial()); + } else { + Global::graphics.drawShape(shape, transform_comp->getMT()); + } + } + + } else { + if (draw_comp->objHasMaterial()){ + Global::graphics.drawShape(draw_comp->getShape(), transform_comp->getMT(), draw_comp->getMaterial()); + } else { + //std::cout << "draw shape: " << pair.first << std::endl; + Global::graphics.drawShape(draw_comp->getShape(), transform_comp->getMT()); + } + } + } + } + } + +} + +void DrawSystem::scrollEvent(double distance){} +void DrawSystem::mousePosEvent(double xpos, double ypos){} diff --git a/engine-ocean/Game/Systems/drawsystem.h b/engine-ocean/Game/Systems/drawsystem.h new file mode 100644 index 0000000..d1c8eb9 --- /dev/null +++ b/engine-ocean/Game/Systems/drawsystem.h @@ -0,0 +1,23 @@ +#ifndef DRAWSYSTEM_H +#define DRAWSYSTEM_H + + +#include "Game/GameObjects/GameObject.h" +#include <vector> +#include "system.h" + +class DrawSystem : public System +{ +public: + DrawSystem(std::map<std::string, std::shared_ptr<GameObject>>& gameobjects); + void draw() override; + void update(double deltaTime) override; + void scrollEvent(double distance) override; + void mousePosEvent(double xpos, double ypos) override; + + +private: + std::map<std::string, std::shared_ptr<GameObject>>& m_gameobjects; +}; + +#endif // DRAWSYSTEM_H diff --git a/engine-ocean/Game/Systems/objectcreationsystem.cpp b/engine-ocean/Game/Systems/objectcreationsystem.cpp new file mode 100644 index 0000000..85241fe --- /dev/null +++ b/engine-ocean/Game/Systems/objectcreationsystem.cpp @@ -0,0 +1,161 @@ +#include "objectcreationsystem.h" +#include "Game/Components/CollisionComponents/CollisionComponent.h" +#include "Game/Components/CollisionComponents/boundingtriangle.h" +#include "Game/Components/DrawComponent.h" +#include "Game/Components/PathfindComponent.h" +#include "Game/Components/TransformComponent.h" +#include "Game/Ocean/ocean.h" + +#include <random> + +ObjectCreationSystem::ObjectCreationSystem(std::map<std::string, std::shared_ptr<GameObject>>& gameobjects, + std::map<std::string, std::shared_ptr<GameObject>>& dynamic_gameobjects, + std::map<std::string, std::shared_ptr<GameObject>>& rigid_gameobjects, + std::map<std::string, BlackboardData>& global_blackboard, + std::map<std::string, std::vector<std::shared_ptr<GameObject>>>& lootables): + m_gameobjects(gameobjects), + m_dynamic_gameobjects(dynamic_gameobjects), + m_rigid_gameobjects(rigid_gameobjects), + m_global_blackboard(global_blackboard), + m_lootables(lootables) + +{ + initializeAllObjects(); +} + +void ObjectCreationSystem::initializeAllObjects(){ + + m_ground_level = -.5f; + initializePlayerObject(); + // initializeSlopedGround(); +// initializeGround(); +// initializeBackground(); + initOcean(); + + addLight(); +} + +void ObjectCreationSystem::initializeSlopedGround(){ + std::shared_ptr<GameObject> sloped_ground = std::make_shared<GameObject>(); + std::vector<glm::vec3> obj_data = Global::graphics.addShape("sloped_ground", "/Users/jesswan/Desktop/cs1950u/cs1950u-jjesswan/Resources/Meshes/floor.obj"); + + std::shared_ptr<ModelTransform> mt = std::make_shared<ModelTransform>(); + mt->setScale(1.f); + mt->setPos(glm::vec3(0.f)); + + sloped_ground->addComponent<DrawComponent>(std::make_unique<DrawComponent>(Global::graphics.getShape("sloped_ground"))); + sloped_ground->getComponent<DrawComponent>()->addMaterial("grass_tedxxx", "/Users/jesswan/Desktop/cs1950u/cs1950u-jjesswan/Resources/Images/mossyground.png"); + sloped_ground->addComponent<TransformComponent>(std::make_unique<TransformComponent>(mt, "sloped_ground", m_global_blackboard)); + sloped_ground->addComponent<CollisionComponent>(std::make_unique<CollisionComponent>("obj", obj_data, mt)); + + insertRigidObject("sloped_ground", sloped_ground); +} + +void ObjectCreationSystem::insertAnyObject(const std::string name, const std::shared_ptr<GameObject> &game_obj){ + m_gameobjects.insert(std::pair<const std::string, std::shared_ptr<GameObject>>(name, game_obj)); +} + +void ObjectCreationSystem::insertRigidObject(const std::string name, const std::shared_ptr<GameObject> &game_obj){ + m_rigid_gameobjects.insert(std::pair<const std::string, std::shared_ptr<GameObject>>(name, game_obj)); + insertAnyObject(name, game_obj); +} + +void ObjectCreationSystem::insertDynamicObject(const std::string name, const std::shared_ptr<GameObject> &game_obj){ + m_dynamic_gameobjects.insert(std::pair<const std::string, std::shared_ptr<GameObject>>(name, game_obj)); + insertAnyObject(name, game_obj); +} + +void ObjectCreationSystem::initializePlayerObject(){ + //std::shared_ptr<Shape> shape = Global::graphics.getShape("sphere"); + std::vector<glm::vec3> obj_data = Global::graphics.addShape_withMaterial("mouse", "/Users/jesswan/Desktop/cs1950u/cs1950u-jjesswan/Resources/Meshes/mouse2-4.obj", + "/Users/jesswan/Desktop/cs1950u/cs1950u-jjesswan/Resources/Meshes/mouse2-4.mtl", true); + std::shared_ptr<ModelTransform> mt = std::make_shared<ModelTransform>(); + mt->setScale(glm::vec3(.5f)); + mt->setPos(glm::vec3(0.f)); + + std::shared_ptr<GameObject> player = std::make_shared<GameObject>(); + + player->addComponent<DrawComponent>(std::make_unique<DrawComponent>(Global::graphics.getShapeGroup("mouse"))); + player->addComponent<TransformComponent>(std::make_unique<TransformComponent>(mt, "player", m_global_blackboard, true)); + player->addComponent<CollisionComponent>(std::make_unique<CollisionComponent>("dynamic_mesh", mt, mt->getPos(), obj_data)); + + insertDynamicObject("player", player); +} + +void ObjectCreationSystem::initializeGround(){ + std::shared_ptr<GameObject> ground = std::make_shared<GameObject>(); + std::vector<glm::vec3> obj_data = Global::graphics.addShape_withMaterial("ground", "/Users/jesswan/Desktop/cs1950u/cs1950u-jjesswan/Resources/Meshes/meadow_ground.obj", + "/Users/jesswan/Desktop/cs1950u/cs1950u-jjesswan/Resources/Meshes/meadow_ground.mtl", true); + + //std::vector<glm::vec3> obj_data = Global::graphics.addShape("ground", "/Users/jesswan/Desktop/cs1950u/cs1950u-jjesswan/Resources/Meshes/testplane.obj"); + std::shared_ptr<ModelTransform> mt = std::make_shared<ModelTransform>(); + mt->setPos(glm::vec3(0.f, 0.f, 0.f)); + ground->addComponent<DrawComponent>(std::make_unique<DrawComponent>(Global::graphics.getShapeGroup("ground"))); + ground->addComponent<TransformComponent>(std::make_unique<TransformComponent>(mt, "ground", m_global_blackboard)); + ground->addComponent<CollisionComponent>(std::make_unique<CollisionComponent>("obj", obj_data, mt, true)); + + insertRigidObject("ground", ground); +} + +void ObjectCreationSystem::initializeBackground(){ + std::shared_ptr<GameObject> bg = std::make_shared<GameObject>(); + + // "Snowy Mountain - Terrain" (https://skfb.ly/6RzJV) by artfromheath is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/). + std::vector<glm::vec3> obj_data = Global::graphics.addShape_withMaterial("bg", "/Users/jesswan/Desktop/cs1950u/cs1950u-jjesswan/Resources/Meshes/howl_field_background.obj", + "/Users/jesswan/Desktop/cs1950u/cs1950u-jjesswan/Resources/Meshes/howl_field_background.mtl", true); + + //std::vector<glm::vec3> obj_data = Global::graphics.addShape("ground", "/Users/jesswan/Desktop/cs1950u/cs1950u-jjesswan/Resources/Meshes/testplane.obj"); + std::shared_ptr<ModelTransform> mt = std::make_shared<ModelTransform>(); + mt->setPos(glm::vec3(0.f, 0.f, 0.f)); + bg->addComponent<DrawComponent>(std::make_unique<DrawComponent>(Global::graphics.getShapeGroup("bg"))); + bg->addComponent<TransformComponent>(std::make_unique<TransformComponent>(mt, "bg", m_global_blackboard)); + bg->addComponent<CollisionComponent>(std::make_unique<CollisionComponent>("obj", obj_data, mt, true)); + + insertRigidObject("bg", bg); + +} + +void ObjectCreationSystem::initOcean(){ + m_ocean_shape = std::make_shared<GameObject>(); + + std::vector<glm::vec3> obj_data = Global::graphics.addShape_manual("ocean", m_ocean.get_vertices(), m_ocean.get_faces(), true); + + + std::shared_ptr<ModelTransform> mt = std::make_shared<ModelTransform>(); + mt->setScale(1.f); + mt->setPos(glm::vec3(0.f, 0.f, 0.f)); + m_ocean_shape->addComponent<DrawComponent>(std::make_unique<DrawComponent>(Global::graphics.getShape("ocean"))); + m_ocean_shape->getComponent<DrawComponent>()->addMaterial("grass_tedxxx", "/Users/jesswan/Desktop/cs1950u/cs1950u-jjesswan/Resources/Images/mossyground.png"); + + m_ocean_shape->addComponent<TransformComponent>(std::make_unique<TransformComponent>(mt, "ocean", m_global_blackboard)); + m_ocean_shape->addComponent<CollisionComponent>(std::make_unique<CollisionComponent>("obj", obj_data, mt, true)); + + insertRigidObject("ocean", m_ocean_shape); + + +} + + +void ObjectCreationSystem::addLight(){ + std::shared_ptr<Light> light1 = std::make_shared<Light>(LightType::DIRECTIONAL, glm::vec3(0,20,6.5)); + std::vector<std::shared_ptr<Light>> lights; + lights.push_back(light1); + + Global::graphics.bindShader("phong"); + Global::graphics.setLights(lights); +} + + +void ObjectCreationSystem::draw(){} +void ObjectCreationSystem::update(double deltaTime){ + std::cout << "update" << std::endl; + m_ocean.fft_prime(m_time); + Global::graphics.getShape("ocean")->updateVAO(m_ocean.get_vertices(), m_ocean.get_faces()); + m_time += m_timestep; + +} +void ObjectCreationSystem::scrollEvent(double distance){} +void ObjectCreationSystem::mousePosEvent(double xpos, double ypos){} + + + diff --git a/engine-ocean/Game/Systems/objectcreationsystem.h b/engine-ocean/Game/Systems/objectcreationsystem.h new file mode 100644 index 0000000..ce30cde --- /dev/null +++ b/engine-ocean/Game/Systems/objectcreationsystem.h @@ -0,0 +1,82 @@ +#ifndef OBJECTCREATIONSYSTEM_H +#define OBJECTCREATIONSYSTEM_H + + +#include "Game/GameObjects/GameObject.h" +#include "Game/Systems/aisystem.h" +#include "Game/Ocean/ocean.h" + +#include "system.h" + + +class ObjectCreationSystem : public System +{ +public: + ObjectCreationSystem(std::map<std::string, std::shared_ptr<GameObject>>& gameobjects, + std::map<std::string, std::shared_ptr<GameObject>>& dynamic_gameobjects, + std::map<std::string, std::shared_ptr<GameObject>>& rigid_gameobjects, + std::map<std::string, BlackboardData>& global_blackboard, + std::map<std::string, std::vector<std::shared_ptr<GameObject>>>& lootables); + void draw() override; + void update(double deltaTime) override; + void scrollEvent(double distance) override; + void mousePosEvent(double xpos, double ypos) override; + +private: + void initializeAllObjects(); + void initializePlayerObject(); + void initializeGroundObject(); + void initializeObstacle(); + void generateBamboo(); + void generateBall(glm::vec3 scale, glm::vec3 pos, int number); + void addObject_NoTransform(std::string shape_name, std::string obj_name, std::string filename); + void makeBVHDemo(); + void initializeBackground(); + void initOcean(); + + + + void makeLootable(std::string shapename, std::string filename, int count, glm::vec3 scale); + + + + + + void initializeCeilingMesh(); + void initializeSlopedGround(); + void initializeGround(); + void addLight(); + void makeNavMesh(); + + + + + void insertAnyObject(const std::string name, const std::shared_ptr<GameObject> &game_obj); + void insertDynamicObject(const std::string name, const std::shared_ptr<GameObject> &game_obj); + void insertRigidObject(const std::string name, const std::shared_ptr<GameObject> &game_obj); + + + + + std::map<std::string, std::shared_ptr<GameObject>>& m_gameobjects; + std::map<std::string, std::shared_ptr<GameObject>>& m_dynamic_gameobjects; + std::map<std::string, std::shared_ptr<GameObject>>& m_rigid_gameobjects; + + std::map<std::string, BlackboardData>& m_global_blackboard; + + std::map<std::string, std::vector<std::shared_ptr<GameObject>>>& m_lootables; + + + float m_groundBounds = 0.f; + float m_ground_level = 0.f; + + + ocean m_ocean; + std::shared_ptr<GameObject> m_ocean_shape; + double m_time = 0.0; + double m_timestep = .01; + + +}; + +#endif // OBJECTCREATIONSYSTEM_H diff --git a/engine-ocean/Game/Systems/physicssystem.cpp b/engine-ocean/Game/Systems/physicssystem.cpp new file mode 100644 index 0000000..cd70933 --- /dev/null +++ b/engine-ocean/Game/Systems/physicssystem.cpp @@ -0,0 +1,72 @@ +#include "physicssystem.h" +#include "Game/Components/TransformComponent.h" + +PhysicsSystem::PhysicsSystem(std::map<std::string, std::shared_ptr<GameObject>>& dynamic_gameobjects, + std::map<std::string, BlackboardData>& global_blackboard, + std::map<std::string, std::vector<std::shared_ptr<GameObject>>>& lootables) : + m_dynamic_gameobjects(dynamic_gameobjects), + m_global_blackboard(global_blackboard), + m_lootables(lootables) + +{ + +} + +TransformComponent* PhysicsSystem::getTransform(std::shared_ptr<GameObject> &go){ + return go->getComponent<TransformComponent>(); +} + +float PhysicsSystem::gravitySimulation(float &initial_v, double deltaTime, float snapshot_time, float gravity){ + float t = deltaTime-snapshot_time; + float delta_y = initial_v*t + (.5f)*gravity*t*t; + return delta_y; +} + + +void PhysicsSystem::update(double deltaTime){ + //std::cout << "physics" << std::endl; + float dt = deltaTime - snapshot_time; + snapshot_time = deltaTime; + + for (auto &go : m_dynamic_gameobjects){ + // player has its own physics + if (go.first != "player"){ + // position + glm::vec3 m_pos = getTransform(go.second)->getPos(); + getTransform(go.second)->old_pos = m_pos; + + // effect it by gravity + m_pos.y += gravitySimulation(getTransform(go.second)->yVelocity, dt, 0, getTransform(go.second)->gravity); + getTransform(go.second)->yVelocity = getTransform(go.second)->yVelocity + getTransform(go.second)->gravity*(dt); + + m_global_blackboard[go.first].locationData.setToPos = m_pos; + // store m_pos as estimated final_pos + getTransform(go.second)->estimated_final_pos = m_pos; + } + } + +// for (auto &lootGroup : m_lootables){ +// for (auto &loot : lootGroup.second){ +// // position +// glm::vec3 m_pos = getTransform(loot)->getPos(); +// getTransform(loot)->old_pos = m_pos; + +// // effect it by gravity +// m_pos.y += gravitySimulation(getTransform(loot)->yVelocity, dt, 0, getTransform(loot)->gravity); +// getTransform(loot)->yVelocity = getTransform(loot)->yVelocity + getTransform(loot)->gravity*(dt); + +// // store m_pos as estimated final_pos +// getTransform(loot)->estimated_final_pos = m_pos; +// } + +// } + +} + + + + +void PhysicsSystem::draw(){} +void PhysicsSystem::scrollEvent(double distance){} +void PhysicsSystem::mousePosEvent(double xpos, double ypos){} + diff --git a/engine-ocean/Game/Systems/physicssystem.h b/engine-ocean/Game/Systems/physicssystem.h new file mode 100644 index 0000000..fc9abbb --- /dev/null +++ b/engine-ocean/Game/Systems/physicssystem.h @@ -0,0 +1,30 @@ +#ifndef PHYSICSSYSTEM_H +#define PHYSICSSYSTEM_H +#include "Game/Components/TransformComponent.h" +#include "system.h" + + +class PhysicsSystem : public System +{ +public: + PhysicsSystem(std::map<std::string, std::shared_ptr<GameObject>>& dynamic_gameobjects, + std::map<std::string, BlackboardData>& global_blackboard, + std::map<std::string, std::vector<std::shared_ptr<GameObject>>>& lootables); + void draw() override; + void update(double deltaTime) override; + void scrollEvent(double distance) override; + void mousePosEvent(double xpos, double ypos) override; + +private: + float gravitySimulation(float &initial_v, double deltaTime, float snapshot_time, float gravity); + TransformComponent* getTransform(std::shared_ptr<GameObject> &go); + + std::map<std::string, std::shared_ptr<GameObject>>& m_dynamic_gameobjects; + std::map<std::string, BlackboardData>& m_global_blackboard; + std::map<std::string, std::vector<std::shared_ptr<GameObject>>>& m_lootables; + + float horiz_velocity = .005f; + float snapshot_time = 0.f; +}; + +#endif // PHYSICSSYSTEM_H diff --git a/engine-ocean/Game/Systems/system.cpp b/engine-ocean/Game/Systems/system.cpp new file mode 100644 index 0000000..764114c --- /dev/null +++ b/engine-ocean/Game/Systems/system.cpp @@ -0,0 +1,6 @@ +#include "system.h" + +System::System() +{ + +} diff --git a/engine-ocean/Game/Systems/system.h b/engine-ocean/Game/Systems/system.h new file mode 100644 index 0000000..4abca3b --- /dev/null +++ b/engine-ocean/Game/Systems/system.h @@ -0,0 +1,17 @@ +#ifndef SYSTEM_H +#define SYSTEM_H + + +#include "Game/GameObjects/GameObject.h" +#include <map> +class System +{ +public: + System(); + virtual void draw() = 0; + virtual void update(double deltaTime) = 0; + virtual void scrollEvent(double distance) = 0; + virtual void mousePosEvent(double xpos, double ypos) = 0; +}; + +#endif // SYSTEM_H |