Commit 7a51061f authored by Shu's avatar Shu 🍠
Browse files

Ray basic collision

parent 2f3335fe
......@@ -23,6 +23,8 @@
- [ ] Inheritance
- [ ] ECS
- [ ] Relative to area
- [ ] Player as entity
- [ ] Entities block world changes
- [x] Area
- [x] Offset
- [ ] Rotation
......@@ -76,7 +78,7 @@
- [ ] LOD
- [x] Dual MC
- [ ] Octree
- [ ] Collision
- [~] Collision
- [ ] Dynamic index size
- [x] Chunk size performance
- [ ] Render with glBufferSubData
......
......@@ -3,8 +3,7 @@
#include <glm/gtc/matrix_transform.hpp>
#include "../render/window.hpp"
Camera::Camera(GLFWwindow *window, const InputMap& inputs, const Camera::options& opt): window(window), inputs(inputs),
Position(voxel_pos(0), 1), o(opt){
Camera::Camera(const Controllable* origin, const Camera::options& opt): origin(origin), o(opt) {
updateProjection();
}
Camera::~Camera() { }
......@@ -13,75 +12,13 @@ void Camera::updateProjection() {
ProjectionMatrix = glm::perspective(o.fov, RATIO, o.near, o.far);
}
void Camera::update(bool captureMouse, bool captureKeys, float deltaTime) {
// Get mouse position
if(captureMouse) {
int viewportX, viewportY;
glfwGetWindowSize(window, &viewportX, &viewportY);
if(capturingMouse) {
double xPos, yPos;
glfwGetCursorPos(window, &xPos, &yPos);
// Compute new orientation
HorizontalAngle += o.sensibility * 0.0001f * float(viewportX / 2 - xPos);
VerticalAngle += o.sensibility * 0.0001f * float(viewportY / 2 - yPos);
} else {
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
capturingMouse = true;
}
// Reset mouse position for next frame
glfwSetCursorPos(window, viewportX / 2, viewportY / 2);
} else if (capturingMouse) {
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
capturingMouse = false;
}
// Direction : Spherical coordinates to Cartesian coordinates conversion
glm::vec3 direction = getDirection();
// Right vector
glm::vec3 right = glm::vec3(
sin(HorizontalAngle - 3.14f / 2.0f),
0,
cos(HorizontalAngle - 3.14f / 2.0f));
// Up vector
glm::vec3 up = glm::cross(right, direction);
if(captureKeys) {
// Move forward
if (inputs.isDown(Input::Forward)) {
Position.offset += direction * deltaTime * o.speed;
}
// Move backward
if (inputs.isDown(Input::Backward)) {
Position.offset -= direction * deltaTime * o.speed;
}
// Strafe right
if (inputs.isDown(Input::Right)) {
Position.offset += right * deltaTime * o.speed;
}
// Strafe left
if (inputs.isDown(Input::Left)) {
Position.offset -= right * deltaTime * o.speed;
}
// Move up
if (inputs.isDown(Input::Up)) {
Position.offset += up * deltaTime * o.speed;
}
// Move down
if (inputs.isDown(Input::Down)) {
Position.offset -= up * deltaTime * o.speed;
}
Position.center();
}
// MAYBE: only if moved
// MAYBE: save frustum
void Camera::update() {
const auto &offset = origin->position.offset;
const auto axis = origin->getAxis();
// Camera matrix
ViewMatrix = glm::lookAt(
Position.offset, // Camera is here
Position.offset + direction, // and looks here : at the same position, plus "direction"
up // Head is up (set to 0,-1,0 to look upside-down)
offset, // Camera is here
offset + axis.direction, // and looks here : at the same position, plus "direction"
axis.up // Head is up (set to 0,-1,0 to look upside-down)
);
}
\ No newline at end of file
#pragma once
#include "InputMap.hpp"
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "../data/glm.hpp"
#include "Controllable.hpp"
#include "../data/geometry/Frustum.hpp"
#include "../data/geometry/Ray.hpp"
#include "../world/position.h"
/// Moving perspective camera
class Camera {
......@@ -16,42 +11,33 @@ public:
float fov = glm::radians(70.f);
float near = 0.1;
float far = 64;
float speed = 5.0f;
int sensibility = 50;
};
Camera(GLFWwindow*, const InputMap&, const options&);
Camera(const Controllable*, const options&);
~Camera();
void update(bool captureMouse, bool captureKeys, float deltaTime);
void update();
void setOptions(const options &options) {
o = options;
updateProjection();
}
void setOrigin(const Controllable* ct) {
origin = ct;
}
glm::vec3 getDirection() const { return glm::vec3(cos(VerticalAngle) * sin(HorizontalAngle), sin(VerticalAngle), cos(VerticalAngle) * cos(HorizontalAngle)); }
inline geometry::Frustum getFrustum() const { return geometry::Frustum(ViewMatrix, ProjectionMatrix); }
inline geometry::Ray getRay() const { return geometry::Ray(Position, getDirection(), o.far); }
inline geometry::Ray getRay() const { return geometry::Ray(origin->position, origin->getDirection(), o.far); }
constexpr glm::mat4 getViewMatrix() const { return ViewMatrix; }
constexpr glm::mat4 getProjectionMatrix() const { return ProjectionMatrix; }
camera_pos getPosition() const { return Position; }
constexpr float getDepth() const { return o.far; }
private:
GLFWwindow *window;
const InputMap &inputs;
const Controllable* origin;
glm::mat4 ViewMatrix;
glm::mat4 ProjectionMatrix;
void updateProjection();
camera_pos Position;
float HorizontalAngle = 3.14f;
float VerticalAngle = 0.0f;
bool capturingMouse = false;
options o;
};
#include "Controllable.hpp"
Controllable::Controllable(GLFWwindow *window, const InputMap& inputs, const Controllable::options& opt): position(voxel_pos(0), 1), window(window), inputs(inputs), o(opt){ }
Controllable::~Controllable() { }
Controllable::axis Controllable::getAxis() const {
Controllable::axis a;
// Direction : Spherical coordinates to Cartesian coordinates conversion
a.direction = getDirection();
// Right vector
a.right = glm::vec3(sin(HorizontalAngle - 3.14f / 2.0f), 0, cos(HorizontalAngle - 3.14f / 2.0f));
// Up vector
a.up = glm::cross(a.right, a.direction);
return a;
}
void Controllable::capture(bool captureMouse, bool captureKeys, float deltaTime) {
// Get mouse position
if(captureMouse) {
int viewportX, viewportY;
glfwGetWindowSize(window, &viewportX, &viewportY);
if(capturingMouse) {
double xPos, yPos;
glfwGetCursorPos(window, &xPos, &yPos);
// Compute new orientation
HorizontalAngle += o.sensibility * 0.0001f * float(viewportX / 2 - xPos);
VerticalAngle += o.sensibility * 0.0001f * float(viewportY / 2 - yPos);
} else {
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
capturingMouse = true;
}
// Reset mouse position for next frame
glfwSetCursorPos(window, viewportX / 2, viewportY / 2);
} else if (capturingMouse) {
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
capturingMouse = false;
}
const auto axis = getAxis();
velocity = glm::vec3(0);
if(captureKeys) {
// Move forward
if (inputs.isDown(Input::Forward)) {
velocity += axis.direction;
}
// Move backward
if (inputs.isDown(Input::Backward)) {
velocity -= axis.direction;
}
// Strafe right
if (inputs.isDown(Input::Right)) {
velocity += axis.right;
}
// Strafe left
if (inputs.isDown(Input::Left)) {
velocity -= axis.right;
}
// Move up
if (inputs.isDown(Input::Up)) {
velocity += axis.up;
}
// Move down
if (inputs.isDown(Input::Down)) {
velocity -= axis.up;
}
if(velocity != glm::vec3(0)) {
velocity = glm::normalize(velocity) * deltaTime * o.speed;
}
}
}
\ No newline at end of file
#pragma once
#include "InputMap.hpp"
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "../data/glm.hpp"
#include "../world/position.h"
/// Player controller moving entity
struct Controllable {
public:
struct options {
float speed = 5.0f;
int sensibility = 50;
bool collide = true;
};
Controllable(GLFWwindow*, const InputMap&, const options&);
~Controllable();
void capture(bool captureMouse, bool captureKeys, float deltaTime);
void setOptions(const options &options) {
o = options;
}
glm::vec3 getDirection() const { return glm::vec3(cos(VerticalAngle) * sin(HorizontalAngle), sin(VerticalAngle), cos(VerticalAngle) * cos(HorizontalAngle)); }
struct axis {
glm::vec3 direction;
glm::vec3 up;
glm::vec3 right;
};
axis getAxis() const;
camera_pos position;
glm::vec3 velocity;
private:
GLFWwindow *window;
const InputMap &inputs;
float HorizontalAngle = 3.14f;
float VerticalAngle = 0.0f;
bool capturingMouse = false;
options o;
};
......@@ -19,3 +19,6 @@ void ifvec3::center() {
raw += diff;
offset -= diff * static_cast<long>(IDX_LENGTH2);
}
double ifvec3::dist(const ifvec3& p) const {
return glm::length(glm::dvec3(raw - p.raw)) + glm::length(offset - p.offset);
}
......@@ -32,6 +32,7 @@ namespace glm {
void center();
glm::llvec3 raw_as_long() const;
glm::dvec3 as_double() const;
double dist(const ifvec3 &p) const;
inline const ifvec3 &operator+=(const offset_t &v) {
offset += v;
......@@ -39,6 +40,7 @@ namespace glm {
return *this;
}
inline ifvec3 operator+(const offset_t& v) const { return ifvec3(raw, offset + v); }
inline ifvec3 operator-(const offset_t &v) const { return ifvec3(raw, offset - v); }
inline ifvec3 operator/(int i) const { return ifvec3(raw / i, offset / (i * 1.f), false); }
inline ifvec3 operator*(int i) const { return ifvec3(raw * i, offset * (i * 1.f)); }
};
......
......@@ -52,7 +52,8 @@ int main(int /*unused*/, char */*unused*/[]){
glfwSwapInterval(static_cast<int>(options.target_fps < MIN_FPS));
InputMap inputs(window);
Camera camera(window, inputs, options.camera);
Controllable player(window, inputs, options.control);
Camera camera(&player, options.camera);
auto *renderer = new Renderer(options.renderer);
renderer->LightInvDir = glm::vec3(-0.5f, 2, -2);
......@@ -108,10 +109,17 @@ int main(int /*unused*/, char */*unused*/[]){
inputs.toggle(state.capture_mouse, Input::Mouse);
inputs.toggle(options.show_debug_menu, Input::Debug);
camera.update(state.capture_mouse, !UI::isFocus(), deltaTime);
player.capture(state.capture_mouse, !UI::isFocus(), deltaTime);
if(player.velocity != glm::vec3(0) && (
!options.control.collide ||
!world.collide(player.position, player.velocity, options.voxel_density, 1))
) {
player.position += player.velocity;
state.position = player.position;
}
camera.update();
renderer->lookFrom(camera);
state.position = camera.getPosition();
state.look_at = world.raycast(camera.getRay() * options.voxel_density);
if (state.capture_mouse) {
if (state.look_at.has_value()) {
......@@ -161,6 +169,9 @@ int main(int /*unused*/, char */*unused*/[]){
if(actions && UI::Actions::Camera) {
camera.setOptions(options.camera);
}
if(actions && UI::Actions::Control) {
player.setOptions(options.control);
}
if(actions && UI::Actions::ChangeContouring) {
state.contouring = NULL;
world.setContouring(contouring::load(options.contouring_idx, options.contouring_data));
......
......@@ -61,12 +61,12 @@ UI::Actions UI::draw(options &options, state &state, const reports &reports, GLu
ImGui::Separator();
ImGui::Checkbox("Overlay", &options.overlay_show);
if (ImGui::SliderInt("FPS", &options.target_fps, MIN_FPS-1, MAX_FPS+1, options.target_fps > MIN_FPS ? (options.target_fps < MAX_FPS ? "%d" : "UNLIMITED") : "VSYNC")){
actions = actions | Actions::FPS;
actions |= Actions::FPS;
}
ImGui::Text("Sampling %d (requires restart)", options.samples);
if (ImGui::Checkbox("Fullscreen", &options.fullscreen)){
actions = actions | Actions::FullScreen;
actions |= Actions::FullScreen;
}
{
......@@ -87,17 +87,17 @@ UI::Actions UI::draw(options &options, state &state, const reports &reports, GLu
ImGui::SameLine();
ImGui::Checkbox("Skybox", &options.renderer.skybox);
if (changeRenderer) {
actions = actions | Actions::RendererSharders;
actions |= Actions::RendererSharders;
}
}
if (ImGui::ColorEdit3("Fog color", &options.renderer.clear_color[0])) {
actions = actions | Actions::ClearColor;
actions |= Actions::ClearColor;
}
if (ImGui::Checkbox("Wireframe", &options.renderer.wireframe))
glPolygonMode(GL_FRONT_AND_BACK, options.renderer.wireframe ? GL_LINE : GL_FILL);
ImGui::Text("Textures '%s'", options.renderer.textures.c_str()); // MAYBE: select
if (ImGui::SliderFloat("LOD", &options.renderer.mipMapLOD, -1, 1)) {
actions = actions | Actions::RendererTextures;
actions |= Actions::RendererTextures;
}
ImGui::End();
}
......@@ -107,7 +107,7 @@ UI::Actions UI::draw(options &options, state &state, const reports &reports, GLu
ImGui::Text("Path: %s", options.world.folderPath.c_str());
if (ImGui::SliderInt("Load distance", &options.world.loadDistance, 1, options.world.keepDistance) |
ImGui::SliderInt("Keep distance", &options.world.keepDistance, options.world.loadDistance + 1, 21)) {
actions = actions | Actions::World;
actions |= Actions::World;
}
if(ImGui::SliderInt("Voxel density", &options.voxel_density, 1, CHUNK_LENGTH * REGION_LENGTH)) {
options.voxel_density = pow(2, ceil(log(options.voxel_density) / log(2)));
......@@ -121,7 +121,7 @@ UI::Actions UI::draw(options &options, state &state, const reports &reports, GLu
for (size_t i = 0; i < contouring::names.size(); i++) {
const bool is_selected = (options.contouring_idx == (int)i);
if (ImGui::Selectable(contouring::names[i].c_str(), is_selected)) {
actions = actions | Actions::ChangeContouring;
actions |= Actions::ChangeContouring;
contouring::save(options.contouring_idx, state.contouring, options.contouring_data);
options.contouring_idx = i;
}
......@@ -142,15 +142,21 @@ UI::Actions UI::draw(options &options, state &state, const reports &reports, GLu
const auto p = state.position.as_voxel(options.voxel_density);
ImGui::Text("Position: (%lld, %lld, %lld)", p.x, p.y, p.z);
ImGui::Separator();
{
if (ImGui::SliderFloat("Move speed", &options.control.speed, 0.1, 50) |
ImGui::SliderInt("Sensibility", &options.control.sensibility, 1, 100, "%d%%")) {
actions |= Actions::Control;
}
ImGui::Checkbox("Collide", &options.control.collide);
}
ImGui::Separator();
{
bool changePerspective = false;
changePerspective |= ImGui::SliderAngle("FoV", &options.camera.fov, 30, 110);
changePerspective |= ImGui::SliderFloat("Near", &options.camera.near, 0.01, 10);
changePerspective |= ImGui::SliderFloat("Far", &options.camera.far, farRange.first / options.voxel_density, farRange.second / options.voxel_density);
changePerspective |= ImGui::SliderFloat("Move speed", &options.camera.speed, 0.1, 50);
changePerspective |= ImGui::SliderInt("Sensibility", &options.camera.sensibility, 1, 100, "%d%%");
if(changePerspective) {
actions = actions | Actions::Camera;
actions |= Actions::Camera;
}
}
ImGui::End();
......@@ -158,7 +164,7 @@ UI::Actions UI::draw(options &options, state &state, const reports &reports, GLu
const auto far = std::clamp(options.camera.far, farRange.first / options.voxel_density, farRange.second / options.voxel_density);
if(far != options.camera.far) {
options.camera.far = far;
actions = actions | Actions::Camera;
actions |= Actions::Camera;
}
}
......
......@@ -17,10 +17,11 @@ namespace UI {
RendererTextures = 1 << 4,
World = 1 << 5,
Camera = 1 << 6,
ChangeContouring = 1 << 7,
Control = 1 << 7,
ChangeContouring = 1 << 8,
};
inline Actions operator|(Actions a, Actions b) {
return static_cast<Actions>(static_cast<int>(a) | static_cast<int>(b));
inline void operator|=(Actions& a, Actions b) {
a = static_cast<Actions>(static_cast<int>(a) | static_cast<int>(b));
}
inline bool operator&&(Actions a, Actions b) {
return static_cast<int>(a) & static_cast<int>(b);
......
......@@ -62,8 +62,9 @@ struct options {
camera.far = config["camera"]["far"].value_or(camera.far);
camera.near = config["camera"]["near"].value_or(camera.near);
camera.fov = config["camera"]["fov"].value_or(camera.fov);
camera.sensibility = config["camera"]["sensibility"].value_or(camera.sensibility);
camera.speed = config["camera"]["speed"].value_or(camera.speed);
control.sensibility = config["control"]["sensibility"].value_or(control.sensibility);
control.speed = config["control"]["speed"].value_or(control.speed);
control.collide = config["control"]["collide"].value_or(control.collide);
overlay_show = config["overlay"]["visible"].value_or(true);
overlay_corner = config["overlay"]["corner"].value_or(3);
......@@ -117,9 +118,12 @@ struct options {
config.insert_or_assign("camera", toml::table({
{"far", camera.far},
{"near", camera.near},
{"fov", camera.fov},
{"sensibility", camera.sensibility},
{"speed", camera.speed}
{"fov", camera.fov}
}));
config.insert_or_assign("control", toml::table({
{"sensibility", control.sensibility},
{"speed", control.speed},
{"collide", control.collide}
}));
config.insert_or_assign("overlay", toml::table({
{"visible", overlay_show},
......@@ -164,6 +168,7 @@ struct options {
std::map<std::string, std::string> contouring_data;
bool show_debug_controls;
Controllable::options control;
Camera::options camera;
bool editor_show;
......
......@@ -113,6 +113,7 @@ Universe::~Universe() {
LOG_I("Saving " << size << " chunks");
const auto SAVE_CHECK_TIME = 500;
do {
loadQueue.notify_all();
std::cout << "\rSaving... " << size << " " << std::flush;
std::this_thread::sleep_for(std::chrono::microseconds(SAVE_CHECK_TIME));
size = saveQueue.size();
......@@ -384,6 +385,25 @@ ItemList Universe::setCube(const area_<voxel_pos>& pos, const Voxel& val, int ra
}
return list;
}
bool Universe::collide(const glm::ifvec3 &pos, const glm::vec3 &vel, int density, float radius) const {
const auto dir = glm::normalize(vel);
const auto velocity = vel * glm::vec3(density);
const auto from = pos * density + dir;
return raycast(Ray(from, dir, glm::length(velocity) + radius)).has_value();
}
bool Universe::move(glm::ifvec3 &pos, const glm::vec3 &vel, int density, float radius) const {
const auto dir = glm::normalize(vel);
const auto velocity = vel * glm::vec3(density);
const auto from = pos * density + dir;
if (const auto target = raycast(Ray(from, dir, glm::length(velocity) + radius))) {
const auto target_dist = from.dist(glm::ifvec3(target.value().offset + target.value().pos.second, density)) - radius;
pos += vel * glm::vec3(target_dist / glm::length(vel));
return true;
}
pos += vel;
return false;
}
entity_instance_id Universe::addEntity(entity_id type, const Entity::Instance &instance) {
return std::make_pair(type, entities.at(type).instances.push(instance));
......
......@@ -56,6 +56,11 @@ namespace world {
/// Set cube of voxel with pos as center
/// MAYBE: allow set multi area
ItemList setCube(const area_<voxel_pos> &pos, const Voxel &val, int radius);
/// Check for collision on movement
bool collide(const glm::ifvec3 &pos, const glm::vec3 &vel, int density, float radius = 0) const;
/// Move with collision check
/// @note must remove velocity after colision
bool move(glm::ifvec3 &pos, const glm::vec3 &vel, int density, float radius = 0) const;
/// Entities commun properties
struct Entity {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment