diff --git a/src/game/bootloader.cpp b/src/game/bootloader.cpp index a0c7687..aacfc1d 100644 --- a/src/game/bootloader.cpp +++ b/src/game/bootloader.cpp @@ -507,7 +507,7 @@ void setup_rendering(game_context* ctx) ctx->overworld_compositor->add_pass(ctx->overworld_clear_pass); ctx->overworld_compositor->add_pass(ctx->overworld_sky_pass); ctx->overworld_compositor->add_pass(ctx->overworld_material_pass); - ctx->overworld_compositor->add_pass(ctx->overworld_outline_pass); + //ctx->overworld_compositor->add_pass(ctx->overworld_outline_pass); ctx->overworld_compositor->add_pass(ctx->overworld_bloom_pass); ctx->overworld_compositor->add_pass(ctx->overworld_final_pass); @@ -617,7 +617,7 @@ void setup_scenes(game_context* ctx) // Setup lights ctx->sun_indirect = new ambient_light(); - ctx->sun_indirect->set_intensity(0.25f); + ctx->sun_indirect->set_intensity(0.5f); ctx->sun_indirect->update_tweens(); ctx->sun_direct = new directional_light(); diff --git a/src/game/states/play-state.cpp b/src/game/states/play-state.cpp index aa7ce77..694338f 100644 --- a/src/game/states/play-state.cpp +++ b/src/game/states/play-state.cpp @@ -141,7 +141,7 @@ void play_state_enter(game_context* ctx) float x = math::random(-pebble_radius, pebble_radius); float z = math::random(-pebble_radius, pebble_radius); - auto pebble_entity = pebble_archetype->create(ecs_registry); + auto pebble_entity = ant_head_archetype->create(ecs_registry); auto& transform = ecs_registry.get(pebble_entity); transform.local = math::identity_transform; @@ -159,7 +159,7 @@ void play_state_enter(game_context* ctx) auto nest_entity = nest_archetype->create(ecs_registry); // Create terrain - int terrain_radius = 4; + int terrain_radius = 2; for (int x = -terrain_radius; x <= terrain_radius; ++x) { for (int z = -terrain_radius; z <= terrain_radius; ++z) @@ -212,10 +212,7 @@ void play_state_enter(game_context* ctx) ctx->camera_system->set_camera(ctx->overworld_camera); auto ant_head = ant_head_archetype->create(ecs_registry); - math::transform head_xf = math::identity_transform; - head_xf.translation = {50, 0, 0}; - head_xf.scale = float3{1, 1, 1} * 1.0f; - ec::set_transform(ecs_registry, ant_head, head_xf, true); + ec::place(ecs_registry, ant_head, {50, 0}); ctx->overworld_scene->update_tweens(); @@ -276,7 +273,7 @@ void play_state_enter(game_context* ctx) { auto larva = larva_archetype->create(ecs_registry); ec::assign_render_layers(ecs_registry, larva, 1); - //ecs::warp_to(ecs_registry, larva, {0, -20, 0}); + ec::warp_to(ecs_registry, larva, {50, 0.1935f, 10}); //auto& transform = ecs_registry.get(larva_entity); //transform.transform = math::identity_transform; //transform.transform.translation = nest->get_shaft_position(*central_shaft, central_shaft->depth[1]); diff --git a/src/game/systems/subterrain-system.cpp b/src/game/systems/subterrain-system.cpp index 56b36c6..50f7d62 100644 --- a/src/game/systems/subterrain-system.cpp +++ b/src/game/systems/subterrain-system.cpp @@ -380,7 +380,7 @@ void subterrain_system::march(::cube_tree* node) void subterrain_system::regenerate_subterrain_model() { - float* face_normals = new float[subterrain_mesh->get_faces().size() * 3]; + float3* face_normals = new float3[subterrain_mesh->get_faces().size()]; calculate_face_normals(face_normals, *subterrain_mesh); static const float3 barycentric_coords[3] = @@ -414,7 +414,7 @@ void subterrain_system::regenerate_subterrain_model() { if (edge->face) { - n += reinterpret_cast(face_normals[edge->face->index * 3]); + n += face_normals[edge->face->index]; } edge = edge->previous->symmetric; diff --git a/src/game/systems/terrain-system.cpp b/src/game/systems/terrain-system.cpp index 227b76b..6582915 100644 --- a/src/game/systems/terrain-system.cpp +++ b/src/game/systems/terrain-system.cpp @@ -175,7 +175,7 @@ model* terrain_system::generate_terrain_model(mesh* terrain_mesh) vertex_array* vao = terrain_model->get_vertex_array(); // Resize VBO - int vertex_size = 3 + 3 + 3; + int vertex_size = 3 + 2 + 3 + 4 + 3; int vertex_stride = vertex_size * sizeof(float); vbo->resize(terrain_mesh->get_faces().size() * 3 * vertex_stride, nullptr); @@ -183,8 +183,12 @@ model* terrain_system::generate_terrain_model(mesh* terrain_mesh) std::size_t offset = 0; vao->bind_attribute(VERTEX_POSITION_LOCATION, *vbo, 3, vertex_attribute_type::float_32, vertex_stride, 0); offset += 3; + vao->bind_attribute(VERTEX_TEXCOORD_LOCATION, *vbo, 2, vertex_attribute_type::float_32, vertex_stride, sizeof(float) * offset); + offset += 2; vao->bind_attribute(VERTEX_NORMAL_LOCATION, *vbo, 3, vertex_attribute_type::float_32, vertex_stride, sizeof(float) * offset); offset += 3; + vao->bind_attribute(VERTEX_TANGENT_LOCATION, *vbo, 4, vertex_attribute_type::float_32, vertex_stride, sizeof(float) * offset); + offset += 4; vao->bind_attribute(VERTEX_BARYCENTRIC_LOCATION, *vbo, 3, vertex_attribute_type::float_32, vertex_stride, sizeof(float) * offset); offset += 3; @@ -223,11 +227,13 @@ void terrain_system::project_terrain_mesh(mesh* terrain_mesh, const terrain_comp void terrain_system::update_terrain_model(model* terrain_model, mesh* terrain_mesh) { - aabb bounds = - { - {std::numeric_limits::infinity(), std::numeric_limits::infinity(), std::numeric_limits::infinity()}, - {-std::numeric_limits::infinity(), -std::numeric_limits::infinity(), -std::numeric_limits::infinity()} - }; + const std::vector& faces = terrain_mesh->get_faces(); + const std::vector& vertices = terrain_mesh->get_vertices(); + + aabb bounds = calculate_bounds(*terrain_mesh); + float bounds_width = bounds.max_point.x - bounds.min_point.x; + float bounds_height = bounds.max_point.y - bounds.min_point.y; + float bounds_depth = bounds.max_point.z - bounds.min_point.z; static const float3 barycentric_coords[3] = { @@ -236,20 +242,56 @@ void terrain_system::update_terrain_model(model* terrain_model, mesh* terrain_me float3{0, 0, 1} }; - int triangle_count = terrain_mesh->get_faces().size(); + int triangle_count = faces.size(); int vertex_count = triangle_count * 3; - int vertex_size = 3 + 3 + 3; + int vertex_size = 3 + 2 + 3 + 4 + 3; // Allocate vertex data float* vertex_data = new float[vertex_size * vertex_count]; - // Allocate face normals - float* face_normals = new float[terrain_mesh->get_faces().size() * 3]; + // Allocate and calculate face normals + float3* face_normals = new float3[faces.size()]; calculate_face_normals(face_normals, *terrain_mesh); + + // Allocate and calculate vertex normals + float3* vertex_normals = new float3[vertices.size()]; + for (std::size_t i = 0; i < vertices.size(); ++i) + { + const mesh::vertex* vertex = vertices[i]; + + float3 n = {0, 0, 0}; + mesh::edge* start = vertex->edge; + mesh::edge* edge = start; + do + { + if (edge->face) + { + n += face_normals[edge->face->index]; + } + + edge = edge->previous->symmetric; + } + while (edge != start); + n = math::normalize(n); + + vertex_normals[i] = n; + } + + // Allocate and generate vertex texture coordinates + float2* vertex_texcoords = new float2[vertices.size()]; + for (std::size_t i = 0; i < vertices.size(); ++i) + { + const mesh::vertex* vertex = vertices[i]; + vertex_texcoords[i].x = (vertex->position.x - bounds.min_point.x) / bounds_width; + vertex_texcoords[i].y = (vertex->position.z - bounds.min_point.z) / bounds_depth; + } + + // Allocate and calculate vertex tangents + float4* vertex_tangents = new float4[vertices.size()]; + calculate_vertex_tangents(vertex_tangents, vertex_texcoords, vertex_normals, *terrain_mesh); // Generate vertex data float* v = vertex_data; - const std::vector& faces = terrain_mesh->get_faces(); for (int i = 0; i < triangle_count; ++i) { const mesh::face* triangle = faces[i]; @@ -261,38 +303,31 @@ void terrain_system::update_terrain_model(model* terrain_model, mesh* terrain_me for (int j = 0; j < 3; ++j) { const mesh::vertex* vertex = abc[j]; - - float3 n = {0, 0, 0}; - mesh::edge* start = vertex->edge; - mesh::edge* edge = start; - do - { - if (edge->face) - { - n += reinterpret_cast(face_normals[edge->face->index * 3]); - } - - edge = edge->previous->symmetric; - } - while (edge != start); - n = math::normalize(n); - - *(v++) = vertex->position[0]; - *(v++) = vertex->position[1]; - *(v++) = vertex->position[2]; - *(v++) = n[0]; - *(v++) = n[1]; - *(v++) = n[2]; - *(v++) = barycentric_coords[j][0]; - *(v++) = barycentric_coords[j][1]; - *(v++) = barycentric_coords[j][2]; + const float3& position = vertex->position; + const float2& texcoord = vertex_texcoords[vertex->index]; + const float3& normal = vertex_normals[vertex->index]; + const float4& tangent = vertex_tangents[vertex->index]; + const float3& barycentric = barycentric_coords[j]; + + *(v++) = position.x; + *(v++) = position.y; + *(v++) = position.z; - // Add position to bounds - for (int i = 0; i < 3; ++i) - { - bounds.min_point[i] = std::min(bounds.min_point[i], vertex->position[i]); - bounds.max_point[i] = std::max(bounds.max_point[i], vertex->position[i]); - } + *(v++) = texcoord.x; + *(v++) = texcoord.y; + + *(v++) = normal.x; + *(v++) = normal.y; + *(v++) = normal.z; + + *(v++) = tangent.x; + *(v++) = tangent.y; + *(v++) = tangent.z; + *(v++) = tangent.w; + + *(v++) = barycentric.x; + *(v++) = barycentric.y; + *(v++) = barycentric.z; } } @@ -304,6 +339,9 @@ void terrain_system::update_terrain_model(model* terrain_model, mesh* terrain_me // Free vertex data delete[] face_normals; + delete[] vertex_normals; + delete[] vertex_texcoords; + delete[] vertex_tangents; delete[] vertex_data; } diff --git a/src/geometry/mesh-functions.cpp b/src/geometry/mesh-functions.cpp index c3d179e..969863d 100644 --- a/src/geometry/mesh-functions.cpp +++ b/src/geometry/mesh-functions.cpp @@ -70,31 +70,95 @@ void create_triangle_mesh(mesh& mesh, const std::vector& vertices, const } } -void calculate_face_normals(float* normals, const mesh& mesh) +void calculate_face_normals(float3* normals, const mesh& mesh) { const std::vector& faces = mesh.get_faces(); for (std::size_t i = 0; i < faces.size(); ++i) { const mesh::face& face = *(faces[i]); - float3& normal = reinterpret_cast(normals[i * 3]); + const float3& a = face.edge->vertex->position; + const float3& b = face.edge->next->vertex->position; + const float3& c = face.edge->previous->vertex->position; - 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); - - normal = math::normalize(math::cross(b - a, c - a)); + normals[i] = math::normalize(math::cross(b - a, c - a)); } } 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); + const float3& a = face.edge->vertex->position; + const float3& b = face.edge->next->vertex->position; + const float3& c = face.edge->previous->vertex->position; return math::normalize(math::cross(b - a, c - a)); } +void calculate_vertex_tangents(float4* tangents, const float2* texcoords, const float3* normals, const mesh& mesh) +{ + const std::vector& faces = mesh.get_faces(); + const std::vector& vertices = mesh.get_vertices(); + + // Allocate tangent and bitangent buffers + float3* tangent_buffer = new float3[vertices.size()]; + float3* bitangent_buffer = new float3[vertices.size()]; + for (std::size_t i = 0; i < vertices.size(); ++i) + { + tangent_buffer[i] = {0.0f, 0.0f, 0.0f}; + bitangent_buffer[i] = {0.0f, 0.0f, 0.0f}; + } + + // Accumulate tangents and bitangents + for (std::size_t i = 0; i < faces.size(); ++i) + { + const mesh::face& face = *(faces[i]); + std::size_t ia = face.edge->vertex->index; + std::size_t ib = face.edge->next->vertex->index; + std::size_t ic = face.edge->previous->vertex->index; + const float3& a = vertices[ia]->position; + const float3& b = vertices[ib]->position; + const float3& c = vertices[ic]->position; + const float2& uva = texcoords[ia]; + const float2& uvb = texcoords[ib]; + const float2& uvc = texcoords[ic]; + + 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 = (ba * uvca.y - ca * uvba.y) * f; + float3 bitangent = (ba * -uvca.x + ca * uvba.x) * f; + + tangent_buffer[ia] += tangent; + tangent_buffer[ib] += tangent; + tangent_buffer[ic] += tangent; + bitangent_buffer[ia] += bitangent; + bitangent_buffer[ib] += bitangent; + bitangent_buffer[ic] += bitangent; + } + + // Orthogonalize tangents + for (std::size_t i = 0; i < vertices.size(); ++i) + { + const float3& n = normals[i]; + const float3& t = tangent_buffer[i]; + const float3& b = bitangent_buffer[i]; + + // Gram-Schmidt orthogonalize tangent + float3 tangent = math::normalize(t - n * math::dot(n, t)); + + // Calculate bitangent sign + float bitangent_sign = (math::dot(math::cross(n, t), b) < 0.0f) ? -1.0f : 1.0f; + + tangents[i] = {tangent.x, tangent.y, tangent.z, bitangent_sign}; + } + + // Free faceted tangents and bitangents + delete[] tangent_buffer; + delete[] bitangent_buffer; +} + aabb calculate_bounds(const mesh& mesh) { float3 bounds_min; diff --git a/src/geometry/mesh-functions.hpp b/src/geometry/mesh-functions.hpp index 00306cd..6f9d3dc 100644 --- a/src/geometry/mesh-functions.hpp +++ b/src/geometry/mesh-functions.hpp @@ -39,15 +39,20 @@ void create_triangle_mesh(mesh& mesh, const std::vector& vertices, const /** * Calculates normals for each face. * - * @param[out] Array in which normals will be stored. Must be able to hold `3 * face count` floats. - * @param layer Face layer in which the normals will be stored. Must have three components. + * @param[out] Array in which faceted normals will be stored. */ -void calculate_face_normals(float* normals, const mesh& mesh); +void calculate_face_normals(float3* normals, const mesh& mesh); float3 calculate_face_normal(const mesh::face& face); -void calculate_vertex_normals(float* normals, const mesh& mesh); - +/** + * Calculates smooth tangents per-vertex. + * + * @param[out] tangents Array in which vertex tangents will be stored. A bitangent sign is stored in each tangent vector's fourth component. + * @param[in] texcoords Array containing vertex texture coordinates. + * @param[in] normals Array containing vertex normals. + */ +void calculate_vertex_tangents(float4* tangents, const float2* texcoords, const float3* normals, const mesh& mesh); /** diff --git a/src/geometry/mesh.cpp b/src/geometry/mesh.cpp index b0e023d..31c7eb9 100644 --- a/src/geometry/mesh.cpp +++ b/src/geometry/mesh.cpp @@ -42,7 +42,7 @@ mesh::~mesh() } } -mesh::vertex* mesh::add_vertex(const std::array& position) +mesh::vertex* mesh::add_vertex(const float3& position) { mesh::vertex* vertex = new mesh::vertex(); vertex->edge = nullptr; diff --git a/src/geometry/mesh.hpp b/src/geometry/mesh.hpp index 5624fda..be8983c 100644 --- a/src/geometry/mesh.hpp +++ b/src/geometry/mesh.hpp @@ -20,8 +20,8 @@ #ifndef ANTKEEPER_MESH_HPP #define ANTKEEPER_MESH_HPP -#include #include +#include "utility/fundamental-types.hpp" /** * Half-edge mesh. @@ -52,7 +52,7 @@ public: * @param position Position of the vertex. * @return Pointer to the added vertex. */ - mesh::vertex* add_vertex(const std::array& position); + mesh::vertex* add_vertex(const float3& position); /** * Adds an edge to the mesh. @@ -110,7 +110,7 @@ public: mesh::edge* edge; /// Vertex position - std::array position; + float3 position; /// Index of this vertex std::size_t index;