libcarla/include/carla/geom/Mesh.cpp
2024-10-18 13:19:59 +08:00

392 lines
10 KiB
C++

// Copyright (c) 2020 Computer Vision Center (CVC) at the Universitat Autonoma
// de Barcelona (UAB).
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#include <carla/geom/Mesh.h>
#include <string>
#include <sstream>
#include <ios>
#include <iostream>
#include <fstream>
#include <carla/geom/Math.h>
namespace carla {
namespace geom {
bool Mesh::IsValid() const {
// should be at least some one vertex
if (_vertices.empty()) {
std::cout << "Mesh validation error: there are no vertices in the mesh." << std::endl;
return false;
}
// if there are indices, the amount must be multiple of 3
if (!_indexes.empty() && _indexes.size() % 3 != 0) {
std::cout << "Mesh validation error: the index amount must be multiple of 3." << std::endl;
return false;
}
if (!_materials.empty() && _materials.back().index_end == 0) {
std::cout << "Mesh validation error: last material was not closed." << std::endl;
return false;
}
return true;
}
// e.g:
// 1 3 5 7
// #---#---#---#
// | / | / | / |
// #---#---#---#
// 2 4 6 8
void Mesh::AddTriangleStrip(const std::vector<Mesh::vertex_type> &vertices) {
if (vertices.size() == 0) {
return;
}
DEBUG_ASSERT(vertices.size() >= 3);
size_t i = GetVerticesNum() + 2;
AddVertices(vertices);
bool index_clockwise = true;
while (i < GetVerticesNum()) {
index_clockwise = !index_clockwise;
if (index_clockwise) {
AddIndex(i + 1);
AddIndex(i);
AddIndex(i - 1);
} else {
AddIndex(i - 1);
AddIndex(i);
AddIndex(i + 1);
}
++i;
}
}
// e.g:
// 2 1 6
// #---#---#
// | / | \ |
// #---#---#
// 3 4 5
void Mesh::AddTriangleFan(const std::vector<Mesh::vertex_type> &vertices) {
DEBUG_ASSERT(vertices.size() >= 3);
const size_t initial_index = GetVerticesNum() + 1;
size_t i = GetVerticesNum() + 2;
AddVertices(vertices);
while (i < GetVerticesNum()) {
AddIndex(initial_index);
AddIndex(i);
AddIndex(i + 1);
++i;
}
}
void Mesh::AddVertex(vertex_type vertex) {
_vertices.push_back(vertex);
}
void Mesh::AddVertices(const std::vector<Mesh::vertex_type> &vertices) {
std::copy(vertices.begin(), vertices.end(), std::back_inserter(_vertices));
}
void Mesh::AddNormal(normal_type normal) {
_normals.push_back(normal);
}
void Mesh::AddIndex(index_type index) {
_indexes.push_back(index);
}
void Mesh::AddUV(uv_type uv) {
_uvs.push_back(uv);
}
void Mesh::AddUVs(const std::vector<uv_type> & uv) {
std::copy(uv.begin(), uv.end(), std::back_inserter(_uvs));
}
void Mesh::AddMaterial(const std::string &material_name) {
const size_t open_index = _indexes.size();
if (!_materials.empty()) {
if (_materials.back().index_end == 0) {
// @todo: change this comment to a debug warning
// std::cout << "last material was not closed, closing it..." << std::endl;
EndMaterial();
}
}
if (open_index % 3 != 0) {
std::cout << "open_index % 3 != 0" << std::endl;
return;
}
_materials.emplace_back(material_name, open_index, 0);
}
void Mesh::EndMaterial() {
const size_t close_index = _indexes.size();
if (_materials.empty() ||
_materials.back().index_start == close_index ||
_materials.back().index_end != 0) {
// @todo: change this comment to a debug warning
// std::cout << "WARNING: Bad end of material. Material not started." << std::endl;
return;
}
if (_indexes.empty() || close_index % 3 != 0) {
// @todo: change this comment to a debug warning
// std::cout << "WARNING: Bad end of material. Face not started/ended." << std::endl;
return;
}
_materials.back().index_end = close_index;
}
std::string Mesh::GenerateOBJ() const {
if (!IsValid()) {
return "";
}
std::stringstream out;
out << std::fixed; // Avoid using scientific notation
out << "# List of geometric vertices, with (x, y, z) coordinates." << std::endl;
for (auto &v : _vertices) {
out << "v " << v.x << " " << v.y << " " << v.z << std::endl;
}
if (!_uvs.empty()) {
out << std::endl << "# List of texture coordinates, in (u, v) coordinates, these will vary between 0 and 1." << std::endl;
for (auto &vt : _uvs) {
out << "vt " << vt.x << " " << vt.y << std::endl;
}
}
if (!_normals.empty()) {
out << std::endl << "# List of vertex normals in (x, y, z) form; normals might not be unit vectors." << std::endl;
for (auto &vn : _normals) {
out << "vn " << vn.x << " " << vn.y << " " << vn.z << std::endl;
}
}
if (!_indexes.empty()) {
out << std::endl << "# Polygonal face element." << std::endl;
auto it_m = _materials.begin();
auto it = _indexes.begin();
size_t index_counter = 0u;
while (it != _indexes.end()) {
// While exist materials
if (it_m != _materials.end()) {
// If the current material ends at this index
if (it_m->index_end == index_counter) {
++it_m;
}
// If the current material start at this index
if (it_m->index_start == index_counter) {
out << "\nusemtl " << it_m->name << std::endl;
}
}
// Add the actual face using the 3 consecutive indices
out << "f " << *it; ++it;
out << " " << *it; ++it;
out << " " << *it << std::endl; ++it;
index_counter += 3;
}
}
return out.str();
}
std::string Mesh::GenerateOBJForRecast() const {
if (!IsValid()) {
return "";
}
std::stringstream out;
out << std::fixed; // Avoid using scientific notation
out << "# List of geometric vertices, with (x, y, z) coordinates." << std::endl;
for (auto &v : _vertices) {
// Switched "y" and "z" for Recast library
out << "v " << v.x << " " << v.z << " " << v.y << std::endl;
}
if (!_indexes.empty()) {
out << std::endl << "# Polygonal face element." << std::endl;
auto it_m = _materials.begin();
auto it = _indexes.begin();
size_t index_counter = 0u;
while (it != _indexes.end()) {
// While exist materials
if (it_m != _materials.end()) {
// If the current material ends at this index
if (it_m->index_end == index_counter) {
++it_m;
}
// If the current material start at this index
if (it_m->index_start == index_counter) {
out << "\nusemtl " << it_m->name << std::endl;
}
}
// Add the actual face using the 3 consecutive indices
// Changes the face build direction to clockwise since
// the space has changed.
out << "f " << *it; ++it;
const auto i_2 = *it; ++it;
const auto i_3 = *it; ++it;
out << " " << i_3 << " " << i_2 << std::endl;
index_counter += 3;
}
}
return out.str();
}
std::string Mesh::GeneratePLY() const {
if (!IsValid()) {
return "Invalid Mesh";
}
// Generate header
std::stringstream out;
return out.str();
}
const std::vector<Mesh::vertex_type> &Mesh::GetVertices() const {
return _vertices;
}
std::vector<Mesh::vertex_type> &Mesh::GetVertices() {
return _vertices;
}
size_t Mesh::GetVerticesNum() const {
return _vertices.size();
}
const std::vector<Mesh::normal_type> &Mesh::GetNormals() const {
return _normals;
}
const std::vector<Mesh::index_type> &Mesh::GetIndexes() const {
return _indexes;
}
std::vector<Mesh::index_type>& Mesh::GetIndexes() {
return _indexes;
}
size_t Mesh::GetIndexesNum() const {
return _indexes.size();
}
const std::vector<Mesh::uv_type> &Mesh::GetUVs() const {
return _uvs;
}
const std::vector<Mesh::material_type> &Mesh::GetMaterials() const {
return _materials;
}
size_t Mesh::GetLastVertexIndex() const {
return _vertices.size();
}
Mesh& Mesh::ConcatMesh(const Mesh& rhs, int num_vertices_to_link) {
if (!rhs.IsValid()){
return *this += rhs;
}
const size_t v_num = GetVerticesNum();
const size_t i_num = GetIndexesNum();
_vertices.insert(
_vertices.end(),
rhs.GetVertices().begin(),
rhs.GetVertices().end());
_normals.insert(
_normals.end(),
rhs.GetNormals().begin(),
rhs.GetNormals().end());
const size_t vertex_to_start_concating = v_num - num_vertices_to_link;
for( size_t i = 1; i < num_vertices_to_link; ++i ) {
_indexes.push_back( vertex_to_start_concating + i );
_indexes.push_back( vertex_to_start_concating + i + 1 );
_indexes.push_back( v_num + i );
_indexes.push_back( vertex_to_start_concating + i + 1);
_indexes.push_back( v_num + i + 1);
_indexes.push_back( v_num + i);
}
std::transform(
rhs.GetIndexes().begin(),
rhs.GetIndexes().end(),
std::back_inserter(_indexes),
[=](size_t index) {return index + v_num; });
_uvs.insert(
_uvs.end(),
rhs.GetUVs().begin(),
rhs.GetUVs().end());
std::transform(
rhs.GetMaterials().begin(),
rhs.GetMaterials().end(),
std::back_inserter(_materials),
[=](MeshMaterial mat) {
mat.index_start += i_num;
mat.index_end += i_num;
return mat;
});
return *this;
}
Mesh &Mesh::operator+=(const Mesh &rhs) {
const size_t v_num = GetVerticesNum();
const size_t i_num = GetIndexesNum();
_vertices.insert(
_vertices.end(),
rhs.GetVertices().begin(),
rhs.GetVertices().end());
_normals.insert(
_normals.end(),
rhs.GetNormals().begin(),
rhs.GetNormals().end());
std::transform(
rhs.GetIndexes().begin(),
rhs.GetIndexes().end(),
std::back_inserter(_indexes),
[=](size_t index) {return index + v_num;});
_uvs.insert(
_uvs.end(),
rhs.GetUVs().begin(),
rhs.GetUVs().end());
std::transform(
rhs.GetMaterials().begin(),
rhs.GetMaterials().end(),
std::back_inserter(_materials),
[=](MeshMaterial mat) {
mat.index_start += i_num;
mat.index_end += i_num;
return mat;
});
return *this;
}
Mesh operator+(const Mesh &lhs, const Mesh &rhs) {
Mesh m = lhs;
return m += rhs;
}
} // namespace geom
} // namespace carla