This commit is contained in:
Martin Slachta
2026-07-18 14:31:15 +02:00
commit a04f0dc262
3343 changed files with 1140208 additions and 0 deletions
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#define GET_REF(attribute, name) \
inline const typeof(attribute)& name() const { return attribute; }
#define GET_MUT_REF(attribute, name) \
GET_REF(attribute, name) \
inline typeof(attribute)& name() { return attribute; }
#define GET(attribute, name) \
inline typeof(attribute) name() const { return attribute; }
+80
View File
@@ -0,0 +1,80 @@
#include "Files.hpp"
#include <cstring>
#include <filesystem>
#include <fstream>
#include <ios>
#include <print>
#if __linux__
#include <linux/limits.h>
#include <unistd.h>
#include <err.h>
#elif _WIN32
#include <windows.h>
#else
#error "Unsupported platform"
#endif
namespace tw::io {
#ifdef __linux__
const char DIRECTORY_SEPARATOR = '/';
#elif _WIN32
const char DIRECTORY_SEPARATOR = '\\';
#else
#error "Unsupported platform"
#endif
#include "IoException.hpp"
Files::Files(char** argv, int32_t argc) {
char *pBuffer = nullptr;
#ifdef __linux__
pBuffer = (char*)malloc(PATH_MAX);
size_t bytes = readlink("/proc/self/exe", pBuffer, PATH_MAX);
pBuffer[bytes] = '\0';
#elif _WIN32
pBuffer = (char*)malloc(_MAX_PATH);
int bytes = GetModuleFileName(NULL, pBuffer, _MAX_PATH);
if(bytes >= 0)
pBuffer[bytes] = '\0';
#else
#error "Unsupported platform"
#endif
unsigned int lastSlash = 0;
for(size_t i = bytes; i > 0; i--) {
if(pBuffer[i] == DIRECTORY_SEPARATOR) {
lastSlash = i + 1;
break;
}
}
pBuffer[lastSlash] = '\0';
m_exe_path = std::filesystem::path(std::string(pBuffer));
}
std::filesystem::path Files::get_path_of(const path& name) const {
return m_exe_path / name;
}
std::vector<uint32_t> Files::read_file_binary(const path& name) const {
auto path = get_path_of(name);
std::ifstream stream(path, std::ios::binary | std::ios::ate);
if(stream.fail()) {
throw tw::io::IoException(path, std::strerror(errno));
}
size_t length = stream.tellg();
std::vector<uint32_t> content(length);
stream.seekg(0);
stream.read((char*)content.data(), length);
stream.close();
return content;
}
}
+27
View File
@@ -0,0 +1,27 @@
#pragma once
#include <cstdint>
#include <string>
#include <vector>
#include <filesystem>
namespace tw::io {
using namespace std::filesystem;
/**
* Manages access to a application specific directory for storing assets.
*/
class Files {
path m_exe_path;
public:
Files(char** argv, int32_t argc);
path get_path_of(const path& path) const;
std::vector<uint32_t> read_file_binary(const path& path) const;
};
}
+128
View File
@@ -0,0 +1,128 @@
#pragma once
#include <SDL_keycode.h>
#include <SDL_mouse.h>
#include <imgui_impl_sdl2.h>
#include <Window.hpp>
#include "common.hpp"
#include "debug/ComponentGui.hpp"
#include "imgui.h"
namespace tw::io {
class InputManager {
private:
lft::win::Window* m_window;
bool m_is_quit;
float m_velocity_x;
float m_velocity_y;
bool m_is_jumping;
bool m_is_pressed[4];
float m_motion_x;
float m_motion_y;
public:
GET(m_is_quit, is_quit);
float axis_y() const {
float result = 0.0f;
if(m_is_pressed[0]) {
result += 1.0f;
} if(m_is_pressed[1]) {
result -= 1.0f;
}
return result;
}
float axis_x() const {
float result = 0.0f;
if(m_is_pressed[2]) {
result += 1.0f;
} if(m_is_pressed[3]) {
result -= 1.0f;
}
return result;
}
GET(m_motion_x, motion_x);
GET(m_motion_y, motion_y);
InputManager(lft::win::Window* window) :
m_window(window),
m_is_quit(false)
{
m_is_pressed[0] = m_is_pressed[1] = m_is_pressed[2] = m_is_pressed[3] = false;
}
void update() {
SDL_Event event;
m_motion_x = 0.0f;
m_motion_y = 0.0f;
while(m_window->poll_event(&event)) {
ImGui_ImplSDL2_ProcessEvent(&event);
switch(event.type) {
case SDL_QUIT:
m_is_quit = true;
break;
case SDL_KEYDOWN:
case SDL_KEYUP: {
switch(event.key.keysym.sym) {
case SDLK_w:
m_is_pressed[0] = event.key.state;
break;
case SDLK_s:
m_is_pressed[1] = event.key.state;
break;
case SDLK_a:
m_is_pressed[2] = event.key.state;
break;
case SDLK_d:
m_is_pressed[3] = event.key.state;
break;
}
} break;
case SDL_MOUSEMOTION:
m_motion_x = event.motion.xrel;
m_motion_y = event.motion.yrel;
break;
}
}
}
};
class InputState {
public:
};
}
namespace tw::dbg {
template<>
class ComponentGui<tw::io::InputManager> {
public:
void draw(tw::io::InputManager* instance) {
ImGui::SeparatorText("Character Body Component");
float motion[2] = {instance->motion_x(), instance->motion_y()};
ImGui::InputFloat2("Motion", motion);
float axis[2] = {instance->axis_x(), instance->axis_y()};
ImGui::InputFloat2("Axis", axis);
}
};
}
+24
View File
@@ -0,0 +1,24 @@
#pragma once
#include <exception>
#include <string>
#include <format>
namespace tw::io {
class IoException : public std::exception {
std::string m_filename;
std::string m_message;
public:
IoException(std::string filename, std::string what) :
m_message(std::format("{}: {}", filename, what)),
m_filename(filename)
{ }
const char* what() const noexcept override { return m_message.c_str(); }
const std::string& get_filename() const { return m_filename; }
};
}
+10
View File
@@ -0,0 +1,10 @@
#pragma once
namespace tw {
class InputManager {
public:
void set_axis();
};
}
+85
View File
@@ -0,0 +1,85 @@
#pragma once
#include <chrono>
#include <thread>
#include <print>
namespace tw {
/**
* Controls thread sleep to run exactly `n` times per second
*/
class LockStep {
using Clock = std::chrono::steady_clock;
using Millis = std::chrono::microseconds;
const uint64_t IN_SECOND = std::chrono::duration_cast<Millis>(std::chrono::seconds(1)).count();
uint64_t milliseconds;
std::chrono::time_point<Clock> m_last_point;
uint64_t m_last_interval_ms;
bool m_popped;
public:
LockStep(uint32_t steps_per_second) {
m_last_point = Clock::now();
set_steps_per_second(steps_per_second);
}
inline uint64_t milliseconds_per_frame() const {
return milliseconds;
}
inline void set_steps_per_second(uint32_t steps_per_second) {
milliseconds = IN_SECOND / steps_per_second;
}
inline uint64_t wait_time_in_ms() {
return m_last_interval_ms;
}
inline uint64_t fps() {
if(m_last_interval_ms == 0) {
return IN_SECOND;
}
return IN_SECOND / m_last_interval_ms;
}
inline double delta_time() const {
return m_last_interval_ms / (double)IN_SECOND;
}
bool update() {
auto now = Clock::now();
m_last_interval_ms = duration_cast<Millis>(now - m_last_point).count();
if(m_last_interval_ms >= milliseconds) {
m_last_point = now;
return true;
}
return false;
}
/**
* Waits if time interval from previous call is less than time between two steps
*/
bool wait_for_next_step() {
auto now = Clock::now();
m_last_interval_ms = duration_cast<Millis>(now - m_last_point).count();
if(m_last_interval_ms < milliseconds) {
std::this_thread::sleep_for(Millis(milliseconds - m_last_interval_ms));
return true;
}
m_last_point = now;
return false;
}
};
}
+22
View File
@@ -0,0 +1,22 @@
#pragma once
#include "metrics/HistoryBuffer.hpp"
#include <glm/glm.hpp>
#include <Jolt/Jolt.h>
#include <Jolt/Physics/Character/CharacterVirtual.h>
namespace tw {
class CharacterBody {
public:
JPH::CharacterVirtual* m_character;
JPH::Vec3 m_desired_velocity;
CharacterBody(JPH::CharacterVirtual* character) :
m_character(character)
{ }
};
}
+62
View File
@@ -0,0 +1,62 @@
#pragma once
#include <chrono>
#include <glm/glm.hpp>
#include "metrics/HistoryBuffer.hpp"
namespace tw {
/**
* Controls the character's body
*/
class CharacterController {
float m_speed;
using Clock = std::chrono::steady_clock;
HistoryBuffer<Clock::time_point, glm::vec3> m_history;
HistoryBuffer<Clock::time_point, glm::vec3> m_position_history;
glm::vec3 m_input;
uint32_t m_frame_idx;
public:
GET(m_speed, speed);
GET_MUT_REF(m_history, input_history);
GET_MUT_REF(m_position_history, position_history);
CharacterController(float speed) :
m_speed(speed),
m_history(Clock::now(), glm::vec3(), 10 * 20),
m_position_history(Clock::now(), glm::vec3(), 10 * 20),
m_frame_idx(0)
{ }
void set_input(uint32_t frame_idx, glm::vec3 input) {
// m_history.set(frame_idx, input);
m_input = input;
}
void set_frame_idx(uint32_t idx) {
m_frame_idx = idx;
}
glm::vec3 input() const {
return m_input;
// return m_history.values()[m_history.values().size() - 1];
}
glm::vec3 input(uint32_t frame_idx) const {
return m_input;
// auto value = m_history.get(frame_idx);
// if(value.has_value()) {
// return *value.value();
// }
// return glm::vec3();
}
};
}
+12
View File
@@ -0,0 +1,12 @@
#include "CharacterSystem.hpp"
#include <glm/gtc/matrix_transform.hpp>
namespace tw {
void CharacterSystem::add_character(glm::vec3 initial_position) {
glm::mat4 transform = glm::translate(glm::mat4(), initial_position);
m_positions.push_back(transform);
}
}
+19
View File
@@ -0,0 +1,19 @@
#pragma once
#include <glm/glm.hpp>
#include <vector>
namespace tw {
class CharacterSystem {
private:
std::vector<glm::mat4> m_positions;
public:
/**
* Character type agnostic function
*/
void add_character(glm::vec3 initial_position);
};
}
+28
View File
@@ -0,0 +1,28 @@
#pragma once
#include <vector>
#include <cstdint>
#include <glm/glm.hpp>
namespace tw {
#define CHUNK_WIDTH 16
struct ChunkData {
using Type = uint8_t;
std::vector<Type> data;
ChunkData() : data(CHUNK_WIDTH * CHUNK_WIDTH * CHUNK_WIDTH)
{ }
Type get(glm::ivec3 position) {
assert(position.x >= 0 && position.x < CHUNK_WIDTH);
assert(position.y >= 0 && position.y < CHUNK_WIDTH);
assert(position.z >= 0 && position.z < CHUNK_WIDTH);
return data[position.x + position.y * CHUNK_WIDTH + position.z * CHUNK_WIDTH * CHUNK_WIDTH];
}
};
}
+18
View File
@@ -0,0 +1,18 @@
#pragma once
#include <cstdint>
#include <glm/glm.hpp>
#include <optional>
#include "ChunkData.hpp"
namespace tw {
class ChunkManager {
public:
ChunkManager(uint32_t render_distance);
std::optional<ChunkData&> get_chunk(glm::ivec2 position);
};
}
+26
View File
@@ -0,0 +1,26 @@
#pragma once
#include "ChunkData.hpp"
#include "draw/MeshData.hpp"
namespace tw {
constexpr std::vector<uint8_t> CHUNK_VERTICES() {
}
class ChunkMeshData {
std::vector<glm::vec3> m_vertices;
std::vector<glm::vec3> m_normal;
std::vector<uint16_t> m_indices;
public:
ChunkMeshData(const ChunkData& chunk) {
auto mesh_data = drw::MeshData::plane(glm::vec2(8.0f, 8.0f));
}
};
}
+220
View File
@@ -0,0 +1,220 @@
#include "JoltPhysicsWorld.hpp"
#include <cmath>
#include <iostream>
#include <print>
#include <cstring>
#include <tracy/Tracy.hpp>
#include "Jolt/Core/TempAllocator.h"
#include "Jolt/Physics/Character/Character.h"
#include "Jolt/Core/Factory.h"
#include "Jolt/Core/JobSystemThreadPool.h"
#include "Jolt/Core/Memory.h"
#include "Jolt/Physics/Collision/BroadPhase/BroadPhaseLayer.h"
#include "Jolt/Physics/EActivation.h"
#include "Jolt/Physics/StateRecorderImpl.h"
#include "Jolt/RegisterTypes.h"
#include "World.hpp"
#include "world/CharacterBody.hpp"
#include "world/CharacterController.hpp"
#include "world/RigidBody.hpp"
#include "world/Transform.hpp"
namespace tw {
bool JoltPhysicsWorld::initialization() {
JPH::RegisterDefaultAllocator();
JPH::Factory::sInstance = new JPH::Factory();
JPH::RegisterTypes();
return true;
}
JoltPhysicsWorld::JoltPhysicsWorld(World* world) :
m_is_initialized(initialization()),
temp_allocator(std::make_unique<JPH::TempAllocatorImpl>(10 * 1024 * 1024)),
job_system(JPH::cMaxPhysicsJobs, JPH::cMaxPhysicsBarriers, std::thread::hardware_concurrency() - 1),
m_world(world),
m_history(0, std::move(std::make_unique<JPH::StateRecorderImpl>()), 10),
m_latest_frame(0),
broad_phase_layer_interface(),
object_vs_broadphase_layer_filter(),
object_vs_object_layer_filter(),
physics_system(),
body_activation_listener(),
contact_listener(),
m_character_vs_character_collision()
{
// for(int i = 0; i < m_thread_pool.size(); i++) {
// temp_allocator.emplace_back(std::make_unique<JPH::TempAllocatorImpl>(10 * 1024 * 1024));
// }
// for(auto& allocator : temp_allocator) {
// // allocator = std::make_unique<JPH::TempAllocatorImpl>(10 * 1024 * 1024);
// }
const uint32_t MAX_BODIES = 1024;
const uint32_t BODY_MUTEXES = 0;
const uint32_t MAX_BODY_PAIRS = 1024;
const uint32_t MAX_CONTACT_CONSTRAINTS = 1024;
physics_system.Init(MAX_BODIES, BODY_MUTEXES, MAX_BODY_PAIRS, MAX_CONTACT_CONSTRAINTS,
broad_phase_layer_interface,
object_vs_broadphase_layer_filter,
object_vs_object_layer_filter);
physics_system.SetBodyActivationListener(&body_activation_listener);
physics_system.SetContactListener(&contact_listener);
JPH::BoxShapeSettings floor_shape_settings(JPH::Vec3(100.0f, 1.0f, 100.0f));
floor_shape_settings.SetEmbedded();
JPH::ShapeSettings::ShapeResult floor_shape_result = floor_shape_settings.Create();
JPH::ShapeRefC floor_shape = floor_shape_result.Get();
JPH::BodyCreationSettings floor_settings(
floor_shape, JPH::RVec3(0.0, 0., 0.0),
JPH::Quat::sIdentity(),
JPH::EMotionType::Static,
Layers::NON_MOVING);
JPH::Body *floor = body_interface().CreateBody(floor_settings);
body_interface().AddBody(floor->GetID(), JPH::EActivation::DontActivate);
// Now you can interact with the dynamic body, in this case we're going to give it a velocity.
// (note that if we had used CreateBody then we could have set the velocity straight on the body before adding it to the physics system)
// body_interface().SetLinearVelocity(sphere_id, JPH::Vec3(0.0f, -5.0f, 0.0f));
}
void JoltPhysicsWorld::update(uint32_t frame_idx, double delta_time) {
auto view = m_world->registry().view<CharacterController, CharacterBody>();
view.each([&](entt::entity entity, const CharacterController& controller, CharacterBody& rb) {
auto allocator = temp_allocator.get();
ZoneScoped;
ZoneNameF("CharacterController update %d", (uint32_t)0);
auto character = rb.m_character;
bool player_controls_horizontal_velocity = true || character->IsSupported();
bool sEnableCharacterInertia = true;
bool mAllowSliding = true;
if (player_controls_horizontal_velocity) {
glm::vec3 input = controller.input(frame_idx);
// Smooth the player input
JPH::Vec3 vel = JPH::Vec3(input.x, input.y, input.z) * controller.speed();
float inertia_factor = 1.0f - std::exp(-delta_time * 10.0f); // 10.0 = responsiveness tuning knob
rb.m_desired_velocity = sEnableCharacterInertia
? rb.m_desired_velocity + inertia_factor * (vel - rb.m_desired_velocity)
: vel;
// True if the player intended to move
mAllowSliding = input.length() < 0.01f; // allow sliding when idle, prevent when moving
}
JPH::Vec3 current_vertical_velocity = character->GetLinearVelocity().Dot(character->GetUp()) * character->GetUp();
JPH::Vec3 ground_velocity = character->GetGroundVelocity();
JPH::Vec3 new_velocity;
bool moving_towards_ground = (current_vertical_velocity.GetY() - ground_velocity.GetY()) < 0.1f;
if (character->GetGroundState() == JPH::CharacterVirtual::EGroundState::OnGround // If on ground
&& (sEnableCharacterInertia ?
moving_towards_ground // Inertia enabled: And not moving away from ground
: !character->IsSlopeTooSteep(character->GetGroundNormal()))) // Inertia disabled: And not on a slope that is too steep
{
// Assume velocity of ground when on ground
new_velocity = ground_velocity;
// Jump
// if (inJump && moving_towards_ground)
// new_velocity += sJumpSpeed * mCharacter->GetUp();
}
else {
new_velocity = current_vertical_velocity;
}
auto character_up_rotation = character->GetUp();
// Gravity
new_velocity += (physics_system.GetGravity()) * delta_time;
new_velocity += rb.m_desired_velocity;
if (player_controls_horizontal_velocity)
{
// Player input
}
else
{
// Preserve horizontal velocity
JPH::Vec3 current_horizontal_velocity = character->GetLinearVelocity() - current_vertical_velocity;
new_velocity += current_horizontal_velocity;
}
// Update character velocity
character->SetLinearVelocity(new_velocity);
// auto velocity = character->GetLinearVelocity() + physics_system.GetGravity() * delta_time ;
// character->SetLinearVelocity(velocity);
JPH::CharacterVirtual::ExtendedUpdateSettings update_settings = {};
update_settings.mStickToFloorStepDown = mAllowSliding
? JPH::Vec3(0, -0.5f, 0) // snap to floor when idle
: JPH::Vec3::sZero();
update_settings.mWalkStairsStepUp = JPH::Vec3(0, 0.4f, 0); // re-enable stair stepping
{
ZoneScopedN("Extended update");
character->ExtendedUpdate(delta_time,
-character->GetUp() * physics_system.GetGravity().Length(),
update_settings,
physics_system.GetDefaultBroadPhaseLayerFilter(Layers::MOVING),
physics_system.GetDefaultLayerFilter(Layers::MOVING),
{},
{},
*allocator);
}
});
physics_system.Update(delta_time, 1, temp_allocator.get(), &job_system);
auto stateRecorder = std::make_shared<JPH::StateRecorderImpl>();
physics_system.SaveState(*stateRecorder.get());
m_history.set(frame_idx, stateRecorder);
}
void JoltPhysicsWorld::step(uint32_t frame_idx, double delta_time) {
update(frame_idx, delta_time);
m_world->registry().view<Transform, CharacterBody>()
.each([&](Transform& ts, CharacterBody& rb) {
auto character = rb.m_character;
auto transform = character->GetWorldTransform();
memcpy(&ts.transform, &transform, sizeof(glm::mat4));
});
// copy values
// m_world->registry().view<Transform, RigidBody>()
// .each([&](Transform& ts, RigidBody& rb) {
// auto transform = body_interface().GetWorldTransform(rb.id);
// memcpy(&ts.transform, &transform, sizeof(glm::mat4));
// });
//
// m_world->registry().view<Transform, CharacterBody>()
// .each([&](Transform& ts, CharacterBody& rb) {
// auto character = rb.m_character;
//
// auto transform = character->GetWorldTransform();
// memcpy(&ts.transform, &transform, sizeof(glm::mat4));
// });
m_latest_frame = std::max(m_latest_frame, frame_idx);
}
}
+266
View File
@@ -0,0 +1,266 @@
#pragma once
#include "world/RigidBody.hpp"
#include <iostream>
#include <print>
#include "metrics/HistoryBuffer.hpp"
#include <glm/glm.hpp>
#include <Jolt/Jolt.h>
#include <Jolt/Physics/StateRecorderImpl.h>
#include <Jolt/RegisterTypes.h>
#include <Jolt/Core/Factory.h>
#include <Jolt/Core/TempAllocator.h>
#include <Jolt/Core/JobSystemThreadPool.h>
#include <Jolt/Physics/PhysicsSettings.h>
#include <Jolt/Physics/PhysicsSystem.h>
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
#include <Jolt/Physics/Collision/Shape/SphereShape.h>
#include <Jolt/Physics/Body/BodyCreationSettings.h>
#include <Jolt/Physics/Body/BodyActivationListener.h>
#include <Jolt/Physics/Character/CharacterVirtual.h>
namespace tw {
namespace Layers
{
static constexpr JPH::ObjectLayer NON_MOVING = 0;
static constexpr JPH::ObjectLayer MOVING = 1;
static constexpr JPH::ObjectLayer NUM_LAYERS = 2;
};
/// Class that determines if two object layers can collide
class ObjectLayerPairFilterImpl : public JPH::ObjectLayerPairFilter
{
public:
virtual bool ShouldCollide(JPH::ObjectLayer inObject1, JPH::ObjectLayer inObject2) const override
{
switch (inObject1)
{
case Layers::NON_MOVING:
return true; // return inObject2 == Layers::MOVING; // Non moving only collides with moving
case Layers::MOVING:
return inObject2 == Layers::NON_MOVING; // Moving collides with everything
default:
JPH_ASSERT(false);
return false;
}
}
};
namespace BroadPhaseLayers
{
static constexpr JPH::BroadPhaseLayer NON_MOVING(0);
static constexpr JPH::BroadPhaseLayer MOVING(1);
static constexpr uint NUM_LAYERS(2);
};
class BPLayerInterfaceImpl final : public JPH::BroadPhaseLayerInterface
{
public:
BPLayerInterfaceImpl()
{
// Create a mapping table from object to broad phase layer
mObjectToBroadPhase[Layers::NON_MOVING] = BroadPhaseLayers::NON_MOVING;
mObjectToBroadPhase[Layers::MOVING] = BroadPhaseLayers::MOVING;
}
virtual uint GetNumBroadPhaseLayers() const override
{
return BroadPhaseLayers::NUM_LAYERS;
}
virtual JPH::BroadPhaseLayer GetBroadPhaseLayer(JPH::ObjectLayer inLayer) const override
{
JPH_ASSERT(inLayer < Layers::NUM_LAYERS);
return mObjectToBroadPhase[inLayer];
}
#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED)
virtual const char * GetBroadPhaseLayerName(JPH::BroadPhaseLayer inLayer) const override
{
switch ((JPH::BroadPhaseLayer::Type)inLayer)
{
case (JPH::BroadPhaseLayer::Type)BroadPhaseLayers::NON_MOVING: return "NON_MOVING";
case (JPH::BroadPhaseLayer::Type)BroadPhaseLayers::MOVING: return "MOVING";
default: JPH_ASSERT(false); return "INVALID";
}
}
#endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED
private:
JPH::BroadPhaseLayer mObjectToBroadPhase[Layers::NUM_LAYERS];
};
class ObjectVsBroadPhaseLayerFilterImpl : public JPH::ObjectVsBroadPhaseLayerFilter
{
public:
virtual bool ShouldCollide(JPH::ObjectLayer inLayer1, JPH::BroadPhaseLayer inLayer2) const override
{
switch (inLayer1)
{
case Layers::NON_MOVING:
return true;
// return inLayer2 == BroadPhaseLayers::MOVING;
case Layers::MOVING:
return inLayer2 == BroadPhaseLayers::NON_MOVING;
default:
JPH_ASSERT(false);
return false;
}
}
};
class MyContactListener : public JPH::ContactListener
{
public:
// See: ContactListener
virtual JPH::ValidateResult OnContactValidate(
const JPH::Body &inBody1,
const JPH::Body &inBody2,
JPH::RVec3Arg inBaseOffset,
const JPH::CollideShapeResult &inCollisionResult
) override {
std::cout << "Contact validate callback" << std::endl;
// Allows you to ignore a contact before it is created (using layers to not make objects collide is cheaper!)
return JPH::ValidateResult::AcceptAllContactsForThisBodyPair;
}
virtual void OnContactAdded(
const JPH::Body &inBody1,
const JPH::Body &inBody2,
const JPH::ContactManifold &inManifold,
JPH::ContactSettings &ioSettings
) override
{
std::cout << "A contact was added" << std::endl;
}
virtual void OnContactPersisted(
const JPH::Body &inBody1,
const JPH::Body &inBody2,
const JPH::ContactManifold &inManifold,
JPH::ContactSettings &ioSettings
) override
{
std::cout << "A contact was persisted" << std::endl;
}
virtual void OnContactRemoved(const JPH::SubShapeIDPair &inSubShapePair) override
{
std::cout << "A contact was removed" << std::endl;
}
};
class MyBodyActivationListener : public JPH::BodyActivationListener
{
public:
virtual void OnBodyActivated(const JPH::BodyID &inBodyID, uint64_t inBodyUserData) override
{
std::cout << "A body got activated" << std::endl;
}
virtual void OnBodyDeactivated(const JPH::BodyID &inBodyID, uint64_t inBodyUserData) override
{
std::cout << "A body went to sleep" << std::endl;
}
};
class World;
class JoltPhysicsWorld {
private:
static bool initialization();
bool m_is_initialized;
std::unique_ptr<JPH::TempAllocatorImpl> temp_allocator;
JPH::JobSystemThreadPool job_system;
BPLayerInterfaceImpl broad_phase_layer_interface;
ObjectVsBroadPhaseLayerFilterImpl object_vs_broadphase_layer_filter;
ObjectLayerPairFilterImpl object_vs_object_layer_filter;
JPH::PhysicsSystem physics_system;
MyBodyActivationListener body_activation_listener;
MyContactListener contact_listener;
JPH::CharacterVsCharacterCollisionSimple m_character_vs_character_collision;
HistoryBuffer<int, std::shared_ptr<JPH::StateRecorderImpl>> m_history;
inline JPH::BodyInterface &body_interface() {
return physics_system.GetBodyInterface();
}
JPH::BodyID sphere_id;
World* m_world;
uint32_t m_latest_frame;
void update(uint32_t frame_idx, double delta_time);
public:
JoltPhysicsWorld(World* world);
RigidBody create_dynamic_rigid_body(JPH::Shape* shape, glm::vec3 position) {
JPH::BodyCreationSettings settings(
shape,
JPH::RVec3(position.x, position.y, position.z),
JPH::Quat::sIdentity(),
JPH::EMotionType::Dynamic,
Layers::MOVING);
auto id = body_interface().CreateAndAddBody(settings, JPH::EActivation::Activate);
return {id};
}
JPH::CharacterVirtual* create_character(JPH::Shape* shape, glm::vec3 position) {
JPH::CharacterVirtualSettings settings;
settings.mShape = shape;
settings.mSupportingVolume = JPH::Plane(JPH::Vec3::sAxisY(), -0.5f);
auto* character = new JPH::CharacterVirtual(&settings,
JPH::RVec3(position.x, position.y, position.z),
JPH::Quat::sIdentity(),
0, &physics_system);
character->SetCharacterVsCharacterCollision(&m_character_vs_character_collision);
// m_character_vs_character_collision.Add(character);
return character;
}
void move_character(JPH::CharacterVirtual* character, glm::vec3 force) {
character->SetLinearVelocity(JPH::Vec3(force.x, force.y, force.z));
}
void add_force(RigidBody& rigidbody, glm::vec3 force) {
body_interface().AddForce(rigidbody.id, JPH::RVec3(force.x, force.y, force.z));
}
void step(uint32_t frame_idx, double delta_time);
void rollback(uint32_t frame) {
if(frame == 0) return;
auto snapshot = m_history.get(frame);
if(!snapshot.has_value()) {
throw std::runtime_error("Failed to restore history");
}
auto snapshot_value = snapshot.value()->get();
physics_system.RestoreState(*snapshot_value);
}
void apply_rollback(uint32_t frame) {
for(uint32_t i = frame; i < m_latest_frame; i++) {
update(i, 1000.0f / 20.0f);
}
}
};
}
+15
View File
@@ -0,0 +1,15 @@
#pragma once
#include <Jolt/Jolt.h>
#include <Jolt/Physics/Body/BodyID.h>
namespace tw {
class RigidBody {
public:
JPH::BodyID id;
};
}
+52
View File
@@ -0,0 +1,52 @@
#pragma once
// #include "FastNoise/Generators/Fractal.h"
// #include "FastNoise/SmartNode.h"
#include <vector>
#include <cstdint>
#include <glm/glm.hpp>
// #include <FastNoise/FastNoise.h>
namespace tw {
class Chunk {
public:
std::vector<uint8_t> m_voxels;
};
class TerrainGenerator {
uint32_t m_seed;
// FastNoise::SmartNode<FastNoise::Simplex> m_fn_simplex;
// FastNoise::SmartNode<FastNoise::FractalFBm> m_fn_fractal;
public:
TerrainGenerator(uint32_t seed) {
m_seed = seed;
// m_fn_simplex = FastNoise::New<FastNoise::Simplex>();
// m_fn_fractal = FastNoise::New<FastNoise::FractalFBm>();
//
// m_fn_fractal->SetSource(m_fn_simplex);
// m_fn_fractal->SetOctaveCount(5);
}
Chunk generate(glm::ivec3 offset) {
// std::vector<float> noiseOutput(16 * 16);
// m_fn_simplex->GenUniformGrid2D(noiseOutput.data(), 0, 0, 16, 16, 0.2f, m_seed);
//
// std::vector<uint8_t> chunk(16 * 16 * 16);
//
// for(uint32_t y = 0; y < 16; y++) {
// for(uint32_t x = 0; x < 16; x++) {
// for(uint32_t z = 0; z < noiseOutput[x + y * 16] * 8.0f; z++) {
// chunk[x + y * 16 + z * 16 * 16] = 1;
// }
// }
// }
//
// return Chunk {chunk};
}
};
}
+55
View File
@@ -0,0 +1,55 @@
#pragma once
#include "glm/common.hpp"
#include "glm/ext/matrix_transform.hpp"
#include "glm/glm.hpp"
#include <spdlog/spdlog.h>
namespace tw {
struct Transform {
glm::mat4 transform;
public:
Transform(glm::vec3 translation) :
transform(glm::translate(glm::mat4(1.0f), translation))
{ }
Transform(glm::mat4 transform) :
transform(transform)
{ }
void set_position(glm::vec3 position) {
transform = glm::translate(glm::mat4(1.0f), position);
}
void translate(glm::vec3 translation) {
transform = glm::translate(transform, translation);
}
void look_at(glm::vec3 eye, glm::vec3 target) {
transform = glm::lookAt(eye, target, glm::vec3(0.0f, 1.0f, 0.0f));
}
inline constexpr glm::vec3 position() const {
return glm::vec3(transform[3][0], transform[3][1], transform[3][2]) * transform[3][3];
}
constexpr glm::vec3 right() const {
return glm::vec3(transform[2][0], transform[2][1], transform[2][2]) * transform[2][3];
}
constexpr bool is_closer_than(const Transform& other, float max_distance) const {
auto from = position();
auto to = other.position();
auto vector = to - from;
return vector.x * vector.x +
vector.y * vector.y +
vector.z * vector.z <
max_distance * max_distance;
}
};
}
+14
View File
@@ -0,0 +1,14 @@
#include "World.hpp"
#include <entt/entt.hpp>
namespace tw {
World::World()
{
}
void World::step(double delta_time) {
}
}
+24
View File
@@ -0,0 +1,24 @@
#pragma once
#include "common.hpp"
#include "entt/entt.hpp"
namespace tw {
class World {
protected:
entt::registry m_registry;
public:
GET_MUT_REF(m_registry, registry);
World();
World(const World&) = delete;
World& operator=(const World&) = delete;
void step(double delta_time);
};
}
+20
View File
@@ -0,0 +1,20 @@
#pragma once
#include <string>
#include <cstdint>
namespace tw {
class WorldEntity {
public:
std::string name;
uint32_t entity_id;
WorldEntity(std::string name, uint32_t entity_id) :
name(name),
entity_id(entity_id)
{
}
};
}