diff --git a/data/layout.xml b/data/layout.xml index e1ea914..f2e635d 100644 --- a/data/layout.xml +++ b/data/layout.xml @@ -247,6 +247,7 @@ + @@ -262,7 +263,8 @@ - + + + + + + + + + + + + + + + + + + + + + + + + - + + diff --git a/engine/image.cpp b/engine/image.cpp index e3679a2..416b074 100644 --- a/engine/image.cpp +++ b/engine/image.cpp @@ -46,3 +46,36 @@ void Image::flip() } std::swap(m_data, flipped); } + +ui::Image ui::Image::resize(int w, int h) +{ + Image ret; + ret.create(w, h); + auto temp = (glm::u8vec4*)ret.data(); + auto pixels = (glm::u8vec4*)data(); + float x_ratio = ((float)(width - 1)) / w; + float y_ratio = ((float)(height - 1)) / h; + float x_diff, y_diff, ya, yb; + int offset = 0; + for (int i = 0; i < h; i++) { + for (int j = 0; j < w; j++) { + int x = (int)(x_ratio * j); + int y = (int)(y_ratio * i); + x_diff = (x_ratio * j) - x; + y_diff = (y_ratio * i) - y; + int index = y * width + x; + + // range is 0 to 255 thus bitwise AND with 0xff + glm::vec4 A = pixels[index]; + glm::vec4 B = pixels[index + 1]; + glm::vec4 C = pixels[index + width]; + glm::vec4 D = pixels[index + width + 1]; + + // Y = A(1-w)(1-h) + B(w)(1-h) + C(h)(1-w) + Dwh + glm::vec4 gray = A*(1 - x_diff)*(1 - y_diff) + B * (x_diff)*(1 - y_diff) + + C * (y_diff)*(1 - x_diff) + D * (x_diff*y_diff); + temp[offset++] = glm::clamp(gray, glm::vec4(0), glm::vec4(255)); + } + } + return ret; +} diff --git a/engine/image.h b/engine/image.h index f4e44ae..dda4d34 100644 --- a/engine/image.h +++ b/engine/image.h @@ -26,6 +26,7 @@ public: } void flip(); void create() { m_data = std::make_unique(size()); } + Image resize(int w, int h); }; } diff --git a/engine/node_canvas.cpp b/engine/node_canvas.cpp index 30ef596..2a8d085 100644 --- a/engine/node_canvas.cpp +++ b/engine/node_canvas.cpp @@ -275,24 +275,45 @@ void NodeCanvas::draw() for (auto& mode : ui::Canvas::modes[(int)ui::Canvas::kCanvasMode::Grid]) mode->on_Draw(ortho_proj, proj, camera); - int grid_divs = glm::floor(App::I.grid->m_groud_scale->get_value() * 100); - if (grid_divs != m_grid_divs && (grid_divs % 2) == 0) - { - m_grid_divs = grid_divs; - m_grid.create(1, 1, grid_divs); - } - float grid_scale = m_grid_divs * 0.01f; - ui::ShaderManager::use(kShader::Color); - ui::ShaderManager::u_vec4(kShaderUniform::Col, glm::vec4( - glm::vec3(App::I.grid->m_groud_value->get_value()), - App::I.grid->m_groud_opacity->get_value())); - ui::ShaderManager::u_mat4(kShaderUniform::MVP, proj * camera - * glm::translate(glm::vec3(0, (App::I.grid->m_groud_height->get_value() - 0.5f) * 2.f, 0)) - * glm::eulerAngleX(glm::radians(90.f)) - * glm::scale(glm::vec3(grid_scale, grid_scale, 1)) - ); - m_grid.draw_stroke(); + if (App::I.grid->m_groud_opacity->get_value() > 0.f) + { + // DRAW GRIDS + ui::ShaderManager::use(kShader::Color); + + // ground grid + int grid_divs = glm::floor(App::I.grid->m_groud_scale->get_value() * 100); + if (grid_divs != m_grid_divs && (grid_divs % 2) == 0) + { + m_grid_divs = grid_divs; + m_grid.create(1, 1, grid_divs); + } + float grid_scale = m_grid_divs * 0.01f; + + ui::ShaderManager::u_vec4(kShaderUniform::Col, glm::vec4( + glm::vec3(App::I.grid->m_groud_value->get_value()), + App::I.grid->m_groud_opacity->get_value())); + ui::ShaderManager::u_mat4(kShaderUniform::MVP, proj * camera + * glm::translate(glm::vec3(0, (App::I.grid->m_groud_height->get_value() - 0.5f) * 2.f, 0)) + * glm::eulerAngleX(glm::radians(90.f)) + * glm::scale(glm::vec3(grid_scale, grid_scale, 1)) + ); + //m_grid.draw_stroke(); + App::I.grid->m_hm_plane.draw_stroke(); + } + + // box grid + // ceiling +// ui::ShaderManager::u_vec4(kShaderUniform::Col, glm::vec4( +// glm::vec3(App::I.grid->m_groud_value->get_value()), +// App::I.grid->m_box_opacity->get_value())); +// ui::ShaderManager::u_mat4(kShaderUniform::MVP, proj * camera +// * glm::translate(glm::vec3(0, (App::I.grid->m_groud_height->get_value() + App::I.grid->m_box_height->get_value() - 0.5f) * 2.f, 0)) +// * glm::eulerAngleX(glm::radians(90.f)) +// * glm::scale(glm::vec3(grid_scale, grid_scale, 1)) +// ); +// m_grid.draw_stroke(); + //ui::ShaderManager::use(kShader::Equirect); //ui::ShaderManager::u_mat4(kShaderUniform::MVP, glm::scale(glm::vec3(.5, .5, 1))); diff --git a/engine/node_image_texture.cpp b/engine/node_image_texture.cpp index 840aaa0..93709e2 100644 --- a/engine/node_image_texture.cpp +++ b/engine/node_image_texture.cpp @@ -20,13 +20,14 @@ void NodeImageTexture::draw() { using namespace ui; tex.bind(); - NodeImage::m_sampler.bind(0); + auto& sampler = tex.has_mips ? NodeImage::m_sampler : NodeImage::m_sampler_mips; + sampler.bind(0); glEnable(GL_BLEND); ui::ShaderManager::use(kShader::Texture); ui::ShaderManager::u_int(kShaderUniform::Tex, 0); ui::ShaderManager::u_mat4(kShaderUniform::MVP, m_mvp); NodeImage::m_plane.draw_fill(); - NodeImage::m_sampler.unbind(); + sampler.unbind(); tex.unbind(); glDisable(GL_BLEND); } diff --git a/engine/node_panel_grid.cpp b/engine/node_panel_grid.cpp index bef48c8..ba4bfeb 100644 --- a/engine/node_panel_grid.cpp +++ b/engine/node_panel_grid.cpp @@ -2,6 +2,8 @@ #include "log.h" #include "node_panel_grid.h" #include "canvas.h" +#include "app.h" +#include "image.h" Node* NodePanelGrid::clone_instantiate() const { @@ -26,8 +28,36 @@ void NodePanelGrid::init_controls() m_groud_scale = find("grid-ground-scale"); m_groud_value = find("grid-ground-value"); m_groud_height = find("grid-ground-height"); - m_box_opacity = find("grid-box-opacity"); - m_box_width = find("grid-box-width"); - m_box_height = find("grid-box-height"); - m_box_depth = find("grid-box-depth"); + //m_box_opacity = find("grid-box-opacity"); + //m_box_width = find("grid-box-width"); + //m_box_height = find("grid-box-height"); + //m_box_depth = find("grid-box-depth"); + + auto update_hm = [this](Node* target, float v) { + m_hm_plane.create(1, 1, m_hm_image, -m_hm_height->get_value()); + }; + + m_hm_preview = find("grid-heightmap-preview"); + m_hm_load = find("grid-heightmap-load"); + m_hm_offset = find("grid-heightmap-offset"); + m_hm_height = find("grid-heightmap-height"); + + m_hm_height->on_value_changed = update_hm; + m_hm_preview->SetHeight(0); + + //m_hm_plane.create(1, 1); + + m_hm_load->on_click = [this](Node*) { + App::I.pick_image([this](std::string path) { + ui::Image img; + img.load(path); + m_hm_image = img.resize(128, 128); + m_hm_preview->tex.create(m_hm_image); + m_hm_preview->tex.create_mipmaps(); + auto sz = m_hm_preview->tex.size(); + m_hm_preview->SetAspectRatio(sz.x / sz.y); + m_hm_plane.create(1, 1, m_hm_image, -m_hm_height->get_value()); + m_hm_preview->SetHeight(100); + }); + }; } diff --git a/engine/node_panel_grid.h b/engine/node_panel_grid.h index 68c7953..da0aeb2 100644 --- a/engine/node_panel_grid.h +++ b/engine/node_panel_grid.h @@ -5,6 +5,10 @@ #include "brush.h" #include "node_checkbox.h" #include "node_combobox.h" +#include "node_image_texture.h" +#include "node_button.h" +#include "shape.h" +#include "image.h" class NodePanelGrid : public Node { @@ -17,7 +21,13 @@ public: NodeSliderH* m_box_width; NodeSliderH* m_box_height; NodeSliderH* m_box_depth; - + NodeImageTexture* m_hm_preview; + NodeButton* m_hm_load; + NodeSliderH* m_hm_offset; + NodeSliderH* m_hm_height; + ui::HeightmapPlane m_hm_plane; + ui::Image m_hm_image; + virtual Node* clone_instantiate() const override; virtual void clone_finalize(Node* dest) const override; virtual void init() override; diff --git a/engine/shape.cpp b/engine/shape.cpp index 6bea13a..948ac4f 100644 --- a/engine/shape.cpp +++ b/engine/shape.cpp @@ -4,7 +4,21 @@ using namespace ui; -bool Shape::create_buffers(GLvoid* idx, GLvoid* vertices, int isize, int vsize) +bool ui::Shape::create_buffers(GLushort * idx, GLvoid * vertices, int isize, int vsize) +{ + index_type = GL_UNSIGNED_SHORT; + create_buffers_imp(idx, vertices, isize, vsize); + return false; +} + +bool ui::Shape::create_buffers(GLuint* idx, GLvoid * vertices, int isize, int vsize) +{ + index_type = GL_UNSIGNED_INT; + create_buffers_imp(idx, vertices, isize, vsize); + return false; +} + +bool Shape::create_buffers_imp(GLvoid* idx, GLvoid* vertices, int isize, int vsize) { use_idx = true; @@ -80,6 +94,8 @@ bool Shape::create_buffers(GLvoid* vertices, int vsize) } void Shape::draw_fill() const { + if (count[0] == 0) return; + GLenum type = GL_TRIANGLES; if (count[0] == 1) type = GL_POINTS; @@ -88,7 +104,7 @@ void Shape::draw_fill() const #if USE_VBO glBindVertexArray(arrays[0]); if (use_idx) - glDrawElements(type, count[0], GL_UNSIGNED_SHORT, ioff[0]); + glDrawElements(type, count[0], index_type, ioff[0]); else glDrawArrays(type, 0, count[0]); glBindVertexArray(0); @@ -113,13 +129,15 @@ void Shape::draw_fill() const } void Shape::draw_stroke() const { + if (count[0] == 0) return; + GLenum type = GL_LINES; if (count[1] == 1) type = GL_POINTS; #if USE_VBO glBindVertexArray(arrays[1]); if (use_idx) - glDrawElements(type, count[1], GL_UNSIGNED_SHORT, ioff[1]); + glDrawElements(type, count[1], index_type, ioff[1]); else glDrawArrays(type, 0, count[1]); glBindVertexArray(0); @@ -219,17 +237,82 @@ void Plane::create_impl(float w, float h, int div, GLushort *idx, Shape::vertex_ *idx++ = y; *idx++ = y + div * (div + 1); } -// -// // outline indices -// *idx++ = 0; // A -// *idx++ = (div+1)*(div); // B -// *idx++ = (div+1)*(div); // B -// *idx++ = (div+1)*(div+1)-1; // C -// *idx++ = (div+1)*(div+1)-1; // C -// *idx++ = div; // D -// *idx++ = div; // D -// *idx++ = 0; // A } + +bool ui::HeightmapPlane::create(float w, float h, const Image& img, float scale) +{ + int div = img.width - 1; // TODO: handle height also + int idx_size = (div * div * 6) + (div * (div + 1) * 4); + int vertices_size = (div + 1)*(div + 1); + auto idx = std::make_unique(idx_size); + auto vertices = std::make_unique(vertices_size); + + count[0] = div * div * 6; + count[1] = div * (div + 1) * 4; + ioff[0] = (GLvoid*)0; + ioff[1] = (GLvoid*)(count[0] * sizeof(GLuint)); + + auto pv = vertices.get(); + auto pi = idx.get(); + auto px = (glm::u8vec4*)img.data(); + + const float dx = w / div; + const float dy = h / div; + const float ox = -w * 0.5f; + const float oy = -h * 0.5f; + for (int y = 0; y <= div; y++) + { + for (int x = 0; x <= div; x++) + { + vertex_t v; + v.pos.x = ox + dx * (float)x; + v.pos.y = oy + dy * (float)y; + v.pos.z = (*px++).r / 255.f * scale; + v.pos.w = 1; + v.uvs = glm::vec2(x, y) / (float)div; + *pv++ = v; + } + } + + // generate indices + for (int y = 0; y < div; y++) + { + int i = y * (div + 1); + for (int x = 0; x < div; x++) + { + *pi++ = i; + *pi++ = i + div + 1; + *pi++ = i + div + 2; + *pi++ = i; + *pi++ = i + div + 2; + *pi++ = i + 1; + i++; + } + } + + // generate indices + for (int y = 0; y <= div; y++) + { + int i = y * (div + 1); + for (int x = 0; x <= div; x++) + { + if (x < div) + { + *pi++ = y * (div + 1) + x; + *pi++ = y * (div + 1) + x + 1; + } + + if (y < div) + { + *pi++ = y * (div + 1) + x; + *pi++ = (y + 1) * (div + 1) + x; + } + } + } + + return create_buffers(idx.get(), vertices.get(), sizeof(GLuint) * idx_size, sizeof(vertex_t) * vertices_size); +} + void ui::Plane::update_vertices(const glm::vec4* data, const glm::vec2* uvs, const glm::vec2* uvs2) { static vertex_t vertices[4]; diff --git a/engine/shape.h b/engine/shape.h index fa9b2a1..ce4f305 100644 --- a/engine/shape.h +++ b/engine/shape.h @@ -1,4 +1,5 @@ #pragma once +#include "image.h" enum class kShapeType : uint16_t { @@ -17,10 +18,13 @@ protected: GLuint arrays[2]{ 0, 0 }; GLuint count[2]{ 0, 0 }; GLvoid* ioff[2]{ 0, 0 }; + GLenum index_type = 0; bool use_idx = true; + bool create_buffers_imp(GLvoid* idx, GLvoid* vertices, int isize, int vsize); public: struct vertex_t { glm::vec4 pos; glm::vec2 uvs; glm::vec2 uvs2; }; - bool create_buffers(GLvoid* idx, GLvoid* vertices, int isize, int vsize); + bool create_buffers(GLushort* idx, GLvoid* vertices, int isize, int vsize); + bool create_buffers(GLuint* idx, GLvoid* vertices, int isize, int vsize); bool create_buffers(GLvoid* vertices, int vsize); void draw_fill() const; void draw_stroke() const; @@ -116,31 +120,12 @@ public: return create_buffers(idx.get(), vertices.get(), sizeof(GLushort) * idx_size, sizeof(vertex_t) * vertices_size); } void update_vertices(const glm::vec4* data, const glm::vec2* uvs = nullptr, const glm::vec2* uvs2 = nullptr); -/* - bool create(att::Divisions divisions, att::Width w, att::Height h) - { - const int div = divisions.value; - auto idx = std::make_unique(div * div * 6 + 8); - auto vertices = std::make_unique((div + 1)*(div + 1)); - create_impl(w.value, h.value, div, idx.get(), vertices.get()); - return create_buffers(idx.get(), vertices.get(), sizeof(idx), sizeof(vertices)); - } - template - T get_attribute(T def_val) - { - auto ret = attribs.find(def_val.id); - if (ret == attribs.end()) - return def_val; - return *reinterpret_cast(ret->second.get()); - } - bool create_attrib() override - { - const auto w = get_attribute(att::Width(0)); - const auto h = get_attribute(att::Height(0)); - const auto d = get_attribute(att::Divisions(1)); - return create(d, w, h); - } - */ +}; + +class HeightmapPlane : public Shape +{ +public: + bool create(float w, float h, const Image& img, float scale); }; class RectShape : public Shape diff --git a/engine/texture.cpp b/engine/texture.cpp index 312282c..39b93ff 100644 --- a/engine/texture.cpp +++ b/engine/texture.cpp @@ -65,6 +65,7 @@ void Texture2D::create_mipmaps() bind(); glGenerateMipmap(GL_TEXTURE_2D); unbind(); + has_mips = true; } void Texture2D::assign(GLuint tex, int w/* = -1*/, int h/* = -1*/, GLuint internal_format/* = GL_RGBA8*/, GLuint format/* = GL_RGBA*/) diff --git a/engine/texture.h b/engine/texture.h index e0ada03..db1448c 100644 --- a/engine/texture.h +++ b/engine/texture.h @@ -9,6 +9,7 @@ class Texture2D GLint m_format = 0; GLint m_iformat = 0; public: + bool has_mips = false; bool create(int width, int height, GLint internal_format = GL_RGBA8, GLint format = GL_RGBA, const uint8_t* data = nullptr); bool create(const ui::Image& img); void assign(GLuint tex, int w = -1, int h = -1, GLuint internal_format = GL_RGBA8, GLuint format = GL_RGBA);