Browse Source

Add smooth tangent calculator to mesh functions, and make terain system generate UVs and tangents

master
C. J. Howard 3 years ago
parent
commit
7ed065ac16
8 changed files with 176 additions and 72 deletions
  1. +2
    -2
      src/game/bootloader.cpp
  2. +4
    -7
      src/game/states/play-state.cpp
  3. +2
    -2
      src/game/systems/subterrain-system.cpp
  4. +80
    -42
      src/game/systems/terrain-system.cpp
  5. +74
    -10
      src/geometry/mesh-functions.cpp
  6. +10
    -5
      src/geometry/mesh-functions.hpp
  7. +1
    -1
      src/geometry/mesh.cpp
  8. +3
    -3
      src/geometry/mesh.hpp

+ 2
- 2
src/game/bootloader.cpp View File

@ -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();

+ 4
- 7
src/game/states/play-state.cpp View File

@ -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<ecs::transform_component>(pebble_entity);
transform.local = math::identity_transform<float>;
@ -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<float> head_xf = math::identity_transform<float>;
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<ecs::transform_component>(larva_entity);
//transform.transform = math::identity_transform<float>;
//transform.transform.translation = nest->get_shaft_position(*central_shaft, central_shaft->depth[1]);

+ 2
- 2
src/game/systems/subterrain-system.cpp View File

@ -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<const float3&>(face_normals[edge->face->index * 3]);
n += face_normals[edge->face->index];
}
edge = edge->previous->symmetric;

+ 80
- 42
src/game/systems/terrain-system.cpp View File

@ -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<float> bounds =
{
{std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity()},
{-std::numeric_limits<float>::infinity(), -std::numeric_limits<float>::infinity(), -std::numeric_limits<float>::infinity()}
};
const std::vector<mesh::face*>& faces = terrain_mesh->get_faces();
const std::vector<mesh::vertex*>& vertices = terrain_mesh->get_vertices();
aabb<float> 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<mesh::face*>& 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<const float3&>(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<float>(bounds.min_point[i], vertex->position[i]);
bounds.max_point[i] = std::max<float>(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;
}

+ 74
- 10
src/geometry/mesh-functions.cpp View File

@ -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<mesh::face*>& faces = mesh.get_faces();
for (std::size_t i = 0; i < faces.size(); ++i)
{
const mesh::face& face = *(faces[i]);
float3& normal = reinterpret_cast<float3&>(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<const float3&>(face.edge->vertex->position);
const float3& b = reinterpret_cast<const float3&>(face.edge->next->vertex->position);
const float3& c = reinterpret_cast<const float3&>(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<const float3&>(face.edge->vertex->position);
const float3& b = reinterpret_cast<const float3&>(face.edge->next->vertex->position);
const float3& c = reinterpret_cast<const float3&>(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<mesh::face*>& faces = mesh.get_faces();
const std::vector<mesh::vertex*>& 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<float> calculate_bounds(const mesh& mesh)
{
float3 bounds_min;

+ 10
- 5
src/geometry/mesh-functions.hpp View File

@ -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);
/**

+ 1
- 1
src/geometry/mesh.cpp View File

@ -42,7 +42,7 @@ mesh::~mesh()
}
}
mesh::vertex* mesh::add_vertex(const std::array<float, 3>& position)
mesh::vertex* mesh::add_vertex(const float3& position)
{
mesh::vertex* vertex = new mesh::vertex();
vertex->edge = nullptr;

+ 3
- 3
src/geometry/mesh.hpp View File

@ -20,8 +20,8 @@
#ifndef ANTKEEPER_MESH_HPP
#define ANTKEEPER_MESH_HPP
#include <array>
#include <vector>
#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<float, 3>& 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<float, 3> position;
float3 position;
/// Index of this vertex
std::size_t index;

Loading…
Cancel
Save