blob: 9e64465b034efbd2a97e2afc67efe2579fa19a16 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_UI_LIB_ESCHER_MESH_INDEXED_TRIANGLE_MESH_H_
#define SRC_UI_LIB_ESCHER_MESH_INDEXED_TRIANGLE_MESH_H_
#include <vector>
#include "src/ui/lib/escher/geometry/bounding_box.h"
#include "src/ui/lib/escher/geometry/types.h"
#include "src/ui/lib/escher/shape/mesh_spec.h"
namespace escher {
// Simple representation of an indexed triangle mesh, used during geometric
// algorithms before uploading the mesh to the GPU. By separating positions
// from other attributes, it makes it easy to perform geometric operations such
// as splitting an edge where it intersects a plane then using the same
// interpolation parameter used to generate the new position to also interpolate
// the other attribute values.
template <typename PositionT, typename AttrT1 = nullptr_t, typename AttrT2 = nullptr_t,
typename AttrT3 = nullptr_t>
struct IndexedTriangleMesh {
using PositionType = PositionT;
using AttributeType1 = AttrT1;
using AttributeType2 = AttrT2;
using AttributeType3 = AttrT3;
using IndexType = MeshSpec::IndexType;
using EdgeType = std::pair<IndexType, IndexType>;
BoundingBox bounding_box;
std::vector<IndexType> indices;
std::vector<PositionType> positions;
std::vector<AttributeType1> attributes1;
std::vector<AttributeType2> attributes2;
std::vector<AttributeType3> attributes3;
void clear() {
indices.clear();
positions.clear();
attributes1.clear();
attributes2.clear();
attributes3.clear();
}
uint32_t index_count() const { return static_cast<uint32_t>(indices.size()); }
uint32_t vertex_count() const { return static_cast<uint32_t>(positions.size()); }
uint32_t triangle_count() const { return index_count() / 3; }
void resize_indices(uint32_t num_indices) {
FX_DCHECK(num_indices % 3 == 0);
indices.resize(num_indices);
}
void resize_vertices(uint32_t num_vertices) {
positions.resize(num_vertices);
if (!std::is_same<AttrT1, nullptr_t>::value) {
attributes1.resize(num_vertices);
}
if (!std::is_same<AttrT2, nullptr_t>::value) {
attributes2.resize(num_vertices);
}
if (!std::is_same<AttrT3, nullptr_t>::value) {
attributes3.resize(num_vertices);
}
}
// Return the total number of bytes used by vertex indices.
size_t total_index_bytes() const { return index_count() * sizeof(MeshSpec::IndexType); }
size_t sizeof_attribute1() const {
return std::is_same<AttrT1, nullptr_t>::value ? 0 : sizeof(AttrT1);
};
size_t sizeof_attribute2() const {
return std::is_same<AttrT2, nullptr_t>::value ? 0 : sizeof(AttrT2);
};
size_t sizeof_attribute3() const {
return std::is_same<AttrT3, nullptr_t>::value ? 0 : sizeof(AttrT3);
};
// Return the total number of bytes used by vertex position data.
size_t total_position_bytes() const { return vertex_count() * sizeof(PositionT); }
// Return the total number of bytes used by non-position vertex attributes.
size_t total_attribute1_bytes() const { return vertex_count() * sizeof_attribute1(); }
size_t total_attribute2_bytes() const { return vertex_count() * sizeof_attribute2(); }
size_t total_attribute3_bytes() const { return vertex_count() * sizeof_attribute3(); }
// Return the total number of bytes used by indices, positions, and other
// attributes.
size_t total_bytes() const {
return total_index_bytes() + total_position_bytes() + total_attribute1_bytes() +
total_attribute2_bytes() + total_attribute3_bytes();
}
// Return true if the mesh passes basic sanity checks, and false otherwise.
bool IsValid() const {
if (index_count() % 3 != 0) {
FX_LOGS(ERROR) << "index-count must be a multiple of 3: " << index_count();
return false;
}
for (auto i : indices) {
if (i >= vertex_count()) {
FX_LOGS(ERROR) << "index exceeds vertex-count: " << i << ", " << vertex_count();
return false;
}
}
if (std::is_same<AttrT1, nullptr_t>::value) {
if (attributes1.size() != 0) {
FX_LOGS(ERROR) << "count of null attribute1 must be zero: " << attributes1.size();
return false;
}
} else if (attributes1.size() != vertex_count()) {
FX_LOGS(ERROR) << "count of attribute1 must match vertex-count: " << attributes1.size()
<< ", " << vertex_count();
return false;
}
if (std::is_same<AttrT2, nullptr_t>::value) {
if (attributes2.size() != 0) {
FX_LOGS(ERROR) << "count of null attribute2 must be zero: " << attributes2.size();
return false;
}
} else if (attributes2.size() != vertex_count()) {
FX_LOGS(ERROR) << "count of attribute2 must match vertex-count: " << attributes2.size()
<< ", " << vertex_count();
return false;
}
if (std::is_same<AttrT3, nullptr_t>::value) {
if (attributes3.size() != 0) {
FX_LOGS(ERROR) << "count of null attribute3 must be zero: " << attributes3.size();
return false;
}
} else if (attributes3.size() != vertex_count()) {
FX_LOGS(ERROR) << "count of attribute3 must match vertex-count: " << attributes3.size()
<< ", " << vertex_count();
return false;
}
// Valid!
return true;
}
// Return true if meshes are identical. Will return false in all other cases,
// including e.g. when the meshes are the same but all triangle indices are
// rotated clockwise.
bool operator==(const IndexedTriangleMesh<PositionT, AttrT1>& other) const {
return indices == other.indices && positions == other.positions &&
attributes1 == other.attributes1 && attributes2 == other.attributes2 &&
attributes3 == other.attributes3;
}
};
template <typename AttrT1 = nullptr_t, typename AttrT2 = nullptr_t, typename AttrT3 = nullptr_t>
using IndexedTriangleMesh2d = IndexedTriangleMesh<vec2, AttrT1, AttrT2, AttrT3>;
template <typename AttrT1 = nullptr_t, typename AttrT2 = nullptr_t, typename AttrT3 = nullptr_t>
using IndexedTriangleMesh3d = IndexedTriangleMesh<vec3, AttrT1, AttrT2, AttrT3>;
// Print IndexedTriangleMesh on ostream.
template <typename AttrT>
void IndexedTriangleMeshPrintAttribute(std::ostream& str, const std::vector<AttrT>& attributes,
size_t index, const char* prefix) {
str << prefix << attributes[index];
}
template <>
inline void IndexedTriangleMeshPrintAttribute(std::ostream& str,
const std::vector<nullptr_t>& attributes,
size_t index, const char* prefix) {}
template <typename PositionT, typename AttrT1, typename AttrT2, typename AttrT3>
std::ostream& operator<<(std::ostream& str,
const IndexedTriangleMesh<PositionT, AttrT1, AttrT2, AttrT3>& mesh) {
str << "IndexedTriangleMesh[indices: " << mesh.index_count()
<< " vertices:" << mesh.vertex_count() << "\n";
for (size_t tri = 0; tri + 2 < mesh.index_count(); tri += 3) {
uint32_t ind0 = mesh.indices[tri];
uint32_t ind1 = mesh.indices[tri + 1];
uint32_t ind2 = mesh.indices[tri + 2];
str << "tri " << tri / 3 << ": " << ind0 << "," << ind1 << "," << ind2 << " "
<< mesh.positions[ind0] << "," << mesh.positions[ind1] << "," << mesh.positions[ind2]
<< "\n";
}
for (size_t i = 0; i < mesh.vertex_count(); ++i) {
str << "vert " << i << " pos: " << mesh.positions[i];
IndexedTriangleMeshPrintAttribute(str, mesh.attributes1, i, " attr1: ");
IndexedTriangleMeshPrintAttribute(str, mesh.attributes2, i, " attr2: ");
IndexedTriangleMeshPrintAttribute(str, mesh.attributes3, i, " attr3: ");
str << "\n";
}
return str << "]";
}
} // namespace escher
#endif // SRC_UI_LIB_ESCHER_MESH_INDEXED_TRIANGLE_MESH_H_