Commit 5a0c87cd authored by Shu's avatar Shu 🍠
Browse files

Raycast and edit

parent f3c70601
......@@ -7,7 +7,7 @@
- [ ] Octree world
- [ ] Serialize
- [ ] In memory RLE
- [ ] Edition
- [x] Edition
- [ ] Entity
- [ ] Planet
- [ ] CubicSphere
......
#version 330 core
// Ouput data
layout(location = 0) out vec4 color;
in vec4 Color;
void main(){
color = Color;
}
\ No newline at end of file
#version 330 core
layout(location = 0) in vec3 Position_modelspace;
layout(location = 1) in vec4 Color_model;
uniform mat4 MVP;
out vec4 Color;
void main(){
gl_Position = MVP * vec4(Position_modelspace, 1);
Color = Color_model;
}
......@@ -41,9 +41,15 @@ vec3 expand(vec3 v) {
vec4 getTexture(sampler2DArray sample, vec2 UV) {
#ifdef BLEND
vec4 colx = texture(sample, vec3(UV, vs.Materials[0]));
vec4 coly = texture(sample, vec3(UV, vs.Materials[1]));
vec4 colz = texture(sample, vec3(UV, vs.Materials[2]));
return colx * vs.MaterialRatio.x + coly * vs.MaterialRatio.y + colz * vs.MaterialRatio.z;
if(vs.Materials[1] == vs.Materials[0]) {
return vs.Materials[2] == vs.Materials[0] ? colx :
mix(colx, texture(sample, vec3(UV, vs.Materials[2])), vs.MaterialRatio.z);
} else {
vec4 coly = texture(sample, vec3(UV, vs.Materials[1]));
return vs.Materials[2] == vs.Materials[0] ? mix(colx, coly, vs.MaterialRatio.y) : (
vs.Materials[2] == vs.Materials[1] ? mix(coly, colx, vs.MaterialRatio.x) :
colx * vs.MaterialRatio.x + coly * vs.MaterialRatio.y + texture(sample, vec3(UV, vs.Materials[2])) * vs.MaterialRatio.z);
}
#else
return texture(sample, vec3(UV, vs.Material));
#endif
......
......@@ -4,7 +4,6 @@
namespace contouring {
static const std::array<std::string, 3> names = {"FlatDualMC", "FlatBox", "Dummy"};
static const char* cnames = "FlatDualMC\0FlatBox\0Dummy\0";
int idxByName(const std::string &name);
std::shared_ptr<Abstract> load(int idx, const std::map<std::string, std::string> &data);
......
......@@ -6,6 +6,7 @@
#include <GLFW/glfw3.h>
#include "../data/glm.hpp"
#include "../data/geometry/Frustum.hpp"
#include "../data/geometry/Ray.hpp"
/// Moving perspective camera
class Camera {
......@@ -34,6 +35,7 @@ public:
glm::vec3 getDirection() const { return glm::vec3(cos(VerticalAngle) * sin(HorizontalAngle), sin(VerticalAngle), cos(VerticalAngle) * cos(HorizontalAngle)); }
inline Frustum getFrustum() const { return Frustum(ViewMatrix, ProjectionMatrix); }
inline Ray getRay() const { return Ray(Position, getDirection(), o.far); }
glm::mat4 getViewMatrix() const { return ViewMatrix; }
glm::mat4 getProjectionMatrix() const { return ProjectionMatrix; }
......
#pragma once
#include "Box.hpp"
#include "../glm.hpp"
/// Raycast with distance
struct Ray {
glm::vec3 from;
glm::vec3 dir;
float dist;
// MAYBE: Ray(const glm::mat4& view_matrix) { }
Ray(const glm::vec3& from, const glm::vec3& dir, float dist): from(from), dir(glm::normalize(dir)), dist(dist) { }
inline Ray operator/(float scale) const noexcept {
return Ray(from / scale, dir, dist / scale);
}
/// Get path points in integer grid
/// @note not precise enough
inline void grid(std::vector<voxel_pos>& points) const {
voxel_pos current = from + glm::vec3(.5f);
voxel_pos d = dir * dist;
voxel_pos inc = voxel_pos((d.x < 0) ? -1 : 1, (d.y < 0) ? -1 : 1, (d.z < 0) ? -1 : 1);
voxel_pos size = glm::abs(d);
voxel_pos delta = size << 1ll;
if ((size.x >= size.y) && (size.x >= size.z)) {
int err_1 = delta.y - size.x;
int err_2 = delta.z - size.x;
points.reserve(size.x + 1);
for (int i = 0; i < size.x; i++) {
points.push_back(current);
if (err_1 > 0) {
current.y += inc.y;
err_1 -= delta.x;
}
if (err_2 > 0) {
current.z += inc.z;
err_2 -= delta.x;
}
err_1 += delta.y;
err_2 += delta.z;
current.x += inc.x;
}
} else if ((size.y >= size.x) && (size.y >= size.z)) {
int err_1 = delta.x - size.y;
int err_2 = delta.z - size.y;
points.reserve(size.y + 1);
for (int i = 0; i < size.y; i++) {
points.push_back(current);
if (err_1 > 0) {
current.x += inc.x;
err_1 -= delta.y;
}
if (err_2 > 0) {
current.z += inc.z;
err_2 -= delta.y;
}
err_1 += delta.x;
err_2 += delta.z;
current.y += inc.y;
}
} else {
int err_1 = delta.y - size.z;
int err_2 = delta.x - size.z;
points.reserve(size.z + 1);
for (int i = 0; i < size.z; i++) {
points.push_back(current);
if (err_1 > 0) {
current.y += inc.y;
err_1 -= delta.z;
}
if (err_2 > 0) {
current.x += inc.x;
err_2 -= delta.z;
}
err_1 += delta.y;
err_2 += delta.x;
current.z += inc.z;
}
}
points.push_back(current);
}
};
......@@ -71,6 +71,10 @@ struct options {
show_debug_world = config["show_debug"]["world"].value_or(false);
show_debug_contouring = config["show_debug"]["contouring"].value_or(false);
show_debug_controls = config["show_debug"]["controls"].value_or(false);
editor_show = config["editor"]["visible"].value_or(false);
tool.material = config["editor"]["tool"]["material"].value_or<int>(tool.material);
tool.radius = config["editor"]["tool"]["radius"].value_or(tool.radius);
}
void save() {
auto config = toml::table();
......@@ -123,6 +127,14 @@ struct options {
{"controls", show_debug_controls}
}));
config.insert_or_assign("editor", toml::table({
{"visible", editor_show},
{"tool", toml::table({
{"material", (int)tool.material},
{"radius", tool.radius}
})}
}));
std::ofstream out;
out.open(PATH, std::ios::out | std::ios::trunc);
out << config << "\n\n";
......@@ -148,6 +160,12 @@ struct options {
bool show_debug_controls;
Camera::options camera;
bool editor_show;
struct tool {
int radius = 2;
unsigned long long material = 2;
} tool;
bool overlay_show;
int overlay_corner;
......
......@@ -18,6 +18,8 @@
#include "control/Camera.hpp"
#include "render/Renderer.hpp"
#include "render/pass/ColorProgram.hpp"
#include "render/buffer/ColorBuffer.hpp"
#include "world/World.hpp"
#include "data/state.h"
......@@ -41,11 +43,39 @@ int main(int, char *[]){
Camera camera(window, inputs, options.camera);
Renderer *renderer = new Renderer(options.renderer);
renderer->LightInvDir = glm::vec3(-0.5f, 2, -2);
UI::setup(window);
GLuint aimTexture = Program::loadTexture("ui/Aim", false);
renderer->LightInvDir = glm::vec3(-0.5f, 2, -2);
ColorProgram *lookProgram = new ColorProgram();
ColorBuffer lookBuffer(GL_LINES, 24, {
glm::vec3(0, 0, 0), glm::vec3(0, 0, 1),
glm::vec3(0, 0, 1), glm::vec3(0, 1, 1),
glm::vec3(0, 1, 1), glm::vec3(0, 1, 0),
glm::vec3(0, 1, 0), glm::vec3(0, 0, 0),
glm::vec3(1, 0, 0), glm::vec3(1, 0, 1),
glm::vec3(1, 0, 1), glm::vec3(1, 1, 1),
glm::vec3(1, 1, 1), glm::vec3(1, 1, 0),
glm::vec3(1, 1, 0), glm::vec3(1, 0, 0),
glm::vec3(0, 0, 0), glm::vec3(1, 0, 0),
glm::vec3(0, 0, 1), glm::vec3(1, 0, 1),
glm::vec3(0, 1, 1), glm::vec3(1, 1, 1),
glm::vec3(0, 1, 0), glm::vec3(1, 1, 0),
}, {
glm::vec4(1, 1, 1, 1), glm::vec4(1, 1, 1, 1),
glm::vec4(1, 1, 1, 1), glm::vec4(1, 1, 1, 1),
glm::vec4(1, 1, 1, 1), glm::vec4(1, 1, 1, 1),
glm::vec4(1, 1, 1, 1), glm::vec4(1, 1, 1, 1),
glm::vec4(1, 1, 1, 1), glm::vec4(1, 1, 1, 1),
glm::vec4(1, 1, 1, 1), glm::vec4(1, 1, 1, 1),
glm::vec4(1, 1, 1, 1), glm::vec4(1, 1, 1, 1),
glm::vec4(1, 1, 1, 1), glm::vec4(1, 1, 1, 1),
glm::vec4(1, 1, 1, 1), glm::vec4(1, 1, 1, 1),
glm::vec4(1, 1, 1, 1), glm::vec4(1, 1, 1, 1),
glm::vec4(1, 1, 1, 1), glm::vec4(1, 1, 1, 1),
glm::vec4(1, 1, 1, 1), glm::vec4(1, 1, 1, 1),
});
Remotery *rmt;
rmt_CreateGlobalInstance(&rmt);
......@@ -70,13 +100,13 @@ int main(int, char *[]){
renderer->lookFrom(camera);
state.position = camera.getPosition();
/*look_at = world.raycast(camera.getViewRay() / scale);
if (capture_mouse && look_at.has_value()) {
state.look_at = world.raycast(camera.getRay() / options.voxel_size);
if (state.capture_mouse && state.look_at.has_value()) {
if (inputs.isPressing(Mouse::Left))
world.setCube(look_at.value().first, 2, Voxel{0, 0});
world.setCube(state.look_at.value().first, Voxel{0, 0}, options.tool.radius);
else if (inputs.isPressing(Mouse::Right))
world.setCube(look_at.value().first, 2, Voxel{2, 1});
}*/
world.setCube(state.look_at.value().first, Voxel{UCHAR_MAX, options.tool.material}, options.tool.radius);
}
world.update(state.position / options.voxel_size, reports.world);
inputs.saveKeys();
reports.main.update.push((glfwGetTime() - partTime) * 1000);
......@@ -140,6 +170,12 @@ int main(int, char *[]){
reports.main.models_count++;
reports.main.tris_count += buffer->draw(pass.setup(model));
}
if(state.look_at.has_value()) {
lookProgram->useIt();
lookProgram->start(renderer);
const auto model = glm::scale(glm::translate(glm::scale(glm::mat4(1), glm::vec3(options.voxel_size)), glm::vec3(state.look_at.value().first) - glm::vec3(.5 + options.tool.radius)), glm::vec3(1 + options.tool.radius * 2));
lookBuffer.draw(lookProgram->setup(renderer, model));
}
renderer->postProcess();
UI::render();
......
......@@ -4,6 +4,7 @@
#include "imgui_impl_opengl3.h"
#include "pass/Program.hpp"
#include "../contouring/Abstract.hpp"
#include "../world/materials.hpp"
void UI::setup(GLFWwindow* window) {
// Setup Dear ImGui context
......@@ -39,10 +40,14 @@ UI::Actions UI::draw(options &options, state &state, const reports &reports, GLu
if (options.show_debug_menu) {
ImGui::BeginMainMenuBar();
ImGui::Checkbox("Render", &options.show_debug_render);
ImGui::Checkbox("World", &options.show_debug_world);
ImGui::Checkbox("Contouring", &options.show_debug_contouring);
ImGui::Checkbox("Controls", &options.show_debug_controls);
if (ImGui::BeginMenu("Debug")) {
ImGui::Checkbox("Render", &options.show_debug_render);
ImGui::Checkbox("World", &options.show_debug_world);
ImGui::Checkbox("Contouring", &options.show_debug_contouring);
ImGui::Checkbox("Controls", &options.show_debug_controls);
ImGui::EndMenu();
}
ImGui::Checkbox("Editor", &options.editor_show);
if(ImGui::MenuItem("Close"))
options.show_debug_menu = false;
......@@ -116,10 +121,18 @@ UI::Actions UI::draw(options &options, state &state, const reports &reports, GLu
if (options.show_debug_contouring) {
ImGui::Begin("Debug: Contouring", &options.show_debug_contouring, ImGuiWindowFlags_AlwaysAutoResize);
const auto prev_idx = options.contouring_idx;
if(ImGui::Combo("Contouring", &options.contouring_idx, contouring::cnames)) {
actions = actions | Actions::ChangeContouring;
contouring::save(prev_idx, state.contouring, options.contouring_data);
if (ImGui::BeginCombo("Contouring", contouring::names[options.contouring_idx].c_str())) {
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;
contouring::save(options.contouring_idx, state.contouring, options.contouring_data);
options.contouring_idx = i;
}
if (is_selected)
ImGui::SetItemDefaultFocus();
}
ImGui::EndCombo();
}
ImGui::Checkbox("Culling", &options.culling);
state.contouring->onGui();
......@@ -129,12 +142,6 @@ UI::Actions UI::draw(options &options, state &state, const reports &reports, GLu
if (options.show_debug_controls) {
ImGui::Begin("Debug: Controls", &options.show_debug_controls, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::Text("Position: (%.3f, %.3f, %.3f)", state.position.x, state.position.y, state.position.z);
if (state.look_at.has_value()) {
ImGui::Text("Look at: (%lld, %lld, %lld) (%d, %.1d)", state.look_at.value().first.x, state.look_at.value().first.y, state.look_at.value().first.z, state.look_at.value().second.Material, state.look_at.value().second.Density / UCHAR_MAX);
ImGui::Text("(%.3f, %.3f, %.3f)", state.look_at.value().first.x * options.voxel_size, state.look_at.value().first.y * options.voxel_size, state.look_at.value().first.z * options.voxel_size);
} else {
ImGui::Text("Look at: none");
}
ImGui::Separator();
{
bool changePerspective = false;
......@@ -150,6 +157,29 @@ UI::Actions UI::draw(options &options, state &state, const reports &reports, GLu
ImGui::End();
}
if (options.editor_show) {
ImGui::Begin("Editor", &options.editor_show, ImGuiWindowFlags_AlwaysAutoResize);
if (state.look_at.has_value()) {
ImGui::Text("Look at: (%lld, %lld, %lld) (%s, %.1f)", state.look_at.value().first.x, state.look_at.value().first.y, state.look_at.value().first.z, materials::textures[state.look_at.value().second.Material].c_str(), state.look_at.value().second.Density * 1. / UCHAR_MAX);
ImGui::Text("(%.3f, %.3f, %.3f)", state.look_at.value().first.x * options.voxel_size, state.look_at.value().first.y * options.voxel_size, state.look_at.value().first.z * options.voxel_size);
} else {
ImGui::Text("Look at: none");
}
ImGui::Separator();
if (ImGui::BeginCombo("Material", materials::textures[options.tool.material].c_str())) {
for (size_t i = 0; i < materials::textures.size(); i++) {
const bool is_selected = (options.tool.material == i);
if (ImGui::Selectable(materials::textures[i].c_str(), is_selected))
options.tool.material = i;
if (is_selected)
ImGui::SetItemDefaultFocus();
}
ImGui::EndCombo();
}
ImGui::SliderInt("Radius", &options.tool.radius, 0, 10);
ImGui::End();
}
/*if (show_console) {
ImGui::SetNextWindowPos(ImVec2(UI_MARGIN, 500), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(200, 100), ImGuiCond_FirstUseEver);
......
#include "ColorBuffer.hpp"
ColorBuffer::ColorBuffer(GLenum shape, const unsigned long count, const std::vector<glm::vec3> &vertices, const std::vector<glm::vec4> &colors): Buffer(shape) {
glGenBuffers(1, &ColorBufferID);
setVertices(vertices.size() * sizeof(glm::vec3), &vertices[0]);
setColors(colors.size() * sizeof(glm::vec4), &colors[0]);
ElementCount = count;
}
ColorBuffer::~ColorBuffer() {
glDeleteBuffers(1, &ColorBufferID);
}
uint ColorBuffer::draw(params params) {
if (params.vertexOnly) {
enableVertexAttrib();
} else {
enableAllAttribs();
}
glDrawArrays(Shape, 0, ElementCount);
if (params.vertexOnly) {
disableVertexAttrib();
} else {
disableAllAttribs();
}
return ElementCount;
}
void ColorBuffer::enableAllAttribs() {
enableVertexAttrib();
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, ColorBufferID);
glVertexAttribPointer(
1, // attribute
4, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void *)0 // array buffer offset
);
}
void ColorBuffer::disableAllAttribs() {
glDisableVertexAttribArray(1);
disableVertexAttrib();
}
void ColorBuffer::setColors(const unsigned long size, const void *data) {
glBindBuffer(GL_ARRAY_BUFFER, ColorBufferID);
glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
}
\ No newline at end of file
#pragma once
#include <GL/glew.h>
#include <vector>
#include <glm/glm.hpp>
#include "Buffer.hpp"
/// OpenGL VertexBuffer with Colors
class ColorBuffer: public Buffer {
public:
ColorBuffer(GLenum shape, const unsigned long count, const std::vector<glm::vec3> &vertices, const std::vector<glm::vec4> &colors);
virtual ~ColorBuffer();
void enableAllAttribs();
void disableAllAttribs();
uint draw(params params) override;
private:
unsigned long ElementCount;
GLuint ColorBufferID;
void setColors(const unsigned long size, const void *data);
};
\ No newline at end of file
#include "ColorProgram.hpp"
#include "../Renderer.hpp"
ColorProgram::ColorProgram(): Program() {
std::vector<std::string> flags;
std::vector<Shader*> shaders;
shaders.push_back(loadShader(GL_VERTEX_SHADER, flags));
shaders.push_back(loadShader(GL_FRAGMENT_SHADER, flags));
load(shaders);
MVPMatrixID = glGetUniformLocation(ProgramID, "MVP");
}
ColorProgram::~ColorProgram() { }
std::string ColorProgram::getName() const {
return "Color";
}
void ColorProgram::start(Renderer *renderer) { }
Buffer::params ColorProgram::setup(Renderer *renderer, glm::mat4 modelMatrix) {
const auto mvp = renderer->getProjectionMatrix() * renderer->getViewMatrix() * modelMatrix;
setMVP(&mvp[0][0]);
return Buffer::params{.vertexOnly = false};
}
void ColorProgram::setMVP(const GLfloat *matrix) {
glUniformMatrix4fv(MVPMatrixID, 1, GL_FALSE, matrix);
}
#pragma once
#include "Program.hpp"
/// Final pass
class ColorProgram: public Program {
public:
ColorProgram();
~ColorProgram();
std::string getName() const override;
void start(Renderer *) override;
Buffer::params setup(Renderer *, glm::mat4 modelMatrix) override;
void setMVP(const GLfloat *matrix);
private:
GLuint MVPMatrixID;
};
......@@ -11,20 +11,20 @@ Chunk::Chunk(const chunk_pos& pos, Generator& rnd) {
for (size_t i = 0; i < CHUNK_SIZE; i++) {
voxels[i].Density = std::clamp((densitySet[i] + DENSITY) * GRANULARITY, 0.f, 1.f) * UCHAR_MAX;
voxels[i].Material = voxels[i].Density > 0 ? 1 + std::clamp(static_cast<int>(std::lrint((materialSet[i] + 1) / 2 * (materials::count - 1))),
0, materials::count - 1) : 0; //NOTE: map (approx -1, 1) to (1, mat_max)
voxels[i].Material = voxels[i].Density > 0 ? 1 + std::clamp(static_cast<int>(std::lrint((materialSet[i] + 1) / 2 * (materials::count - 2))),
0, materials::count - 2) : 0; //NOTE: map (approx -1, 1) to (1, mat_max)
}
FastNoiseSIMD::FreeNoiseSet(densitySet);
FastNoiseSIMD::FreeNoiseSet(materialSet);
}
Chunk::~Chunk() { }
bool Chunk::update() {
std::optional<Faces> Chunk::update() {
if(upToDate) {
return false;
return {};
} else {
upToDate = true;
return true;
return {toUpdate};
}
}
......
......@@ -15,14 +15,16 @@ public:
~Chunk();
/// Update voxels
/// @return true if modified
bool update();
/// @return if modified neightbors to update
std::optional<Faces> update();
const Voxel* begin() const {
return voxels.begin();
}
inline void invalidate() {
inline void invalidate(Faces faces) {
upToDate = false;
toUpdate = toUpdate | faces;
modified = true;
}
inline const Voxel& get(ushort idx) const {
return voxels[idx];
......@@ -32,11 +34,23 @@ public:
}
void set(ushort idx, const Voxel& val) {
voxels[idx] = val;
invalidate();
invalidate(
((!getNeighborIdx(idx, Face::Up).has_value()) & Faces::Up) |
((!getNeighborIdx(idx, Face::Down).has_value()) & Faces::Down) |
((!getNeighborIdx(idx, Face::Left).has_value()) & Faces::Left) |
((!getNeighborIdx(idx, Face::Right).has_value()) & Faces::Right) |
((!getNeighborIdx(idx, Face::Forward).has_value()) & Faces::Forward) |
((!getNeighborIdx(idx, Face::Backward).has_value()) & Faces::Backward));
}
void setAt(const chunk_voxel_pos& pos, const Voxel& val) {
set(getIdx(pos), val);
}
Item breakAt(const chunk_voxel_pos& pos, const Voxel& val) {
const auto idx = getIdx(pos);
const auto res = voxels[idx];
set(idx, val);
return Item{res.Density, res.Material};
}
static inline chunk_voxel_pos getPosition(ushort idx) {
return chunk_voxel_pos(idx / CHUNK_LENGTH2, (idx / CHUNK_LENGTH) % CHUNK_LENGTH, idx % CHUNK_LENGTH);
......@@ -54,4 +68,8 @@ private:
std::array<Voxel, CHUNK_SIZE> voxels;
/// Require update
bool upToDate = true;
/// Neighbors to update