vktutorial/gravity_physics_system.hpp

129 lines
4.0 KiB
C++

#pragma once
#include <hk_game_object.hpp>
#define GLM_FORCE_RADIANS
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#include <glm/glm.hpp>
#include <glm/gtc/constants.hpp>
// std
#include <memory>
#include <random>
#include <vector>
namespace hk
{
class GravityPhysicsSystem
{
public:
GravityPhysicsSystem(float strength) :m_strengthGravity(strength) {}
void update(std::vector<GameObject> &objs, float dt, unsigned int substeps = 1)
{
const float stepDelta = dt / substeps;
for (int i = 0; i < substeps; i++)
{
stepSimulation(objs, stepDelta);
}
}
glm::vec2 computeForce(GameObject &fromObj, GameObject &toObj) const
{
auto offset = fromObj.m_transform2d.translation - toObj.m_transform2d.translation;
float distanceSquared = glm::dot(offset, offset);
if (glm::abs(distanceSquared < 1e-10f))
{
return {.0f, .0f};
}
float force = m_strengthGravity * toObj.m_rigidBody2d.mass * fromObj.m_rigidBody2d.mass / distanceSquared;
return force * offset / glm::sqrt(distanceSquared);
}
const float m_strengthGravity;
private:
void stepSimulation(std::vector<GameObject> &physicsObjs, float dt)
{
// Loops throught all pairs of objects and applies attractive force between them
for (auto iterA = physicsObjs.begin(); iterA != physicsObjs.end(); ++iterA)
{
auto &objA = *iterA;
for (auto iterB = iterA; iterB != physicsObjs.end(); ++iterB)
{
if (iterA == iterB) continue;
auto &objB = *iterB;
auto force = computeForce(objA, objB);
objA.m_rigidBody2d.velocity += dt * -force / objA.m_rigidBody2d.mass;
objB.m_rigidBody2d.velocity += dt * force / objB.m_rigidBody2d.mass;
}
}
// update each objects position based on its final velocity
for (auto &obj : physicsObjs)
{
obj.m_transform2d.translation += dt * obj.m_rigidBody2d.velocity;
}
}
};
class Vec2FieldSystem
{
public:
void update(const GravityPhysicsSystem &physicsSystem, std::vector<GameObject> &physicsObjs, std::vector<GameObject> &vectorField)
{
for (auto &vf : vectorField)
{
glm::vec2 direction{};
for (auto &obj : physicsObjs)
{
direction += physicsSystem.computeForce(obj, vf);
}
vf.m_transform2d.scale.x = 0.005f + 0.045f * glm::clamp(glm::log(glm::length(direction) + 1) / 3.f, 0.f, 1.f);
vf.m_transform2d.rotation = atan2(direction.y, direction.x);
}
}
};
std::unique_ptr<Model> createSquareModel(Device &device, glm::vec2 offset)
{
std::vector<Model::Vertex> vertices = {
{{-0.5f, -0.5f}}, {{0.5f, 0.5f}},
{{-0.5f, 0.5f}}, {{-0.5f, -0.5f}},
{{0.5f, -0.5f}}, {{0.5f, 0.5f}},
};
for (auto &v : vertices)
{
v.posision += offset;
}
return std::make_unique<Model>(device, vertices);
}
std::unique_ptr<Model> createCircleModel(Device &device, unsigned int numSides)
{
std::vector<Model::Vertex> uniqueVertices{};
for(int i = 0; i < numSides; i++)
{
float angle = i * glm::two_pi<float>() / numSides;
uniqueVertices.push_back({{glm::cos(angle), glm::sin(angle)}});
}
uniqueVertices.push_back({});
std::vector<Model::Vertex> vertices{};
for(int i = 0; i < numSides; i++)
{
vertices.push_back(uniqueVertices[i]);
vertices.push_back(uniqueVertices[(i+1) % numSides]);
vertices.push_back(uniqueVertices[numSides]);
}
return std::make_unique<Model>(device, vertices);
}
}