Program Listing for File mesh.cpp¶
↰ Return to documentation for file (/home/runner/work/Legion-Engine/Legion-Engine/legion/engine/core/data/mesh.cpp
)
#include <core/data/mesh.hpp>
#include <core/data/importers/mesh_importers.hpp>
namespace legion::core
{
std::unordered_map<id_type, std::unique_ptr<std::pair<async::rw_spinlock, mesh>>> MeshCache::m_meshes;
async::rw_spinlock MeshCache::m_meshesLock;
id_type MeshCache::debugId;
void mesh::to_resource(filesystem::basic_resource* resource, const mesh& value)
{
OPTICK_EVENT();
// Erase all previous data.
resource->clear();
// Write new data.
auto& data = resource->get();
appendBinaryData(&value.filePath, data);
appendBinaryData(&value.vertices, data);
appendBinaryData(&value.colors, data);
appendBinaryData(&value.normals, data);
appendBinaryData(&value.uvs, data);
appendBinaryData(&value.tangents, data);
appendBinaryData(&value.indices, data);
// Append each submesh data.
uint64 submeshCount = value.submeshes.size();
appendBinaryData(&submeshCount, data);
for (auto& submesh : value.submeshes)
{
appendBinaryData(&submesh.name, data);
appendBinaryData(&submesh.indexCount, data);
appendBinaryData(&submesh.indexOffset, data);
}
}
void mesh::from_resource(mesh* value, const filesystem::basic_resource& resource)
{
OPTICK_EVENT();
*value = mesh{};
// Get point from which to start reading.
byte_vec::const_iterator start = resource.begin();
// Read data
retrieveBinaryData(value->filePath, start);
retrieveBinaryData(value->vertices, start);
retrieveBinaryData(value->colors, start);
retrieveBinaryData(value->normals, start);
retrieveBinaryData(value->uvs, start);
retrieveBinaryData(value->tangents, start);
retrieveBinaryData(value->indices, start);
// Read sub-mesh count
uint64 submeshCount;
retrieveBinaryData(submeshCount, start);
// Read and append all sub-meshes
for (int i = 0; i < submeshCount; i++)
{
sub_mesh submesh;
retrieveBinaryData(submesh.name, start);
retrieveBinaryData(submesh.indexCount, start);
retrieveBinaryData(submesh.indexOffset, start);
value->submeshes.push_back(submesh);
}
}
void mesh::calculate_tangents(mesh* data)
{
OPTICK_EVENT();
// https://learnopengl.com/Advanced-Lighting/Normal-Mapping
data->tangents.resize(data->normals.size());
// Iterate over all triangles of each sub-mesh.
for (auto& submesh : data->submeshes)
for (unsigned i = submesh.indexOffset; i < submesh.indexOffset + submesh.indexCount; i += 3)
{
// Get vertices of the triangle.
math::vec3 vtx0 = data->vertices[data->indices[i]];
math::vec3 vtx1 = data->vertices[data->indices[i + 1]];
math::vec3 vtx2 = data->vertices[data->indices[i + 2]];
// Get UVs of the triangle.
math::vec2 uv0 = data->uvs[data->indices[i]];
math::vec2 uv1 = data->uvs[data->indices[i + 1]];
math::vec2 uv2 = data->uvs[data->indices[i + 2]];
// Get primary edges
math::vec3 edge0 = vtx1 - vtx0;
math::vec3 edge1 = vtx2 - vtx0;
// Get difference in uv over the two primary edges.
math::vec2 deltaUV0 = uv1 - uv0;
math::vec2 deltaUV1 = uv2 - uv0;
// Get inverse of the determinant of the UV tangent matrix.
float inverseUVDeterminant = 1.0f / (deltaUV0.x * deltaUV1.y - deltaUV1.x * deltaUV0.y);
// T = tangent
// B = bi-tangent
// E0 = first primary edge
// E1 = second primary edge
// dU0 = delta of x texture coordinates of the first primary edge
// dV0 = delta of y texture coordinates of the first primary edge
// dU1 = delta of x texture coordinates of the second primary edge
// dV1 = delta of y texture coordinates of the second primary edge
// ┌ ┐ 1 ┌ ┐ ┌ ┐
// │ Tx Ty Tz │ _ ─────────────── │ dV1 -dV0 │ │ E0x E0y E0z │
// │ Bx By Bz │ ─ dU0ΔV1 - dU1ΔV0 │ -dU1 dU0 │ │ E1x E1y E1z │
// └ ┘ └ ┘ └ ┘
math::vec3 tangent;
tangent.x = inverseUVDeterminant * ((deltaUV1.y * edge0.x) - (deltaUV0.y * edge1.x));
tangent.y = inverseUVDeterminant * ((deltaUV1.y * edge0.y) - (deltaUV0.y * edge1.y));
tangent.z = inverseUVDeterminant * ((deltaUV1.y * edge0.z) - (deltaUV0.y * edge1.z));
// Check if the tangent is valid.
if (tangent == math::vec3(0, 0, 0) || tangent != tangent)
continue;
// Normalize the tangent.
tangent = math::normalize(tangent);
// Accumulate the tangent in order to be able to smooth it later.
data->tangents[data->indices[i]] += tangent;
data->tangents[data->indices[i + 1]] += tangent;
data->tangents[data->indices[i + 2]] += tangent;
}
// Smooth all tangents.
for (unsigned i = 0; i < data->tangents.size(); i++)
if (data->tangents[i] != math::vec3(0, 0, 0))
data->tangents[i] = math::normalize(data->tangents[i]);
}
std::pair<async::rw_spinlock&, mesh&> mesh_handle::get()
{
OPTICK_EVENT();
async::readonly_guard guard(MeshCache::m_meshesLock);
auto& [lock, mesh] = *(MeshCache::m_meshes[id].get());
return std::make_pair(std::ref(lock), std::ref(mesh));
}
mesh_handle MeshCache::create_mesh(const std::string& name, const filesystem::view& file, mesh_import_settings settings)
{
OPTICK_EVENT();
// Get ID.
id_type id = nameHash(name);
{ // Check if the mesh already exists, and return that instead if it does.
async::readonly_guard guard(m_meshesLock);
if (m_meshes.count(id))
return { id };
}
if (!file.is_valid() || !file.file_info().is_file)
return invalid_mesh_handle;
// Try to load the mesh.
auto result = filesystem::AssetImporter::tryLoad<mesh>(file, settings);
if (result != common::valid)
{
log::error("Error while loading file: {} {}", static_cast<std::string>(file.get_filename()), result.get_error());
return invalid_mesh_handle;
}
mesh data = result;
data.filePath = file.get_virtual_path(); // Set the filename.
{ // Insert the mesh into the mesh list.
async::readwrite_guard guard(m_meshesLock);
auto* pair_ptr = new std::pair<async::rw_spinlock, mesh>();
pair_ptr->second = std::move(data);
m_meshes.emplace(id, std::unique_ptr<std::pair<async::rw_spinlock, mesh>>(pair_ptr));
}
return { id };
}
mesh_handle MeshCache::create_mesh(const std::string& name, const mesh& meshData)
{
id_type newId = nameHash(name); // Get the new id.
async::readwrite_guard guard(m_meshesLock);
auto* pair_ptr = new std::pair<async::rw_spinlock, mesh>();
pair_ptr->second = std::move(meshData);
m_meshes.emplace(newId, std::unique_ptr<std::pair<async::rw_spinlock, mesh>>(pair_ptr));
return { newId };
}
mesh_handle MeshCache::copy_mesh(const std::string& name, const std::string& newName)
{
OPTICK_EVENT();
id_type id = nameHash(name); // Get the id of the original mesh.
id_type newId = nameHash(newName); // Get the new id.
{
async::readonly_guard guard(m_meshesLock);
mesh data = m_meshes[id]->second; // Get a copy of the original mesh.
if (m_meshes.count(newId))
{// If the new mesh already exists, overwrite it.
mesh& destination = m_meshes[newId]->second;
async::readwrite_guard rwguard(m_meshes[newId]->first);
destination = data;
}
else // If the new mesh doesn't exist yet create it with the copy.
{
auto* pair_ptr = new std::pair<async::rw_spinlock, mesh>();
pair_ptr->second = data;
m_meshes.emplace(std::make_pair(newId, pair_ptr));
}
}
return { newId }; // Return a handle to the new mesh.
}
mesh_handle MeshCache::copy_mesh(id_type id, const std::string& newName)
{
OPTICK_EVENT();
id_type newId = nameHash(newName); // Get the new id.
{
async::readonly_guard guard(m_meshesLock);
mesh data = m_meshes[id]->second; // Get a copy of the original mesh.
if (m_meshes.count(newId))
{// If the new mesh already exists, overwrite it.
mesh& destination = m_meshes[newId]->second;
async::readwrite_guard rwguard(m_meshes[newId]->first);
destination = data;
}
else // If the new mesh doesn't exist yet create it with the copy.
{
auto* pair_ptr = new std::pair<async::rw_spinlock, mesh>();
pair_ptr->second = data;
m_meshes.emplace(std::make_pair(newId, pair_ptr));
}
}
return { newId }; // Return a handle to the new mesh.
}
mesh_handle MeshCache::get_handle(const std::string& name)
{
OPTICK_EVENT();
id_type id = nameHash(name);
async::readonly_guard guard(MeshCache::m_meshesLock);
if (MeshCache::m_meshes.count(id))
return { id };
return invalid_mesh_handle;
}
mesh_handle MeshCache::get_handle(id_type id)
{
OPTICK_EVENT();
async::readonly_guard guard(MeshCache::m_meshesLock);
if (MeshCache::m_meshes.count(id))
return { id };
return invalid_mesh_handle;
}
void MeshCache::destroy_mesh(id_type id)
{
bool erased = false;
{
async::readwrite_guard guard(MeshCache::m_meshesLock);
if (!m_meshes.count(id))
return;
erased = m_meshes.erase(id);
}
if (erased)
log::debug("Destroyed mesh {}", id);
}
}