diff --git a/data/layout.xml b/data/layout.xml
index 1a5c89a..36a8908 100644
--- a/data/layout.xml
+++ b/data/layout.xml
@@ -292,7 +292,7 @@
-
+
@@ -301,6 +301,7 @@
+
diff --git a/src/app.cpp b/src/app.cpp
index 96537c6..a740bcb 100644
--- a/src/app.cpp
+++ b/src/app.cpp
@@ -328,7 +328,10 @@ void App::init()
LOG("OPENGL: %.*s", length, message);
FlushConsoleInputBuffer(GetStdHandle(STD_OUTPUT_HANDLE));
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), info.wAttributes);
- //__debugbreak();
+#ifdef _WIN32
+ if (severity == GL_DEBUG_SEVERITY_HIGH)
+ __debugbreak();
+#endif
}
}, nullptr);
glEnable(GL_DEBUG_OUTPUT);
diff --git a/src/app_shaders.cpp b/src/app_shaders.cpp
index 1531edf..3986a41 100644
--- a/src/app_shaders.cpp
+++ b/src/app_shaders.cpp
@@ -509,6 +509,7 @@ void App::initShaders()
"uniform mat4 mvp;\n"
"in vec4 pos;\n"
"in vec3 nor;\n"
+ "in vec2 uvs;\n"
"out vec3 n;\n"
"void main() {\n"
" n = nor;\n"
@@ -522,6 +523,61 @@ void App::initShaders()
"void main() {\n"
" mediump float d = max(0.0, dot(normalize(n), light_dir));\n"
" frag = vec4(vec3(d), 1.0);\n"
+ //" frag = vec4(normalize(n) * 0.5 + 0.5, 1.0);\n"
+ "}\n";
+
+ // LAMBERT LIGHTMAP
+ static const char* shader_lambert_lightmap_v =
+ SHADER_VERSION
+ "uniform mat4 mvp;\n"
+ "in vec4 pos;\n"
+ "in vec3 nor;\n"
+ "in vec2 uvs;\n"
+ "out vec3 n;\n"
+ "out vec2 uv;\n"
+ "void main() {\n"
+ " n = nor;\n"
+ " uv = uvs;\n"
+ " gl_Position = mvp * pos;\n"
+ "}\n";
+ static const char* shader_lambert_lightmap_f =
+ SHADER_VERSION
+ "uniform mediump sampler2D tex;\n"
+ "uniform mediump vec3 light_dir;\n"
+ "in mediump vec3 n;\n"
+ "in mediump vec2 uv;\n"
+ "out mediump vec4 frag;\n"
+ "void main() {\n"
+ " mediump float d = max(0.0, dot(normalize(n), light_dir));\n"
+ " mediump vec4 c = texture(tex, uv);\n"
+ " frag = vec4(c.rgb * d, 1.0);\n"
+ "}\n";
+
+ // BAKE UVS
+ static const char* shader_bakeuv_v =
+ SHADER_VERSION
+ "uniform mat4 mvp;\n"
+ "in vec4 pos;\n"
+ "in vec3 nor;\n"
+ "in vec2 uvs;\n"
+ "out vec3 n;\n"
+ "out vec3 p;\n"
+ "void main() {\n"
+ " n = nor;\n"
+ " p = vec3(mvp * pos);\n"
+ " gl_Position = vec4(uvs * 2.0 - 1.0, 0.0, 1.0);\n"
+ "}\n";
+ static const char* shader_bakeuv_f =
+ SHADER_VERSION
+ "uniform int mode;\n"
+ "in highp vec3 n;\n"
+ "in highp vec3 p;\n"
+ "out highp vec3 frag;\n"
+ "void main() {\n"
+ " switch(mode) {\n"
+ " case 0: frag = normalize(n); break;\n"
+ " case 1: frag = p; break;\n"
+ " }\n"
"}\n";
@@ -566,6 +622,10 @@ void App::initShaders()
LOG("Failed to create shader VertexColor");
if (!ShaderManager::create(kShader::Lambert, shader_lambert_v, shader_lambert_f))
LOG("Failed to create shader Lambert");
+ if (!ShaderManager::create(kShader::LambertLightmap, shader_lambert_lightmap_v, shader_lambert_lightmap_f))
+ LOG("Failed to create shader LambertLightmap");
+ if (!ShaderManager::create(kShader::BakeUV, shader_bakeuv_v, shader_bakeuv_f))
+ LOG("Failed to create shader BakeUV");
LOG("shaders initialized");
}
diff --git a/src/node_panel_grid.cpp b/src/node_panel_grid.cpp
index a4881bb..dca18c9 100644
--- a/src/node_panel_grid.cpp
+++ b/src/node_panel_grid.cpp
@@ -39,6 +39,7 @@ void NodePanelGrid::init_controls()
m_hm_lpitch = find("grid-heightmap-lpitch");
m_hm_shading = find("grid-heightmap-shading");
m_render = find("grid-render");
+ m_commit = find("grid-commit");
m_hm_preview->SetHeight(0);
m_hm_plane.create(1, 1, 100);
@@ -49,12 +50,14 @@ void NodePanelGrid::init_controls()
m_hm_plane.create(1, 1, m_hm_image, v * 5.f, get_height());
else
m_hm_plane.create(1, 1, 100 * v * 5.f);
+ m_rt_dirty = true;
LOG("resolution value %f", v);
};
m_hm_height->on_value_final = [this](Node* target, float v) {
if (m_hm_image.data())
m_hm_plane.create(1, 1, m_hm_image, m_groud_resolution->get_value() * 5.f, get_height());
+ m_rt_dirty = true;
LOG("height value %f", v);
};
@@ -79,6 +82,7 @@ void NodePanelGrid::init_controls()
m_hm_preview->SetHeight(100);
if (m_groud_opacity->get_value() == 0.f)
m_groud_opacity->set_value(1.f);
+ m_rt_dirty = true;
}
async_update();
async_end();
@@ -106,18 +110,31 @@ void NodePanelGrid::init_controls()
m_hm_plane.create(1, 1, m_hm_image,
m_groud_resolution->get_value() * 5.f, get_height());
m_hm_preview->SetHeight(100);
+ m_rt_dirty = true;
}
};
m_render->on_click = [this](Node*)
+ {
+ gl_state gl;
+ gl.save();
+ bake_uvs();
+ m_hm_shading->set_index(3);
+ m_shade_mode = ShadeMode::Textured;
+ gl.restore();
+ };
+ m_commit->on_click = [this](Node*)
{
gl_state gl;
gl.save();
Canvas::I->draw_objects([this](const glm::mat4& camera, const glm::mat4& proj, int i) {
draw_heightmap(proj, camera);
});
+ m_groud_opacity->set_value(0);
gl.restore();
};
+ m_texture.create(1024, 1024);
+ m_sampler_linear.create();
}
float NodePanelGrid::get_height() const
@@ -134,27 +151,26 @@ void NodePanelGrid::draw_heightmap(const glm::mat4& proj, const glm::mat4& camer
{
if (m_groud_opacity->get_value() > 0.f)
{
- glEnable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
glClear(GL_DEPTH_BUFFER_BIT);
auto mvp = proj * camera
- * glm::translate(glm::vec3(0, get_offset(), 0))
- * glm::scale(glm::vec3(1, get_height(), 1))
- * glm::eulerAngleX(glm::radians(90.f));
+ * glm::translate(glm::vec3(0, get_offset(), 0));
// DRAW SOLID
if (m_hm_image.m_data)
{
+ auto light_yaw = m_hm_lyaw->get_value() * glm::pi() * 2.f;
+ auto light_pitch = m_hm_lpitch->get_value() * 5;
+ auto light_pos = glm::vec3(sinf(light_yaw), light_pitch, cosf(light_yaw));
+ auto light_dir = glm::normalize(light_pos);
+
if (m_shade_mode == ShadeMode::Solid)
{
glDisable(GL_BLEND);
ShaderManager::use(kShader::Lambert);
ShaderManager::u_mat4(kShaderUniform::MVP, mvp);
- auto light_yaw = m_hm_lyaw->get_value() * glm::pi() * 2.f;
- auto light_pitch = m_hm_lpitch->get_value();
- auto light_pos = glm::vec3(sinf(light_yaw), cosf(light_yaw), light_pitch);
- ShaderManager::u_vec3(kShaderUniform::LightDir, glm::normalize(-light_pos));
+ ShaderManager::u_vec3(kShaderUniform::LightDir, light_dir);
m_hm_plane.draw_fill();
}
else if (m_shade_mode == ShadeMode::Flat)
@@ -167,6 +183,17 @@ void NodePanelGrid::draw_heightmap(const glm::mat4& proj, const glm::mat4& camer
ShaderManager::u_mat4(kShaderUniform::MVP, mvp);
m_hm_plane.draw_fill();
}
+ else if(m_shade_mode == ShadeMode::Textured)
+ {
+ ShaderManager::use(kShader::LambertLightmap);
+ ShaderManager::u_mat4(kShaderUniform::MVP, mvp);
+ ShaderManager::u_vec3(kShaderUniform::LightDir, light_dir);
+ ShaderManager::u_int(kShaderUniform::Tex, 0);
+ m_sampler_linear.bind(0);
+ glActiveTexture(GL_TEXTURE0);
+ m_texture.bind();
+ m_hm_plane.draw_fill();
+ }
}
// DRAW GRIDS
@@ -191,3 +218,89 @@ void NodePanelGrid::draw_heightmap(const glm::mat4& proj, const glm::mat4& camer
}
}
}
+
+void NodePanelGrid::bake_uvs()
+{
+ if (!m_hm_image.m_data)
+ return;
+
+ RTT fb;
+ fb.create(m_texture.size().x, m_texture.size().y, -1, GL_RGBA32F);
+ fb.bindFramebuffer();
+ fb.clear({ 1, 0, 0, 1 });
+ glDisable(GL_BLEND);
+ glDisable(GL_DEPTH_TEST);
+ glViewport(0, 0, fb.getWidth(), fb.getHeight());
+ ShaderManager::use(kShader::BakeUV);
+ ShaderManager::u_mat4(kShaderUniform::MVP, glm::mat4(1));
+
+ // bake normal
+ ShaderManager::u_int(kShaderUniform::Mode, 0);
+ m_hm_plane.draw_fill();
+ std::unique_ptr data_nor(fb.readTextureDataFloat());
+ //stbi_write_jpg("bake-nor.jpg", fb.getWidth(), fb.getHeight(), 4, fb.readTextureData(), 75);
+
+ // bake position
+ ShaderManager::u_int(kShaderUniform::Mode, 1);
+ m_hm_plane.draw_fill();
+ std::unique_ptr data_pos(fb.readTextureDataFloat());
+ //stbi_write_jpg("bake-pos.jpg", fb.getWidth(), fb.getHeight(), 4, fb.readTextureData(), 75);
+
+ fb.unbindFramebuffer();
+ fb.destroy();
+
+ if (m_rt_dirty)
+ {
+ nanort::BVHBuildOptions build_options; // Use default option
+ build_options.cache_bbox = false;
+ nanort::TriangleMesh triangle_mesh(reinterpret_cast(m_hm_plane.vertices.data()), m_hm_plane.idx.data(), sizeof(vertex_t));
+ nanort::TriangleSAHPred triangle_pred(reinterpret_cast(m_hm_plane.vertices.data()), m_hm_plane.idx.data(), sizeof(vertex_t));
+ bool ret = m_rt_accel.Build(static_cast(m_hm_plane.idx.size() / 3), triangle_mesh, triangle_pred, build_options);
+ if (!ret)
+ return;
+ m_rt_dirty = false;
+ }
+
+ auto light_yaw = m_hm_lyaw->get_value() * glm::pi() * 2.f;
+ auto light_pitch = m_hm_lpitch->get_value() * 5;
+ auto light_pos = glm::vec3(sinf(light_yaw), light_pitch, cosf(light_yaw));
+ auto light_dir = glm::normalize(light_pos);
+
+ auto data_out = std::make_unique(fb.getWidth() * fb.getHeight() * 4);
+ for (int y = 0; y < fb.getHeight(); y++)
+ {
+ for (int x = 0; x < fb.getWidth(); x++)
+ {
+ int i = y * fb.getHeight() + x;
+ auto nor = glm::make_vec4(&data_nor[i * 4]);
+ auto pos = glm::make_vec4(&data_pos[i * 4]);
+ auto& out = *reinterpret_cast(&data_out[i * 4]);
+
+ nanort::Ray ray;
+ ray.org[0] = pos.x;// + nor.x * 0.005;
+ ray.org[1] = pos.y;// + nor.y * 0.005;
+ ray.org[2] = pos.z;// + nor.z * 0.005;
+ ray.dir[0] = light_dir.x;
+ ray.dir[1] = light_dir.y;
+ ray.dir[2] = light_dir.z;
+
+ float kFar = 1.0e+30f;
+ ray.min_t = 0.001f;
+ ray.max_t = kFar;
+
+ nanort::TriangleIntersector<> triangle_intersector(reinterpret_cast(m_hm_plane.vertices.data()), m_hm_plane.idx.data(), sizeof(vertex_t));
+ nanort::TriangleIntersection<> isect;
+ bool hit = m_rt_accel.Traverse(ray, triangle_intersector, &isect);
+ if (hit)
+ {
+ out = { 50, 50, 50, 255 };
+ }
+ else
+ {
+ out = { 255, 255, 255, 255 };
+ }
+ }
+ }
+ //stbi_write_jpg("bake-out.jpg", fb.getWidth(), fb.getHeight(), 4, data_out.get(), 75);
+ m_texture.update(data_out.get());
+}
diff --git a/src/node_panel_grid.h b/src/node_panel_grid.h
index aae4993..50c966b 100644
--- a/src/node_panel_grid.h
+++ b/src/node_panel_grid.h
@@ -9,11 +9,12 @@
#include "node_button.h"
#include "shape.h"
#include "image.h"
+#include "nanort.h"
class NodePanelGrid : public Node
{
public:
- enum class ShadeMode : uint8_t { Transparent, Flat, Solid };
+ enum class ShadeMode : uint8_t { Transparent, Flat, Solid, Textured };
NodeSliderH* m_groud_opacity;
NodeSliderH* m_groud_value;
@@ -29,9 +30,14 @@ public:
NodeSliderH* m_hm_lyaw;
NodeSliderH* m_hm_lpitch;
NodeButton* m_render;
+ NodeButton* m_commit;
HeightmapPlane m_hm_plane;
Image m_hm_image;
+ Texture2D m_texture;
+ Sampler m_sampler_linear;
std::string m_file_path;
+ nanort::BVHAccel m_rt_accel;
+ bool m_rt_dirty = true;;
ShadeMode m_shade_mode{ ShadeMode::Transparent };
virtual Node* clone_instantiate() const override;
@@ -41,4 +47,5 @@ public:
float get_height() const;
float get_offset() const;
void draw_heightmap(const glm::mat4& proj, const glm::mat4& camera) const;
+ void bake_uvs();
};
diff --git a/src/rtt.cpp b/src/rtt.cpp
index e32b619..2f50a72 100644
--- a/src/rtt.cpp
+++ b/src/rtt.cpp
@@ -167,6 +167,8 @@ void RTT::bindFramebuffer()
void RTT::unbindFramebuffer()
{
+ if (!bound)
+ return;
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, oldDFboID);
glBindFramebuffer(GL_READ_FRAMEBUFFER, oldRFboID);
oldRFboID = 0;
@@ -180,14 +182,28 @@ void RTT::clear(glm::vec4 color)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
-void RTT::readTextureData(uint8_t* buffer)
+uint8_t* RTT::readTextureData(uint8_t* buffer)
{
- glReadBuffer(GL_BACK);
- bindFramebuffer();
- //glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, buffer);
+ if (!buffer)
+ buffer = createBuffer();
+ GLint old;
+ glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old);
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, fboID);
glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
- unbindFramebuffer();
- //glReadBuffer(GL_NONE);
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, old);
+ return buffer;
+}
+
+float* RTT::readTextureDataFloat(float* buffer)
+{
+ if (!buffer)
+ buffer = createBufferFloat();
+ GLint old;
+ glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old);
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, fboID);
+ glReadPixels(0, 0, w, h, GL_RGBA, GL_FLOAT, buffer);
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, old);
+ return buffer;
}
uint8_t* RTT::createBuffer()
@@ -195,6 +211,11 @@ uint8_t* RTT::createBuffer()
return new uint8_t[w * h * 4];
}
+float * RTT::createBufferFloat()
+{
+ return new float[w * h * 4];
+}
+
void RTT::bindTexture()
{
glBindTexture(GL_TEXTURE_2D, texID);
diff --git a/src/rtt.h b/src/rtt.h
index 925481e..77f923c 100644
--- a/src/rtt.h
+++ b/src/rtt.h
@@ -21,8 +21,10 @@ public:
bool create(int width, int height, int tex = -1, GLint internal_format = GL_RGBA8);
bool recreate() { return create(w, h); }
void clear(glm::vec4 color = glm::vec4(0));
- void readTextureData(uint8_t* buffer);
+ uint8_t* readTextureData(uint8_t* buffer = nullptr);
+ float* readTextureDataFloat(float* buffer = nullptr);
uint8_t* createBuffer();
+ float* createBufferFloat();
void bindFramebuffer();
void unbindFramebuffer();
void bindTexture();
diff --git a/src/shader.h b/src/shader.h
index bf9bc88..5a664fd 100644
--- a/src/shader.h
+++ b/src/shader.h
@@ -31,6 +31,7 @@ enum class kShaderUniform : uint16_t
Direction = const_hash("dir"),
UseFragCoordUV2 = const_hash("fragUV2"),
LightDir = const_hash("light_dir"),
+ Mode = const_hash("mode"),
};
enum class kShader : uint16_t
@@ -56,6 +57,8 @@ enum class kShader : uint16_t
BrushStroke = const_hash("brush-stroke"),
VertexColor = const_hash("vertex-color"),
Lambert = const_hash("lambert"),
+ LambertLightmap = const_hash("lambert-lightmap"),
+ BakeUV = const_hash("bakeuv"),
};
class Shader
diff --git a/src/shape.cpp b/src/shape.cpp
index aadbc8c..8c6584f 100644
--- a/src/shape.cpp
+++ b/src/shape.cpp
@@ -255,8 +255,8 @@ bool HeightmapPlane::create(float w, float h, const Image& img, float scale, flo
int div = new_size.x - 1; // TODO: handle height also
int idx_size = (div * div * 6) + (div * (div + 1) * 4);
int vertices_size = (div + 1)*(div + 1);
- std::vector idx(idx_size);
- std::vector vertices(vertices_size);
+ idx.resize(idx_size);
+ vertices.resize(vertices_size);
std::vector nor_count((size_t)vertices_size, 0);
count[0] = div * div * 6;
@@ -277,8 +277,8 @@ bool HeightmapPlane::create(float w, float h, const Image& img, float scale, flo
{
vertex_t v;
v.pos.x = ox + dx * (float)x;
- v.pos.y = oy + dy * (float)y;
- v.pos.z = (*px++).r / 255.f;
+ v.pos.y = (*px++).r / 255.f * height;
+ v.pos.z = oy + dy * (float)y;
v.pos.w = 1;
v.uvs2 = v.uvs = glm::vec2(x, y) / (float)div;
*pv++ = v;
@@ -286,7 +286,7 @@ bool HeightmapPlane::create(float w, float h, const Image& img, float scale, flo
}
// generate indices
- const glm::vec3 yscale(1, 1, height);
+ const glm::vec3 yscale(1, height, 1);
for (int y = 0; y < div; y++)
{
int i = y * (div + 1);
@@ -363,8 +363,8 @@ bool HeightmapPlane::create(float w, float h, int div)
{
vertex_t v;
v.pos.x = ox + dx * (float)x;
- v.pos.y = oy + dy * (float)y;
- v.pos.z = 0.f;
+ v.pos.y = 0.f;
+ v.pos.z = oy + dy * (float)y;
v.pos.w = 1.f;
v.uvs2 = v.uvs = glm::vec2(x, y) / (float)div;
v.nor = { 0, 1, 0 };
diff --git a/src/shape.h b/src/shape.h
index fe9a65f..8ff8c0f 100644
--- a/src/shape.h
+++ b/src/shape.h
@@ -125,6 +125,8 @@ public:
class HeightmapPlane : public Shape
{
public:
+ std::vector idx;
+ std::vector vertices;
bool create(float w, float h, const Image& img, float scale, float height);
bool create(float w, float h, int div);
};
diff --git a/src/texture.cpp b/src/texture.cpp
index a3b588b..a93de34 100644
--- a/src/texture.cpp
+++ b/src/texture.cpp
@@ -134,7 +134,7 @@ void Sampler::set_filter(GLint filter_min, GLint filter_mag)
glSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, filter_mag);
#endif // USE_SAMPLER
}
-void Sampler::bind(int unit)
+void Sampler::bind(int unit) const
{
current_unit = unit;
#if USE_SAMPLER
diff --git a/src/texture.h b/src/texture.h
index 95004e0..bae25b1 100644
--- a/src/texture.h
+++ b/src/texture.h
@@ -27,12 +27,12 @@ public:
class Sampler
{
GLuint id = 0;
- GLint current_unit = 0;
+ mutable GLint current_unit = 0;
public:
bool create(GLint filter = GL_LINEAR, GLint wrap = GL_CLAMP_TO_EDGE);
void set(GLint filter = GL_LINEAR, GLint wrap = GL_CLAMP_TO_EDGE);
void set_filter(GLint filter_min, GLint filter_mag);
- void bind(int unit);
+ void bind(int unit) const;
void unbind();
bool ready() const { return id != 0; }
};