diff --git a/src/game/systems/painting-system.cpp b/src/game/systems/painting-system.cpp index ec951bd..d52f5e3 100644 --- a/src/game/systems/painting-system.cpp +++ b/src/game/systems/painting-system.cpp @@ -35,6 +35,7 @@ #include "rasterizer/vertex-buffer.hpp" #include "rasterizer/vertex-attribute-type.hpp" #include "renderer/vertex-attributes.hpp" +#include "geometry/mesh-functions.hpp" #include using namespace ecs; @@ -56,9 +57,9 @@ painting_system::painting_system(entt::registry& registry, ::event_dispatcher* e min_stroke_length_squared = min_stroke_length * min_stroke_length; max_stroke_segments = 4096; current_stroke_segment = 0; - std::size_t vertex_size = 4; - std::size_t vertex_stride = sizeof(float) * vertex_size; - std::size_t vertex_count = max_stroke_segments * 6; + vertex_size = 15; + vertex_stride = sizeof(float) * vertex_size; + vertex_count = max_stroke_segments * 6; // Create stroke model stroke_model = new model(); @@ -69,6 +70,10 @@ painting_system::painting_system(entt::registry& registry, ::event_dispatcher* e stroke_vbo = stroke_model->get_vertex_buffer(); stroke_vbo->repurpose(sizeof(float) * vertex_size * vertex_count, nullptr, buffer_usage::dynamic_draw); stroke_model->get_vertex_array()->bind_attribute(VERTEX_POSITION_LOCATION, *stroke_vbo, 4, vertex_attribute_type::float_32, vertex_stride, 0); + stroke_model->get_vertex_array()->bind_attribute(VERTEX_NORMAL_LOCATION, *stroke_vbo, 3, vertex_attribute_type::float_32, vertex_stride, sizeof(float) * 4); + stroke_model->get_vertex_array()->bind_attribute(VERTEX_TEXCOORD_LOCATION, *stroke_vbo, 2, vertex_attribute_type::float_32, vertex_stride, sizeof(float) * 7); + stroke_model->get_vertex_array()->bind_attribute(VERTEX_TANGENT_LOCATION, *stroke_vbo, 3, vertex_attribute_type::float_32, vertex_stride, sizeof(float) * 9); + stroke_model->get_vertex_array()->bind_attribute(VERTEX_BITANGENT_LOCATION, *stroke_vbo, 3, vertex_attribute_type::float_32, vertex_stride, sizeof(float) * 12); // Create stroke model instance stroke_model_instance = new model_instance(); @@ -100,21 +105,25 @@ void painting_system::update(double t, double dt) auto cast_result = cast_ray(tool.cursor); if (cast_result.has_value()) { - float3 p2 = - stroke_end = cast_result.value(); + stroke_end = std::get<0>(cast_result.value()); + float3 surface_normal = std::get<1>(cast_result.value()); float3 segment_difference = stroke_end - stroke_start; float segment_length_squared = math::dot(segment_difference, segment_difference); if (segment_length_squared >= min_stroke_length_squared) { float segment_length = std::sqrt(segment_length_squared); - float3 segment_forward = segment_difference / segment_length; - float3 segment_right = math::normalize(math::cross(segment_forward, float3{0, 1, 0})); - float3 segment_up = math::cross(segment_right, segment_forward); + float3 segment_right = math::normalize(math::cross(segment_forward, surface_normal)); + float3 segment_up = math::cross(segment_right, segment_forward); + + float angle = std::acos(math::dot(segment_forward, float3{0, 0, -1})); + float3 cross = math::cross(segment_forward, float3{0, 0, -1}); + if (math::dot(surface_normal, cross) < 0.0f) + angle = -angle; - float3 segment_center = (stroke_start + stroke_end) * 0.5f; + math::quaternion tangent_rotation = math::normalize(math::angle_axis(-angle, surface_normal)); float3 p1 = stroke_start; float3 p2 = stroke_end; @@ -145,30 +154,99 @@ void painting_system::update(double t, double dt) } } - float4 segment_vertices[12]; - float w = static_cast(t); + const float3 positions[] = + { + a, b, c, + c, b, d, + c, d, e, + e, d, f + }; + const float w = static_cast(t); + + float2 texcoords[] = + { + {0, 0}, {1, 0}, {0, 1}, + {0, 1}, {1, 0}, {1, 1}, + {0, 0}, {1, 0}, {0, 1}, + {0, 1}, {1, 0}, {1, 1}, + }; + + float3 tangent_positions[] = + { + {0, 0, 0}, {1, 0, 0}, {0, 0, 1}, + {0, 0, 1}, {1, 0, 0}, {1, 0, 1}, + {0, 0, 0}, {1, 0, 0}, {0, 0, 1}, + {0, 0, 1}, {1, 0, 0}, {1, 0, 1} + }; + + /// @TODO: smooth normals in middle of segment + + float3 tangents[12]; + float3 bitangents[12]; + for (int i = 0; i < 4; ++i) + { + const float3& a = tangent_positions[i * 3]; + const float3& b = tangent_positions[i * 3 + 1]; + const float3& c = tangent_positions[i * 3 + 2]; + const float2& uva = texcoords[i * 3]; + const float2& uvb = texcoords[i * 3 + 1]; + const float2& uvc = texcoords[i * 3 + 2]; + + float3 ba = b - a; + float3 ca = c - a; + float2 uvba = uvb - uva; + float2 uvca = uvc - uva; + + float f = 1.0f / (uvba.x * uvca.y - uvca.x * uvba.y); + float3 tangent = math::normalize((ba * uvca.y - ca * uvba.y) * f); + float3 bitangent = math::normalize((ba * -uvca.x + ca * uvba.x) * f); + + // Rotate tangent and bitangent according to segment rotation + tangent = math::normalize(tangent_rotation * tangent); + bitangent = math::normalize(tangent_rotation * bitangent); + + tangents[i * 3] = tangent; + tangents[i * 3 + 1] = tangent; + tangents[i * 3 + 2] = tangent; + + bitangents[i * 3] = bitangent; + bitangents[i * 3 + 1] = bitangent; + bitangents[i * 3 + 2] = bitangent; + } - segment_vertices[0] = {a.x, a.y, a.z, w}; - segment_vertices[1] = {b.x, b.y, b.z, w}; - segment_vertices[2] = {c.x, c.y, c.z, w}; - segment_vertices[3] = {c.x, c.y, c.z, w}; - segment_vertices[4] = {b.x, b.y, b.z, w}; - segment_vertices[5] = {d.x, d.y, d.z, w}; - segment_vertices[6] = {c.x, c.y, c.z, w}; - segment_vertices[7] = {d.x, d.y, d.z, w}; - segment_vertices[8] = {e.x, e.y, e.z, w}; - segment_vertices[9] = {e.x, e.y, e.z, w}; - segment_vertices[10] = {d.x, d.y, d.z, w}; - segment_vertices[11] = {f.x, f.y, f.z, w}; + float vertex_data[15 * 12]; + float* v = &vertex_data[0]; + for (int i = 0; i < 12; ++i) + { + *(v++) = positions[i].x; + *(v++) = positions[i].y; + *(v++) = positions[i].z; + *(v++) = w; + + *(v++) = surface_normal.x; + *(v++) = surface_normal.y; + *(v++) = surface_normal.z; + + *(v++) = texcoords[i].x; + *(v++) = texcoords[i].y; + + *(v++) = tangents[i].x; + *(v++) = tangents[i].y; + *(v++) = tangents[i].z; + + *(v++) = bitangents[i].x; + *(v++) = bitangents[i].y; + *(v++) = bitangents[i].z; + } - std::size_t segment_size = sizeof(float) * 4 * 6; + std::size_t segment_size = sizeof(float) * vertex_size * 6; if (mitered) { - stroke_vbo->update((current_stroke_segment - 1) * segment_size, segment_size * 2, &segment_vertices[0][0]); + stroke_vbo->update((current_stroke_segment - 1) * segment_size, segment_size * 2, &vertex_data[0]); } else { - stroke_vbo->update(current_stroke_segment * segment_size, segment_size, &segment_vertices[6][0]); + stroke_vbo->update(current_stroke_segment * segment_size, segment_size, &vertex_data[vertex_size * 6]); } ++current_stroke_segment; @@ -211,7 +289,7 @@ void painting_system::handle_event(const tool_pressed_event& event) { brush_entity = event.entity; painting = true; - stroke_start = cast_result.value(); + stroke_start = std::get<0>(cast_result.value()); stroke_end = stroke_start; p0 = stroke_start; p0a = p0; @@ -229,7 +307,7 @@ void painting_system::handle_event(const tool_released_event& event) if (cast_result.has_value()) { - stroke_end = cast_result.value(); + stroke_end = std::get<0>(cast_result.value()); } brush_entity = entt::null; @@ -237,9 +315,13 @@ void painting_system::handle_event(const tool_released_event& event) } } -std::optional painting_system::cast_ray(const float3& position) const +std::optional> painting_system::cast_ray(const float3& position) const { - std::optional result; + std::optional> result; + + float3 intersection; + float3 surface_normal; + mesh::face* face = nullptr; ray untransformed_ray = {position + float3{0.0f, 10000.0f, 0.0f}, {0, -1, 0}}; float min_distance = std::numeric_limits::infinity(); @@ -267,10 +349,17 @@ std::optional painting_system::cast_ray(const float3& position) const if (mesh_result->t < min_distance) { min_distance = mesh_result->t; - result = untransformed_ray.extrapolate(min_distance); + intersection = untransformed_ray.extrapolate(min_distance); + face = mesh_result->face; } } }); + if (face != nullptr) + { + surface_normal = calculate_face_normal(*face); + result = std::make_tuple(intersection, surface_normal); + } + return result; } diff --git a/src/game/systems/painting-system.hpp b/src/game/systems/painting-system.hpp index a689bbe..708a6de 100644 --- a/src/game/systems/painting-system.hpp +++ b/src/game/systems/painting-system.hpp @@ -51,7 +51,7 @@ private: virtual void handle_event(const tool_pressed_event& event); virtual void handle_event(const tool_released_event& event); - std::optional cast_ray(const float3& position) const; + std::optional> cast_ray(const float3& position) const; event_dispatcher* event_dispatcher; resource_manager* resource_manager; @@ -74,6 +74,10 @@ private: float3 p0a; float3 p0b; + std::size_t vertex_size; + std::size_t vertex_stride; + std::size_t vertex_count; + model* stroke_model; model_group* stroke_model_group; vertex_buffer* stroke_vbo; diff --git a/src/geometry/mesh-accelerator.cpp b/src/geometry/mesh-accelerator.cpp index ede4b39..24fee2b 100644 --- a/src/geometry/mesh-accelerator.cpp +++ b/src/geometry/mesh-accelerator.cpp @@ -170,7 +170,7 @@ octree32::node_type mesh_accelerator::find_node(const float3& point) const static_cast(transformed_point.y), static_cast(transformed_point.z)); - /// Return max depth node at the determined location + // Return max depth node at the determined location return octree32::node(octree32::max_depth, location); } diff --git a/src/geometry/mesh-functions.cpp b/src/geometry/mesh-functions.cpp index 8f068a2..c3d179e 100644 --- a/src/geometry/mesh-functions.cpp +++ b/src/geometry/mesh-functions.cpp @@ -87,6 +87,14 @@ void calculate_face_normals(float* normals, const mesh& mesh) } } +float3 calculate_face_normal(const mesh::face& face) +{ + const float3& a = reinterpret_cast(face.edge->vertex->position); + const float3& b = reinterpret_cast(face.edge->next->vertex->position); + const float3& c = reinterpret_cast(face.edge->previous->vertex->position); + return math::normalize(math::cross(b - a, c - a)); +} + aabb calculate_bounds(const mesh& mesh) { float3 bounds_min; diff --git a/src/geometry/mesh-functions.hpp b/src/geometry/mesh-functions.hpp index 410e7da..00306cd 100644 --- a/src/geometry/mesh-functions.hpp +++ b/src/geometry/mesh-functions.hpp @@ -44,8 +44,12 @@ void create_triangle_mesh(mesh& mesh, const std::vector& vertices, const */ void calculate_face_normals(float* normals, const mesh& mesh); +float3 calculate_face_normal(const mesh::face& face); + void calculate_vertex_normals(float* normals, const mesh& mesh); + + /** * Calculates the AABB bounds of a mesh. */ diff --git a/src/renderer/passes/material-pass.cpp b/src/renderer/passes/material-pass.cpp index 123b8ec..c389d8e 100644 --- a/src/renderer/passes/material-pass.cpp +++ b/src/renderer/passes/material-pass.cpp @@ -353,10 +353,10 @@ void material_pass::render(render_context* context) const glEnable(GL_STENCIL_TEST); glStencilFunc(GL_EQUAL, 1, ~0); - //glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO); - //glStencilMask(~0); - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - glStencilMask(0); + glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO); + glStencilMask(~0); + //glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + //glStencilMask(0); } else {