@ -0,0 +1,395 @@ | |||
/* | |||
* Copyright (C) 2023 Christopher J. Howard | |||
* | |||
* This file is part of Antkeeper source code. | |||
* | |||
* Antkeeper source code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* Antkeeper source code is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#ifndef ANTKEEPER_GEOM_BREP_ATTRIBUTE_MAP_HPP | |||
#define ANTKEEPER_GEOM_BREP_ATTRIBUTE_MAP_HPP | |||
#include <engine/geom/brep/brep-attribute.hpp> | |||
#include <engine/utility/hash/fnv1a.hpp> | |||
#include <iterator> | |||
#include <memory> | |||
#include <stdexcept> | |||
#include <type_traits> | |||
#include <unordered_map> | |||
#include <utility> | |||
namespace geom { | |||
/** | |||
* Maps names to B-rep attributes. | |||
*/ | |||
class brep_attribute_map | |||
{ | |||
public: | |||
template <class Iter, bool Const> | |||
class iterator_template | |||
{ | |||
public: | |||
using iterator_type = Iter; | |||
using iterator_category = std::bidirectional_iterator_tag; | |||
using iterator_concept = std::bidirectional_iterator_tag; | |||
using difference_type = std::iter_difference_t<Iter>; | |||
using value_type = brep_attribute_base; | |||
using pointer = std::conditional<Const, const value_type*, value_type*>::type; | |||
using reference = std::conditional<Const, const value_type&, value_type&>::type; | |||
[[nodiscard]] inline constexpr reference operator*() const noexcept | |||
{ | |||
return *m_it->second; | |||
} | |||
[[nodiscard]] inline constexpr pointer operator->() const noexcept | |||
{ | |||
return &(*m_it->second); | |||
} | |||
[[nodiscard]] inline constexpr reference operator[](difference_type i) const noexcept | |||
{ | |||
return *(m_it[i]); | |||
} | |||
inline iterator_template& operator++() noexcept | |||
{ | |||
++m_it; | |||
return *this; | |||
} | |||
[[nodiscard]] inline iterator_template operator++(int) noexcept | |||
{ | |||
iterator_template tmp = *this; | |||
++(*this); | |||
return tmp; | |||
} | |||
inline iterator_template& operator--() noexcept | |||
{ | |||
--m_it; | |||
return *this; | |||
} | |||
[[nodiscard]] inline iterator_template operator--(int) noexcept | |||
{ | |||
iterator_template tmp = *this; | |||
--(*this); | |||
return tmp; | |||
} | |||
inline iterator_template& operator+=(difference_type n) noexcept | |||
{ | |||
m_it += n; | |||
return *this; | |||
} | |||
inline iterator_template& operator-=(difference_type n) noexcept | |||
{ | |||
m_it -= n; | |||
return *this; | |||
} | |||
[[nodiscard]] inline bool operator==(const iterator_template& other) const noexcept | |||
{ | |||
return m_it == other.m_it; | |||
}; | |||
[[nodiscard]] inline std::weak_ordering operator<=>(const iterator_template& other) const noexcept | |||
{ | |||
return m_it <=> other.m_it; | |||
} | |||
[[nodiscard]] inline difference_type operator-(const iterator_template& rhs) const noexcept | |||
{ | |||
return m_it - rhs.m_it; | |||
} | |||
[[nodiscard]] inline iterator_template operator+(difference_type n) const noexcept | |||
{ | |||
return iterator_template{m_it + n}; | |||
} | |||
[[nodiscard]] inline iterator_template operator-(difference_type n) const noexcept | |||
{ | |||
return iterator_template{m_it - n}; | |||
} | |||
[[nodiscard]] friend iterator_template operator+(difference_type lhs, const iterator_template& rhs) noexcept | |||
{ | |||
return iterator_template{lhs + rhs.m_it}; | |||
} | |||
[[nodiscard]] friend iterator_template operator-(difference_type lhs, const iterator_template& rhs) noexcept | |||
{ | |||
return iterator_template{lhs - rhs.m_it}; | |||
} | |||
private: | |||
friend class brep_attribute_map; | |||
iterator_type m_it; | |||
}; | |||
using iterator = iterator_template<std::unordered_map<hash::fnv1a32_t, std::unique_ptr<brep_attribute_base>>::iterator, false>; | |||
using const_iterator = iterator_template<std::unordered_map<hash::fnv1a32_t, std::unique_ptr<brep_attribute_base>>::const_iterator, true>; | |||
/// @name Iterators | |||
/// @{ | |||
/// Returns an iterator to the first attribute. | |||
/// @{ | |||
[[nodiscard]] inline const_iterator begin() const noexcept | |||
{ | |||
const_iterator it; | |||
it.m_it = m_attributes.begin(); | |||
return it; | |||
} | |||
[[nodiscard]] inline iterator begin() noexcept | |||
{ | |||
iterator it; | |||
it.m_it = m_attributes.begin(); | |||
return it; | |||
} | |||
[[nodiscard]] inline const_iterator cbegin() const noexcept | |||
{ | |||
return begin(); | |||
} | |||
/// @} | |||
/// Returns an iterator to the attribute following the last attribute. | |||
/// @{ | |||
[[nodiscard]] inline const_iterator end() const noexcept | |||
{ | |||
const_iterator it; | |||
it.m_it = m_attributes.end(); | |||
return it; | |||
} | |||
[[nodiscard]] inline iterator end() noexcept | |||
{ | |||
iterator it; | |||
it.m_it = m_attributes.end(); | |||
return it; | |||
} | |||
[[nodiscard]] inline const_iterator cend() const noexcept | |||
{ | |||
return end(); | |||
} | |||
/// @} | |||
/// @} | |||
/// @name Capacity | |||
/// @{ | |||
/// Returns `true` if the container is empty, `false` otherwise. | |||
[[nodiscard]] inline bool empty() const noexcept | |||
{ | |||
return m_attributes.empty(); | |||
} | |||
/// Returns the number of attributes in the container. | |||
[[nodiscard]] inline std::size_t size() const noexcept | |||
{ | |||
return m_attributes.size(); | |||
} | |||
/// @} | |||
/// @name Modifiers | |||
/// @{ | |||
/** | |||
* Removes all attributes from the container. | |||
*/ | |||
inline void clear() noexcept | |||
{ | |||
m_attributes.clear(); | |||
} | |||
/** | |||
* Constructs a new attribute. If an attribute with the given name exists, it will be replaced. | |||
* | |||
* @tparam T Attribute data type. | |||
* | |||
* @param name Name of the new attribute. | |||
* | |||
* @return Iterator to the new attribute. | |||
*/ | |||
template <class T> | |||
iterator emplace(hash::fnv1a32_t name) | |||
{ | |||
if (auto i = m_attributes.find(name); i != m_attributes.end()) | |||
{ | |||
i->second.reset(); | |||
i->second = std::make_unique<brep_attribute<T>>(name, m_element_count); | |||
iterator it; | |||
it.m_it = i; | |||
return it; | |||
} | |||
iterator it; | |||
it.m_it = m_attributes.emplace(name, std::make_unique<brep_attribute<T>>(name, m_element_count)).first; | |||
return it; | |||
} | |||
/** | |||
* Removes an attribute from the container. | |||
* | |||
* @param pos Iterator to the attribute to remove. | |||
* | |||
* @return Iterator following the erased attribute. | |||
*/ | |||
inline iterator erase(iterator pos) | |||
{ | |||
iterator it; | |||
it.m_it = m_attributes.erase(pos.m_it); | |||
return it; | |||
} | |||
/** | |||
* Removes an attribute from the container. | |||
* | |||
* @param name Name of the attribute to remove. | |||
* | |||
* @return Number of attributes removed (0 or 1). | |||
*/ | |||
inline std::size_t erase(hash::fnv1a32_t name) | |||
{ | |||
return m_attributes.erase(name); | |||
} | |||
/** | |||
* Constructs a new attribute if an attribute with the given name does not exist. | |||
* | |||
* @tparam T Attribute data type. | |||
* | |||
* @param name Name of the new attribute. | |||
* | |||
* @return Pair consisting of an iterator to the new or pre-existing attribute, and a Boolean value that's `true` if the new attribute was constructed, or `false` if an attribute with the given name pre-existed. | |||
*/ | |||
template <class T> | |||
std::pair<iterator, bool> try_emplace(hash::fnv1a32_t name) | |||
{ | |||
if (auto i = m_attributes.find(name); i != m_attributes.end()) | |||
{ | |||
iterator it; | |||
it.m_it = i; | |||
return {it, false}; | |||
} | |||
auto pair = m_attributes.emplace(name, std::make_unique<brep_attribute<T>>(name, m_element_count)); | |||
iterator it; | |||
it.m_it = pair.first; | |||
return {it, pair.second}; | |||
} | |||
/// @} | |||
/// @name Lookup | |||
/// @{ | |||
/** | |||
* Returns a reference to the attribute with the given name. If no such attribute exists, an exception of type std::out_of_range is thrown. | |||
* | |||
* @tparam T Attribute data type. | |||
* | |||
* @return Reference to the attribute with the given name. | |||
* | |||
* @except std::out_of_range B-rep attribute not found. | |||
*/ | |||
/// @{ | |||
template <class T> | |||
[[nodiscard]] const brep_attribute<T>& at(hash::fnv1a32_t name) const | |||
{ | |||
auto it = find(name); | |||
if (it == end()) | |||
{ | |||
throw std::out_of_range("B-rep attribute not found"); | |||
} | |||
return static_cast<const brep_attribute<T>&>(*it); | |||
} | |||
template <class T> | |||
[[nodiscard]] brep_attribute<T>& at(hash::fnv1a32_t name) | |||
{ | |||
auto it = find(name); | |||
if (it == end()) | |||
{ | |||
throw std::out_of_range("B-rep attribute not found"); | |||
} | |||
return static_cast<brep_attribute<T>&>(*it); | |||
} | |||
/// @} | |||
/** | |||
* Finds an attribute with the given name. | |||
* | |||
* @param name Name of an attribute. | |||
* | |||
* @return Iterator to the attribute with the given name. If no such attribute is found, an end iterator is returned. | |||
*/ | |||
/// @{ | |||
[[nodiscard]] inline const_iterator find(hash::fnv1a32_t name) const | |||
{ | |||
const_iterator it; | |||
it.m_it = m_attributes.find(name); | |||
return it; | |||
} | |||
[[nodiscard]] inline iterator find(hash::fnv1a32_t name) | |||
{ | |||
iterator it; | |||
it.m_it = m_attributes.find(name); | |||
return it; | |||
} | |||
/// @} | |||
/** | |||
* Checks if there is an attribute with a given name in the container. | |||
* | |||
* @param name Attribute name. | |||
* | |||
* @return `true` if an attribute with the given name was found, `false` otherwise. | |||
*/ | |||
[[nodiscard]] inline bool contains(hash::fnv1a32_t name) const | |||
{ | |||
return m_attributes.contains(name); | |||
} | |||
/// @} | |||
private: | |||
template <class T> | |||
friend class brep_element_container; | |||
friend class brep_mesh; | |||
brep_attribute_map& operator=(const brep_attribute_map& other) | |||
{ | |||
m_element_count = other.m_element_count; | |||
m_attributes.clear(); | |||
for (const auto& [key, value]: other.m_attributes) | |||
{ | |||
m_attributes.emplace(key, value->clone()); | |||
} | |||
return *this; | |||
} | |||
std::size_t m_element_count{}; | |||
std::unordered_map<hash::fnv1a32_t, std::unique_ptr<brep_attribute_base>> m_attributes; | |||
}; | |||
} // namespace geom | |||
#endif // ANTKEEPER_GEOM_BREP_ATTRIBUTE_MAP_HPP |
@ -0,0 +1,266 @@ | |||
/* | |||
* Copyright (C) 2023 Christopher J. Howard | |||
* | |||
* This file is part of Antkeeper source code. | |||
* | |||
* Antkeeper source code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* Antkeeper source code is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#ifndef ANTKEEPER_GEOM_BREP_ATTRIBUTE_HPP | |||
#define ANTKEEPER_GEOM_BREP_ATTRIBUTE_HPP | |||
#include <engine/utility/hash/fnv1a.hpp> | |||
#include <memory> | |||
#include <vector> | |||
namespace geom { | |||
/** | |||
* Abstract base class for B-rep element attributes. | |||
*/ | |||
class brep_attribute_base | |||
{ | |||
public: | |||
/// Returns the name of the attribute. | |||
[[nodiscard]] inline constexpr hash::fnv1a32_t name() const noexcept | |||
{ | |||
return m_name; | |||
} | |||
protected: | |||
inline explicit constexpr brep_attribute_base(hash::fnv1a32_t name) noexcept: | |||
m_name(name) | |||
{} | |||
private: | |||
friend class brep_attribute_map; | |||
template <class T> | |||
friend class brep_element_container; | |||
/** | |||
* Erases the attribute value of an element with the given index. | |||
* | |||
* @param i Index of an element. | |||
*/ | |||
virtual void erase(std::size_t i) = 0; | |||
/** | |||
* Appends a new attribute value to the end of the container. | |||
*/ | |||
virtual void emplace_back() = 0; | |||
/// Returns a copy of this attribute. | |||
[[nodiscard]] virtual std::unique_ptr<brep_attribute_base> clone() const = 0; | |||
hash::fnv1a32_t m_name; | |||
}; | |||
/** | |||
* Per-element B-rep data. | |||
* | |||
* @tparam T Data type. | |||
*/ | |||
template <class T> | |||
class brep_attribute: public brep_attribute_base | |||
{ | |||
public: | |||
using value_type = T; | |||
using reference = value_type&; | |||
using const_reference = const value_type&; | |||
using pointer = value_type*; | |||
using const_pointer = const value_type*; | |||
using iterator = std::vector<value_type>::iterator; | |||
using const_iterator = std::vector<value_type>::const_iterator; | |||
using reverse_iterator = std::vector<value_type>::reverse_iterator; | |||
using const_reverse_iterator = std::vector<value_type>::const_reverse_iterator; | |||
/** | |||
* Constructs an attribute. | |||
* | |||
* @param name Name of the attribute. | |||
* @param element_count Number of elements. | |||
*/ | |||
brep_attribute(hash::fnv1a32_t name, std::size_t element_count): | |||
brep_attribute_base(name), | |||
m_values(element_count) | |||
{} | |||
/// @name Attribute access | |||
/// @{ | |||
/** | |||
* Returns a reference to the attribute value of an element. | |||
* | |||
* @param i Index of an element. | |||
* | |||
* @return Reference to the attribute value of the element at index @p i. | |||
*/ | |||
/// @{ | |||
[[nodiscard]] inline constexpr const_reference operator[](std::size_t i) const | |||
{ | |||
return m_values[i]; | |||
} | |||
[[nodiscard]] inline constexpr reference operator[](std::size_t i) | |||
{ | |||
return m_values[i]; | |||
} | |||
/// @} | |||
/// Returns a reference to the attribute value of the first element. | |||
/// @{ | |||
[[nodiscard]] inline constexpr const_reference front() const | |||
{ | |||
return m_values.front(); | |||
} | |||
[[nodiscard]] inline constexpr reference front() | |||
{ | |||
return m_values.front(); | |||
} | |||
/// @} | |||
/// Returns a reference to the attribute value of the last element. | |||
/// @{ | |||
[[nodiscard]] inline constexpr const_reference back() const | |||
{ | |||
return m_values.back(); | |||
} | |||
[[nodiscard]] inline constexpr reference back() | |||
{ | |||
return m_values.back(); | |||
} | |||
/// @} | |||
/// Returns a pointer to the underlying array serving as attribute value storage. | |||
/// @{ | |||
[[nodiscard]] inline constexpr const value_type* data() const noexcept | |||
{ | |||
return m_values.data(); | |||
} | |||
[[nodiscard]] inline constexpr value_type* data() noexcept | |||
{ | |||
return m_values.data(); | |||
} | |||
/// @} | |||
/// @} | |||
/// @name Iterators | |||
/// @{ | |||
/// Returns an iterator to the attribute value of the first element. | |||
/// @{ | |||
[[nodiscard]] inline constexpr const_iterator begin() const noexcept | |||
{ | |||
return m_values.begin(); | |||
} | |||
[[nodiscard]] inline constexpr iterator begin() noexcept | |||
{ | |||
return m_values.begin(); | |||
} | |||
[[nodiscard]] inline constexpr const_iterator cbegin() const noexcept | |||
{ | |||
return m_values.begin(); | |||
} | |||
/// @} | |||
/// Returns an iterator to the attribute value of the element following the last element. | |||
/// @{ | |||
[[nodiscard]] inline constexpr const_iterator end() const noexcept | |||
{ | |||
return m_values.end(); | |||
} | |||
[[nodiscard]] inline constexpr iterator end() noexcept | |||
{ | |||
return m_values.end(); | |||
} | |||
[[nodiscard]] inline constexpr const_iterator cend() const noexcept | |||
{ | |||
return m_values.end(); | |||
} | |||
/// @} | |||
/// Returns a reverse iterator to the attribute value of the first element of the reversed container. | |||
/// @{ | |||
[[nodiscard]] inline constexpr const_reverse_iterator rbegin() const noexcept | |||
{ | |||
return m_values.rbegin(); | |||
} | |||
[[nodiscard]] inline constexpr reverse_iterator rbegin() noexcept | |||
{ | |||
return m_values.rbegin(); | |||
} | |||
[[nodiscard]] inline constexpr const_reverse_iterator crbegin() const noexcept | |||
{ | |||
return m_values.rbegin(); | |||
} | |||
/// @} | |||
/// Returns a reverse iterator to the attribute value of the element following the last element of the reversed container. | |||
/// @{ | |||
[[nodiscard]] inline constexpr const_reverse_iterator rend() const noexcept | |||
{ | |||
return m_values.rend(); | |||
} | |||
[[nodiscard]] inline constexpr reverse_iterator rend() noexcept | |||
{ | |||
return m_values.rend(); | |||
} | |||
[[nodiscard]] inline constexpr const_reverse_iterator crend() const noexcept | |||
{ | |||
return m_values.rend(); | |||
} | |||
/// @} | |||
/// @} | |||
/// @name Capacity | |||
/// @{ | |||
/// Returns `true` if the container is empty, `false` otherwise. | |||
[[nodiscard]] inline constexpr bool empty() const noexcept | |||
{ | |||
return m_values.empty(); | |||
} | |||
/// Returns the number of attribute values in the container. | |||
[[nodiscard]] inline constexpr std::size_t size() const noexcept | |||
{ | |||
return m_values.size(); | |||
} | |||
/// @} | |||
private: | |||
void erase(std::size_t i) override | |||
{ | |||
m_values[i] = std::move(m_values.back()); | |||
m_values.pop_back(); | |||
} | |||
void emplace_back() override | |||
{ | |||
m_values.emplace_back(); | |||
} | |||
[[nodiscard]] std::unique_ptr<brep_attribute_base> clone() const override | |||
{ | |||
auto copy = std::make_unique<brep_attribute<T>>(name(), 0); | |||
copy->m_values = m_values; | |||
return std::move(copy); | |||
} | |||
std::vector<value_type> m_values; | |||
}; | |||
} // namespace geom | |||
#endif // ANTKEEPER_GEOM_BREP_ATTRIBUTE_HPP |
@ -0,0 +1,132 @@ | |||
/* | |||
* Copyright (C) 2023 Christopher J. Howard | |||
* | |||
* This file is part of Antkeeper source code. | |||
* | |||
* Antkeeper source code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* Antkeeper source code is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#include <engine/geom/brep/brep-edge.hpp> | |||
#include <engine/geom/brep/brep-mesh.hpp> | |||
#include <algorithm> | |||
namespace geom { | |||
void brep_edge_loop_list::push_back(brep_loop* loop) | |||
{ | |||
if (empty()) | |||
{ | |||
// List empty, initialize | |||
m_head = loop; | |||
loop->m_edge_next = loop; | |||
loop->m_edge_previous = loop; | |||
} | |||
else | |||
{ | |||
// Append loop | |||
loop->m_edge_next = m_head; | |||
loop->m_edge_previous = m_head->m_edge_previous; | |||
m_head->m_edge_previous->m_edge_next = loop; | |||
m_head->m_edge_previous = loop; | |||
} | |||
++m_size; | |||
} | |||
void brep_edge_loop_list::remove(brep_loop* loop) | |||
{ | |||
// Directly link next and previous loops | |||
loop->m_edge_next->m_edge_previous = loop->m_edge_previous; | |||
loop->m_edge_previous->m_edge_next = loop->m_edge_next; | |||
// If loop was the list head, update head | |||
if (m_head == loop) | |||
{ | |||
m_head = loop->m_edge_next; | |||
} | |||
--m_size; | |||
} | |||
brep_edge* brep_edge_container::emplace_back(brep_vertex* a, brep_vertex* b) | |||
{ | |||
if (a == b) | |||
{ | |||
return nullptr; | |||
} | |||
brep_edge* ab = brep_element_container<brep_edge>::emplace_back(); | |||
ab->m_index = size() - 1; | |||
ab->m_vertices[0] = a; | |||
ab->m_vertices[1] = b; | |||
// Append edge AB to the edge lists of vertices A and B | |||
a->m_edges.push_back(ab); | |||
b->m_edges.push_back(ab); | |||
return ab; | |||
}; | |||
void brep_edge_container::erase(brep_edge* edge) | |||
{ | |||
// Kill all loops and faces bounded by this edge | |||
while (!edge->loops().empty()) | |||
{ | |||
m_mesh->faces().erase(edge->loops().back()->face()); | |||
} | |||
// Remove this edge from its vertices' lists of edges | |||
edge->vertices().front()->m_edges.remove(edge); | |||
edge->vertices().back()->m_edges.remove(edge); | |||
// Erase edge | |||
brep_element_container<brep_edge>::erase(edge); | |||
} | |||
void brep_edge_container::clear() noexcept | |||
{ | |||
while (!empty()) | |||
{ | |||
erase(back()); | |||
} | |||
} | |||
brep_edge* brep_edge_container::find(brep_vertex* a, brep_vertex* b) const | |||
{ | |||
if (!a->edges().empty() && !b->edges().empty() && a != b) | |||
{ | |||
brep_edge* ea = a->edges().front(); | |||
brep_edge* eb = b->edges().front(); | |||
const std::size_t n = std::min<std::size_t>(a->edges().size(), b->edges().size()); | |||
for (std::size_t i = 0; i < n; ++i) | |||
{ | |||
if (ea->vertices()[1] == b || ea->vertices()[0] == b) | |||
{ | |||
return ea; | |||
} | |||
if (eb->vertices()[1] == a || eb->vertices()[0] == a) | |||
{ | |||
return eb; | |||
} | |||
ea = ea->m_vertex_next[ea->vertices()[1] == a]; | |||
eb = eb->m_vertex_next[eb->vertices()[1] == b]; | |||
} | |||
} | |||
return nullptr; | |||
} | |||
} // namespace geom |
@ -0,0 +1,330 @@ | |||
/* | |||
* Copyright (C) 2023 Christopher J. Howard | |||
* | |||
* This file is part of Antkeeper source code. | |||
* | |||
* Antkeeper source code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* Antkeeper source code is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#ifndef ANTKEEPER_GEOM_BREP_EDGE_HPP | |||
#define ANTKEEPER_GEOM_BREP_EDGE_HPP | |||
#include <engine/geom/brep/brep-loop.hpp> | |||
#include <engine/geom/brep/brep-element-container.hpp> | |||
#include <array> | |||
#include <cstddef> | |||
#include <iterator> | |||
namespace geom { | |||
class brep_vertex; | |||
class brep_loop; | |||
template <class T> | |||
class brep_element_container; | |||
/** | |||
* List of B-rep loops that share a common edge. | |||
*/ | |||
class brep_edge_loop_list | |||
{ | |||
public: | |||
friend class brep_mesh; | |||
friend class brep_face_container; | |||
struct const_iterator | |||
{ | |||
public: | |||
friend class brep_edge_loop_list; | |||
using iterator_category = std::bidirectional_iterator_tag; | |||
using iterator_concept = std::bidirectional_iterator_tag; | |||
using difference_type = std::ptrdiff_t; | |||
using value_type = brep_loop*; | |||
using pointer = const value_type*; | |||
using reference = const value_type&; | |||
[[nodiscard]] inline constexpr value_type operator*() const noexcept | |||
{ | |||
return m_loop; | |||
} | |||
[[nodiscard]] inline constexpr value_type operator->() const noexcept | |||
{ | |||
return m_loop; | |||
} | |||
inline const_iterator& operator++() noexcept | |||
{ | |||
m_loop = m_loop->m_edge_next; | |||
++m_position; | |||
return *this; | |||
} | |||
[[nodiscard]] inline const_iterator operator++(int) noexcept | |||
{ | |||
const_iterator tmp = *this; | |||
++(*this); | |||
return tmp; | |||
} | |||
inline const_iterator& operator--() noexcept | |||
{ | |||
m_loop = m_loop->m_edge_previous; | |||
--m_position; | |||
return *this; | |||
} | |||
[[nodiscard]] inline const_iterator operator--(int) noexcept | |||
{ | |||
const_iterator tmp = *this; | |||
--(*this); | |||
return tmp; | |||
} | |||
[[nodiscard]] inline bool operator==(const const_iterator& other) const noexcept | |||
{ | |||
return m_position == other.m_position; | |||
}; | |||
[[nodiscard]] inline std::weak_ordering operator<=>(const const_iterator& other) const noexcept | |||
{ | |||
return m_position <=> other.m_position; | |||
} | |||
[[nodiscard]] inline difference_type operator-(const const_iterator& rhs) const noexcept | |||
{ | |||
return m_position - rhs.m_position; | |||
} | |||
private: | |||
brep_loop* m_loop; | |||
std::ptrdiff_t m_position; | |||
}; | |||
using const_reverse_iterator = std::reverse_iterator<const_iterator>; | |||
/// @name Element access | |||
/// @{ | |||
/// Returns the first loop. | |||
[[nodiscard]] inline brep_loop* front() const noexcept | |||
{ | |||
return m_head; | |||
} | |||
/// Returns the last loop. | |||
[[nodiscard]] inline brep_loop* back() const noexcept | |||
{ | |||
return m_head->m_edge_previous; | |||
} | |||
/// @} | |||
/// @name Iterators | |||
/// @{ | |||
/// Returns an iterator to the first loop. | |||
/// @{ | |||
[[nodiscard]] inline constexpr const_iterator begin() const noexcept | |||
{ | |||
const_iterator it; | |||
it.m_loop = m_head; | |||
it.m_position = 0; | |||
return it; | |||
} | |||
[[nodiscard]] inline constexpr const_iterator cbegin() const noexcept | |||
{ | |||
return begin(); | |||
} | |||
/// @} | |||
/// Returns an iterator to the loop following the last loop. | |||
/// @{ | |||
[[nodiscard]] inline constexpr const_iterator end() const noexcept | |||
{ | |||
const_iterator it; | |||
it.m_loop = m_head; | |||
it.m_position = static_cast<std::ptrdiff_t>(m_size); | |||
return it; | |||
} | |||
[[nodiscard]] inline constexpr const_iterator cend() const noexcept | |||
{ | |||
return end(); | |||
} | |||
/// @} | |||
/// Returns a reverse iterator to the first loop of the reversed list. | |||
/// @{ | |||
[[nodiscard]] inline constexpr const_reverse_iterator rbegin() const noexcept | |||
{ | |||
return std::make_reverse_iterator(end()); | |||
} | |||
[[nodiscard]] inline constexpr const_reverse_iterator crbegin() const noexcept | |||
{ | |||
return rbegin(); | |||
} | |||
/// @} | |||
/// Returns a reverse iterator to the loop following the last loop of the reversed list. | |||
/// @{ | |||
[[nodiscard]] inline constexpr const_reverse_iterator rend() const noexcept | |||
{ | |||
return std::make_reverse_iterator(begin()); | |||
} | |||
[[nodiscard]] inline constexpr const_reverse_iterator crend() const noexcept | |||
{ | |||
return rend(); | |||
} | |||
/// @} | |||
/// @} | |||
/// @name Capacity | |||
/// @{ | |||
/// Returns `true` if the list is empty, `false` otherwise. | |||
[[nodiscard]] inline constexpr bool empty() const noexcept | |||
{ | |||
return !m_size; | |||
} | |||
/// Returns the number of loops in the list. | |||
[[nodiscard]] inline constexpr std::size_t size() const noexcept | |||
{ | |||
return m_size; | |||
} | |||
/// @} | |||
/// @name Modifiers | |||
/// @{ | |||
/** | |||
* Appends a loop to the end of the list. | |||
* | |||
* @param loop Pointer to the loop to append. | |||
*/ | |||
void push_back(brep_loop* loop); | |||
/** | |||
* Removes an loop from the list. | |||
* | |||
* @param loop Pointer to the loop to remove. | |||
*/ | |||
void remove(brep_loop* loop); | |||
/// @} | |||
private: | |||
brep_loop* m_head{}; | |||
std::size_t m_size{}; | |||
}; | |||
/** | |||
* Curve segment bounded by two vertices. | |||
*/ | |||
class brep_edge | |||
{ | |||
public: | |||
friend class brep_mesh; | |||
friend class brep_vertex_edge_list; | |||
friend class brep_element_container<brep_edge>; | |||
friend class brep_edge_container; | |||
friend class brep_face_container; | |||
/** | |||
* Returns the index of this edge in the mesh edge array. | |||
* | |||
* @warning This index may change if any edges are removed from the mesh. | |||
*/ | |||
[[nodiscard]] inline constexpr std::size_t index() const noexcept | |||
{ | |||
return m_index; | |||
} | |||
/// Returns the pair of vertices that bound this edge. | |||
[[nodiscard]] inline constexpr const std::array<brep_vertex*, 2>& vertices() const noexcept | |||
{ | |||
return m_vertices; | |||
} | |||
/// Returns the list of loops that share this edge. | |||
[[nodiscard]] inline constexpr const brep_edge_loop_list& loops() const noexcept | |||
{ | |||
return m_loops; | |||
} | |||
private: | |||
std::size_t m_index; | |||
std::array<brep_vertex*, 2> m_vertices; | |||
std::array<brep_edge*, 2> m_vertex_next; | |||
std::array<brep_edge*, 2> m_vertex_previous; | |||
brep_edge_loop_list m_loops; | |||
}; | |||
/** | |||
* B-rep edge container. | |||
*/ | |||
class brep_edge_container: public brep_element_container<brep_edge> | |||
{ | |||
public: | |||
/** | |||
* Appends a new edge to the end of the container. | |||
* | |||
* @param a First bounding vertex of the edge. | |||
* @param b Second bounding vertex of the edge. | |||
* | |||
* @return Pointer to the new edge. | |||
*/ | |||
brep_edge* emplace_back(brep_vertex* a, brep_vertex* b); | |||
/** | |||
* Erases an edge and all dependent loops and faces. | |||
* | |||
* @param edge Pointer to the edge to erase. | |||
* | |||
* @warning Invalidates iterators and indices of edges, loops, and faces. | |||
*/ | |||
void erase(brep_edge* edge) override; | |||
/** | |||
* Erases all edges and their dependent loops and faces. | |||
*/ | |||
void clear() noexcept; | |||
/** | |||
* Finds an edge bounded by vertices @p a and @p b (in any order). | |||
* | |||
* @param a First (or second) bounding vertex of the edge. | |||
* @param b Second (or first) bounding vertex of the edge. | |||
* | |||
* @return Pointer to an edge bounded by vertices @p a and @p b, or `nullptr` if no such edge was found. | |||
*/ | |||
[[nodiscard]] brep_edge* find(brep_vertex* a, brep_vertex* b) const; | |||
private: | |||
friend class brep_mesh; | |||
/** | |||
* Constructs a B-rep edge container. | |||
* | |||
* @param mesh Pointer to the parent mesh. | |||
*/ | |||
inline explicit brep_edge_container(brep_mesh* mesh) noexcept: | |||
brep_element_container<brep_edge>(mesh) | |||
{} | |||
}; | |||
} // namespace geom | |||
#endif // ANTKEEPER_GEOM_BREP_EDGE_HPP |
@ -0,0 +1,329 @@ | |||
/* | |||
* Copyright (C) 2023 Christopher J. Howard | |||
* | |||
* This file is part of Antkeeper source code. | |||
* | |||
* Antkeeper source code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* Antkeeper source code is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#ifndef ANTKEEPER_GEOM_BREP_ELEMENT_CONTAINER_HPP | |||
#define ANTKEEPER_GEOM_BREP_ELEMENT_CONTAINER_HPP | |||
#include <engine/geom/brep/brep-attribute-map.hpp> | |||
#include <engine/utility/hash/fnv1a.hpp> | |||
#include <cstddef> | |||
#include <iterator> | |||
#include <memory> | |||
#include <vector> | |||
namespace geom { | |||
/** | |||
* Container for B-rep elements. | |||
* | |||
* @tparam T Element type. | |||
*/ | |||
template <class T> | |||
class brep_element_container | |||
{ | |||
public: | |||
friend class brep_mesh; | |||
using element_type = T; | |||
struct const_iterator | |||
{ | |||
public: | |||
using iterator_category = std::random_access_iterator_tag; | |||
using iterator_concept = std::random_access_iterator_tag; | |||
using difference_type = std::ptrdiff_t; | |||
using value_type = element_type*; | |||
using pointer = const value_type*; | |||
using reference = const value_type&; | |||
[[nodiscard]] inline constexpr value_type operator*() const noexcept | |||
{ | |||
return m_it->get(); | |||
} | |||
[[nodiscard]] inline constexpr value_type operator->() const noexcept | |||
{ | |||
return m_it->get(); | |||
} | |||
[[nodiscard]] inline constexpr value_type operator[](difference_type i) const noexcept | |||
{ | |||
return m_it[i]->get(); | |||
} | |||
inline const_iterator& operator++() noexcept | |||
{ | |||
++m_it; | |||
return *this; | |||
} | |||
[[nodiscard]] inline const_iterator operator++(int) noexcept | |||
{ | |||
const_iterator tmp = *this; | |||
++(*this); | |||
return tmp; | |||
} | |||
inline const_iterator& operator--() noexcept | |||
{ | |||
--m_it; | |||
return *this; | |||
} | |||
[[nodiscard]] inline const_iterator operator--(int) noexcept | |||
{ | |||
const_iterator tmp = *this; | |||
--(*this); | |||
return tmp; | |||
} | |||
inline const_iterator& operator+=(difference_type n) noexcept | |||
{ | |||
m_it += n; | |||
return *this; | |||
} | |||
inline const_iterator& operator-=(difference_type n) noexcept | |||
{ | |||
m_it -= n; | |||
return *this; | |||
} | |||
[[nodiscard]] inline bool operator==(const const_iterator& other) const noexcept | |||
{ | |||
return m_it == other.m_it; | |||
}; | |||
[[nodiscard]] inline std::weak_ordering operator<=>(const const_iterator& other) const noexcept | |||
{ | |||
return m_it <=> other.m_it; | |||
} | |||
[[nodiscard]] inline difference_type operator-(const const_iterator& rhs) const noexcept | |||
{ | |||
return m_it - rhs.m_it; | |||
} | |||
[[nodiscard]] inline const_iterator operator+(difference_type n) const noexcept | |||
{ | |||
return const_iterator{m_it + n}; | |||
} | |||
[[nodiscard]] inline const_iterator operator-(difference_type n) const noexcept | |||
{ | |||
return const_iterator{m_it - n}; | |||
} | |||
[[nodiscard]] friend const_iterator operator+(difference_type lhs, const const_iterator& rhs) noexcept | |||
{ | |||
return const_iterator{lhs + rhs.m_it}; | |||
} | |||
[[nodiscard]] friend const_iterator operator-(difference_type lhs, const const_iterator& rhs) noexcept | |||
{ | |||
return const_iterator{lhs - rhs.m_it}; | |||
} | |||
private: | |||
friend class brep_element_container; | |||
std::vector<std::unique_ptr<element_type>>::const_iterator m_it; | |||
}; | |||
using const_reverse_iterator = std::reverse_iterator<const_iterator>; | |||
/// @name Element access | |||
/// @{ | |||
/** | |||
* Returns a pointer to the element at the specified index. | |||
* | |||
* @param i Index of an element. | |||
* | |||
* @return Pointer to the element at index @p i. | |||
*/ | |||
[[nodiscard]] inline constexpr element_type* operator[](std::size_t i) const | |||
{ | |||
return m_elements[i].get(); | |||
} | |||
/// Returns the first element. | |||
[[nodiscard]] inline constexpr element_type* front() const noexcept | |||
{ | |||
return m_elements.front().get(); | |||
} | |||
/// Returns the last element. | |||
[[nodiscard]] inline constexpr element_type* back() const noexcept | |||
{ | |||
return m_elements.back().get(); | |||
} | |||
/// @} | |||
/// @name Iterators | |||
/// @{ | |||
/// Returns an iterator to the first element. | |||
/// @{ | |||
[[nodiscard]] inline constexpr const_iterator begin() const noexcept | |||
{ | |||
const_iterator it; | |||
it.m_it = m_elements.begin(); | |||
return it; | |||
} | |||
[[nodiscard]] inline constexpr const_iterator cbegin() const noexcept | |||
{ | |||
return begin(); | |||
} | |||
/// @} | |||
/// Returns an iterator to the element following the last element. | |||
/// @{ | |||
[[nodiscard]] inline constexpr const_iterator end() const noexcept | |||
{ | |||
const_iterator it; | |||
it.m_it = m_elements.end(); | |||
return it; | |||
} | |||
[[nodiscard]] inline constexpr const_iterator cend() const noexcept | |||
{ | |||
return end(); | |||
} | |||
/// @} | |||
/// Returns a reverse iterator to the first element of the reversed container. | |||
/// @{ | |||
[[nodiscard]] inline constexpr const_reverse_iterator rbegin() const noexcept | |||
{ | |||
return std::make_reverse_iterator(end()); | |||
} | |||
[[nodiscard]] inline constexpr const_reverse_iterator crbegin() const noexcept | |||
{ | |||
return rbegin(); | |||
} | |||
/// @} | |||
/// Returns a reverse iterator to the element following the last element of the reversed container. | |||
/// @{ | |||
[[nodiscard]] inline constexpr const_reverse_iterator rend() const noexcept | |||
{ | |||
return std::make_reverse_iterator(begin()); | |||
} | |||
[[nodiscard]] inline constexpr const_reverse_iterator crend() const noexcept | |||
{ | |||
return rend(); | |||
} | |||
/// @} | |||
/// @} | |||
/// @name Capacity | |||
/// @{ | |||
/// Returns `true` if the container is empty, `false` otherwise. | |||
[[nodiscard]] inline constexpr bool empty() const noexcept | |||
{ | |||
return m_elements.empty(); | |||
} | |||
/// Returns the number of elements in the container. | |||
[[nodiscard]] inline constexpr std::size_t size() const noexcept | |||
{ | |||
return m_elements.size(); | |||
} | |||
/// @} | |||
/// @name Attributes | |||
/// @{ | |||
/// Returns the element attribute map. | |||
/// @{ | |||
[[nodiscard]] const brep_attribute_map& attributes() const noexcept | |||
{ | |||
return m_attribute_map; | |||
} | |||
[[nodiscard]] brep_attribute_map& attributes() noexcept | |||
{ | |||
return m_attribute_map; | |||
} | |||
/// @} | |||
/// @} | |||
protected: | |||
friend class brep_mesh; | |||
/** | |||
* Constructs a B-rep element container. | |||
* | |||
* @param mesh Pointer to the parent mesh. | |||
*/ | |||
inline explicit brep_element_container(brep_mesh* mesh) noexcept: | |||
m_mesh(mesh) | |||
{} | |||
/// @name Modifiers | |||
/// @{ | |||
/** | |||
* Erases an element from the container. | |||
* | |||
* @param element Pointer to the element to erase. | |||
*/ | |||
virtual void erase(element_type* element) | |||
{ | |||
const auto index = element->m_index; | |||
for (auto& [key, values]: m_attribute_map.m_attributes) | |||
{ | |||
values->erase(index); | |||
} | |||
--m_attribute_map.m_element_count; | |||
m_elements.back()->m_index = index; | |||
m_elements[index] = std::move(m_elements.back()); | |||
m_elements.pop_back(); | |||
} | |||
/** | |||
* Appends a new element to the end of the container. | |||
* | |||
* @return Pointer to the new element. | |||
*/ | |||
virtual element_type* emplace_back() | |||
{ | |||
for (auto& [key, values]: m_attribute_map.m_attributes) | |||
{ | |||
values->emplace_back(); | |||
} | |||
++m_attribute_map.m_element_count; | |||
return m_elements.emplace_back(std::make_unique<element_type>()).get(); | |||
} | |||
/// @} | |||
brep_mesh* m_mesh; | |||
private: | |||
std::vector<std::unique_ptr<element_type>> m_elements; | |||
brep_attribute_map m_attribute_map; | |||
}; | |||
} // namespace geom | |||
#endif // ANTKEEPER_GEOM_BREP_ELEMENT_CONTAINER_HPP |
@ -0,0 +1,158 @@ | |||
/* | |||
* Copyright (C) 2023 Christopher J. Howard | |||
* | |||
* This file is part of Antkeeper source code. | |||
* | |||
* Antkeeper source code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* Antkeeper source code is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#include <engine/geom/brep/brep-face.hpp> | |||
#include <engine/geom/brep/brep-mesh.hpp> | |||
#include <algorithm> | |||
#include <vector> | |||
namespace geom { | |||
void brep_face_loop_list::push_back(brep_loop* loop) | |||
{ | |||
if (empty()) | |||
{ | |||
// List empty, initialize | |||
m_head = loop; | |||
loop->m_face_next = loop; | |||
loop->m_face_previous = loop; | |||
} | |||
else | |||
{ | |||
// Append loop | |||
loop->m_face_next = m_head; | |||
loop->m_face_previous = m_head->m_face_previous; | |||
m_head->m_face_previous->m_face_next = loop; | |||
m_head->m_face_previous = loop; | |||
} | |||
++m_size; | |||
} | |||
void brep_face_loop_list::insert(brep_loop* next, brep_loop* loop) | |||
{ | |||
loop->m_face_next = next; | |||
loop->m_face_previous = next->m_face_previous; | |||
next->m_face_previous->m_face_next = loop; | |||
next->m_face_previous = loop; | |||
++m_size; | |||
} | |||
void brep_face_loop_list::remove(brep_loop* loop) | |||
{ | |||
// Directly link next and previous loops | |||
loop->m_face_next->m_face_previous = loop->m_face_previous; | |||
loop->m_face_previous->m_face_next = loop->m_face_next; | |||
// If loop was the list head, update head | |||
if (m_head == loop) | |||
{ | |||
m_head = loop->m_face_next; | |||
} | |||
--m_size; | |||
} | |||
brep_face* brep_face_container::emplace_back(const std::span<brep_vertex*> vertices) | |||
{ | |||
if (vertices.size() < 3) | |||
{ | |||
return nullptr; | |||
} | |||
// Find or make edges | |||
std::vector<brep_edge*> edges(vertices.size()); | |||
std::size_t i = vertices.size() - 1; | |||
for (std::size_t j = 0; j < vertices.size(); ++j) | |||
{ | |||
edges[i] = m_mesh->edges().find(vertices[i], vertices[j]); | |||
if (!edges[i]) | |||
{ | |||
edges[i] = m_mesh->edges().emplace_back(vertices[i], vertices[j]); | |||
} | |||
i = j; | |||
} | |||
// Allocate face | |||
brep_face* face = brep_element_container<brep_face>::emplace_back(); | |||
face->m_index = size() - 1; | |||
// Make face loops | |||
for (std::size_t i = 0; i < vertices.size(); ++i) | |||
{ | |||
brep_loop* loop = m_mesh->loops().emplace_back(); | |||
loop->m_vertex = vertices[i]; | |||
loop->m_edge = edges[i]; | |||
loop->m_face = face; | |||
// Append loop to its edge's list of loops | |||
loop->m_edge->m_loops.push_back(loop); | |||
// Append loop to its face's list of loops | |||
face->m_loops.push_back(loop); | |||
} | |||
return face; | |||
}; | |||
void brep_face_container::erase(brep_face* face) | |||
{ | |||
brep_loop* loop = face->loops().front(); | |||
do | |||
{ | |||
// Remove loop from its edge's list of loops | |||
loop->m_edge->m_loops.remove(loop); | |||
brep_loop* next_loop = loop->m_face_next; | |||
// Erase loop | |||
m_mesh->loops().erase(loop); | |||
loop = next_loop; | |||
} | |||
while (loop != face->loops().front()); | |||
// Erase face | |||
brep_element_container<brep_face>::erase(face); | |||
} | |||
void brep_face_container::clear() noexcept | |||
{ | |||
while (!empty()) | |||
{ | |||
erase(back()); | |||
} | |||
} | |||
void brep_face_container::reverse(brep_face* face) | |||
{ | |||
for (brep_loop* loop: face->loops()) | |||
{ | |||
// Swap order of loop vertices | |||
loop->m_vertex = loop->edge()->vertices()[loop->edge()->vertices()[0] == loop->vertex()]; | |||
// Swap pointers to next and previous face loops | |||
std::swap(loop->m_face_next, loop->m_face_previous); | |||
} | |||
} | |||
} // namespace geom |
@ -0,0 +1,322 @@ | |||
/* | |||
* Copyright (C) 2023 Christopher J. Howard | |||
* | |||
* This file is part of Antkeeper source code. | |||
* | |||
* Antkeeper source code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* Antkeeper source code is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#ifndef ANTKEEPER_GEOM_BREP_FACE_HPP | |||
#define ANTKEEPER_GEOM_BREP_FACE_HPP | |||
#include <engine/geom/brep/brep-loop.hpp> | |||
#include <engine/geom/brep/brep-element-container.hpp> | |||
#include <cstddef> | |||
#include <iterator> | |||
#include <span> | |||
namespace geom { | |||
class brep_vertex; | |||
class brep_loop; | |||
template <class T> | |||
class brep_element_container; | |||
/** | |||
* List of B-rep loops that bound a common face. | |||
*/ | |||
class brep_face_loop_list | |||
{ | |||
public: | |||
friend class brep_mesh; | |||
struct const_iterator | |||
{ | |||
public: | |||
friend class brep_face_loop_list; | |||
using iterator_category = std::bidirectional_iterator_tag; | |||
using iterator_concept = std::bidirectional_iterator_tag; | |||
using difference_type = std::ptrdiff_t; | |||
using value_type = brep_loop*; | |||
using pointer = const value_type*; | |||
using reference = const value_type&; | |||
[[nodiscard]] inline constexpr value_type operator*() const noexcept | |||
{ | |||
return m_loop; | |||
} | |||
[[nodiscard]] inline constexpr value_type operator->() const noexcept | |||
{ | |||
return m_loop; | |||
} | |||
inline const_iterator& operator++() noexcept | |||
{ | |||
m_loop = m_loop->m_face_next; | |||
++m_position; | |||
return *this; | |||
} | |||
[[nodiscard]] inline const_iterator operator++(int) noexcept | |||
{ | |||
const_iterator tmp = *this; | |||
++(*this); | |||
return tmp; | |||
} | |||
inline const_iterator& operator--() noexcept | |||
{ | |||
m_loop = m_loop->m_face_previous; | |||
--m_position; | |||
return *this; | |||
} | |||
[[nodiscard]] inline const_iterator operator--(int) noexcept | |||
{ | |||
const_iterator tmp = *this; | |||
--(*this); | |||
return tmp; | |||
} | |||
[[nodiscard]] inline bool operator==(const const_iterator& other) const noexcept | |||
{ | |||
return m_position == other.m_position; | |||
}; | |||
[[nodiscard]] inline std::weak_ordering operator<=>(const const_iterator& other) const noexcept | |||
{ | |||
return m_position <=> other.m_position; | |||
} | |||
[[nodiscard]] inline difference_type operator-(const const_iterator& rhs) const noexcept | |||
{ | |||
return m_position - rhs.m_position; | |||
} | |||
private: | |||
brep_loop* m_loop; | |||
std::ptrdiff_t m_position; | |||
}; | |||
using const_reverse_iterator = std::reverse_iterator<const_iterator>; | |||
/// @name Element access | |||
/// @{ | |||
/// Returns the first loop. | |||
[[nodiscard]] inline brep_loop* front() const noexcept | |||
{ | |||
return m_head; | |||
} | |||
/// Returns the last loop. | |||
[[nodiscard]] inline brep_loop* back() const noexcept | |||
{ | |||
return m_head->m_face_previous; | |||
} | |||
/// @} | |||
/// @name Iterators | |||
/// @{ | |||
/// Returns an iterator to the first loop. | |||
/// @{ | |||
[[nodiscard]] inline constexpr const_iterator begin() const noexcept | |||
{ | |||
const_iterator it; | |||
it.m_loop = m_head; | |||
it.m_position = 0; | |||
return it; | |||
} | |||
[[nodiscard]] inline constexpr const_iterator cbegin() const noexcept | |||
{ | |||
return begin(); | |||
} | |||
/// @} | |||
/// Returns an iterator to the loop following the last loop. | |||
/// @{ | |||
[[nodiscard]] inline constexpr const_iterator end() const noexcept | |||
{ | |||
const_iterator it; | |||
it.m_loop = m_head; | |||
it.m_position = static_cast<std::ptrdiff_t>(m_size); | |||
return it; | |||
} | |||
[[nodiscard]] inline constexpr const_iterator cend() const noexcept | |||
{ | |||
return end(); | |||
} | |||
/// @} | |||
/// Returns a reverse iterator to the first loop of the reversed list. | |||
/// @{ | |||
[[nodiscard]] inline constexpr const_reverse_iterator rbegin() const noexcept | |||
{ | |||
return std::make_reverse_iterator(end()); | |||
} | |||
[[nodiscard]] inline constexpr const_reverse_iterator crbegin() const noexcept | |||
{ | |||
return rbegin(); | |||
} | |||
/// @} | |||
/// Returns a reverse iterator to the loop following the last loop of the reversed list. | |||
/// @{ | |||
[[nodiscard]] inline constexpr const_reverse_iterator rend() const noexcept | |||
{ | |||
return std::make_reverse_iterator(begin()); | |||
} | |||
[[nodiscard]] inline constexpr const_reverse_iterator crend() const noexcept | |||
{ | |||
return rend(); | |||
} | |||
/// @} | |||
/// @} | |||
/// @name Capacity | |||
/// @{ | |||
/// Returns `true` if the list is empty, `false` otherwise. | |||
[[nodiscard]] inline constexpr bool empty() const noexcept | |||
{ | |||
return !m_size; | |||
} | |||
/// Returns the number of loops in the list. | |||
[[nodiscard]] inline constexpr std::size_t size() const noexcept | |||
{ | |||
return m_size; | |||
} | |||
/// @} | |||
/// @name Modifiers | |||
/// @{ | |||
/** | |||
* Appends a loop to the end of the list. | |||
* | |||
* @param loop Pointer to the loop to append. | |||
*/ | |||
void push_back(brep_loop* loop); | |||
/** | |||
* Inserts a loop before a given loop. | |||
* | |||
* @param next Loop before which this loop should be inserted. | |||
* @param loop Pointer to the loop to insert. | |||
*/ | |||
void insert(brep_loop* next, brep_loop* loop); | |||
/** | |||
* Removes an loop from the list. | |||
* | |||
* @param loop Pointer to the loop to remove. | |||
*/ | |||
void remove(brep_loop* loop); | |||
/// @} | |||
private: | |||
brep_loop* m_head{}; | |||
std::size_t m_size{}; | |||
}; | |||
/** | |||
* Portion of a shell bounded by loops. | |||
*/ | |||
class brep_face | |||
{ | |||
public: | |||
friend class brep_mesh; | |||
friend class brep_element_container<brep_face>; | |||
friend class brep_face_container; | |||
/** | |||
* Returns the index of this face in the mesh face array. | |||
* | |||
* @warning This index may change if any faces are removed from the mesh. | |||
*/ | |||
[[nodiscard]] inline constexpr std::size_t index() const noexcept | |||
{ | |||
return m_index; | |||
} | |||
/// Returns the list of loops associated with this face. | |||
[[nodiscard]] inline constexpr const brep_face_loop_list& loops() const noexcept | |||
{ | |||
return m_loops; | |||
} | |||
private: | |||
std::size_t m_index; | |||
brep_face_loop_list m_loops; | |||
}; | |||
/** | |||
* B-rep face container. | |||
*/ | |||
class brep_face_container: public brep_element_container<brep_face> | |||
{ | |||
public: | |||
/** | |||
* Appends a new face to the end of the container. | |||
* | |||
* @param vertices Ordered vertices of the face. | |||
* | |||
* @return Pointer to the new face. | |||
*/ | |||
brep_face* emplace_back(const std::span<brep_vertex*> vertices); | |||
/** | |||
* Erases a face and all of its loops. | |||
* | |||
* @param face Pointer to the face to erase. | |||
* | |||
* @warning Invalidates iterators and indices of loops and faces. | |||
*/ | |||
void erase(brep_face* face) override; | |||
/** | |||
* Erases all faces and their loops. | |||
*/ | |||
void clear() noexcept; | |||
/** | |||
* Reverses the direction of a face's bounding loops. | |||
* | |||
* @param face Face bounded by the loops to reverse. | |||
*/ | |||
void reverse(brep_face* face); | |||
private: | |||
friend class brep_mesh; | |||
/** | |||
* Constructs a B-rep face container. | |||
* | |||
* @param mesh Pointer to the parent mesh. | |||
*/ | |||
inline explicit brep_face_container(brep_mesh* mesh) noexcept: | |||
brep_element_container<brep_face>(mesh) | |||
{} | |||
}; | |||
} // namespace geom | |||
#endif // ANTKEEPER_GEOM_BREP_FACE_HPP |
@ -0,0 +1,32 @@ | |||
/* | |||
* Copyright (C) 2023 Christopher J. Howard | |||
* | |||
* This file is part of Antkeeper source code. | |||
* | |||
* Antkeeper source code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* Antkeeper source code is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#include <engine/geom/brep/brep-loop.hpp> | |||
namespace geom { | |||
brep_loop* brep_loop_container::emplace_back() | |||
{ | |||
brep_loop* loop = brep_element_container<brep_loop>::emplace_back(); | |||
loop->m_index = size() - 1; | |||
return loop; | |||
}; | |||
} // namespace geom |
@ -0,0 +1,114 @@ | |||
/* | |||
* Copyright (C) 2023 Christopher J. Howard | |||
* | |||
* This file is part of Antkeeper source code. | |||
* | |||
* Antkeeper source code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* Antkeeper source code is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#ifndef ANTKEEPER_GEOM_BREP_LOOP_HPP | |||
#define ANTKEEPER_GEOM_BREP_LOOP_HPP | |||
#include <engine/geom/brep/brep-element-container.hpp> | |||
#include <cstddef> | |||
namespace geom { | |||
class brep_vertex; | |||
class brep_edge; | |||
class brep_face; | |||
template <class T> | |||
class brep_element_container; | |||
/** | |||
* Connected boundary of a single face. | |||
*/ | |||
class brep_loop | |||
{ | |||
public: | |||
friend class brep_mesh; | |||
friend class brep_edge_loop_list; | |||
friend class brep_face_loop_list; | |||
friend class brep_element_container<brep_loop>; | |||
friend class brep_loop_container; | |||
friend class brep_face_container; | |||
/** | |||
* Returns the index of this loop in the mesh loop array. | |||
* | |||
* @warning This index may change if any loops are removed from the mesh. | |||
*/ | |||
[[nodiscard]] inline constexpr std::size_t index() const noexcept | |||
{ | |||
return m_index; | |||
} | |||
/// Returns a pointer to the loop vertex. | |||
[[nodiscard]] inline constexpr brep_vertex* vertex() const noexcept | |||
{ | |||
return m_vertex; | |||
} | |||
/// Returns a pointer to the loop edge. | |||
[[nodiscard]] inline constexpr brep_edge* edge() const noexcept | |||
{ | |||
return m_edge; | |||
} | |||
/// Returns a pointer to the loop face. | |||
[[nodiscard]] inline constexpr brep_face* face() const noexcept | |||
{ | |||
return m_face; | |||
} | |||
private: | |||
std::size_t m_index; | |||
brep_vertex* m_vertex; | |||
brep_edge* m_edge; | |||
brep_face* m_face; | |||
brep_loop* m_edge_next; | |||
brep_loop* m_edge_previous; | |||
brep_loop* m_face_next; | |||
brep_loop* m_face_previous; | |||
}; | |||
/** | |||
* B-rep loop container. | |||
*/ | |||
class brep_loop_container: public brep_element_container<brep_loop> | |||
{ | |||
private: | |||
friend class brep_mesh; | |||
friend class brep_face_container; | |||
/** | |||
* Constructs a B-rep face container. | |||
* | |||
* @param mesh Pointer to the parent mesh. | |||
*/ | |||
inline explicit brep_loop_container(brep_mesh* mesh) noexcept: | |||
brep_element_container<brep_loop>(mesh) | |||
{} | |||
/** | |||
* Appends a new loop to the end of the container. | |||
* | |||
* @return Pointer to the new loop. | |||
*/ | |||
brep_loop* emplace_back() override; | |||
}; | |||
} // namespace geom | |||
#endif // ANTKEEPER_GEOM_BREP_LOOP_HPP |
@ -0,0 +1,182 @@ | |||
/* | |||
* Copyright (C) 2023 Christopher J. Howard | |||
* | |||
* This file is part of Antkeeper source code. | |||
* | |||
* Antkeeper source code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* Antkeeper source code is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#include <engine/geom/brep/brep-mesh.hpp> | |||
#include <engine/math/vector.hpp> | |||
#include <engine/resources/resource-loader.hpp> | |||
#include <engine/resources/deserialize-error.hpp> | |||
#include <engine/resources/deserializer.hpp> | |||
#include <array> | |||
namespace geom { | |||
brep_mesh::brep_mesh() noexcept: | |||
m_vertices(this), | |||
m_edges(this), | |||
m_loops(this), | |||
m_faces(this) | |||
{} | |||
brep_mesh::brep_mesh(const brep_mesh& other): | |||
brep_mesh() | |||
{ | |||
*this = other; | |||
} | |||
brep_mesh& brep_mesh::operator=(const brep_mesh& other) | |||
{ | |||
// Allocate containers | |||
m_vertices.m_elements.resize(other.m_vertices.m_elements.size()); | |||
m_edges.m_elements.resize(other.m_edges.m_elements.size()); | |||
m_loops.m_elements.resize(other.m_loops.m_elements.size()); | |||
m_faces.m_elements.resize(other.m_faces.m_elements.size()); | |||
// Copy-construct elements | |||
for (std::size_t i = 0; i < m_vertices.m_elements.size(); ++i) | |||
{ | |||
m_vertices.m_elements[i] = std::make_unique<brep_vertex>(*other.m_vertices.m_elements[i]); | |||
} | |||
for (std::size_t i = 0; i < m_edges.m_elements.size(); ++i) | |||
{ | |||
m_edges.m_elements[i] = std::make_unique<brep_edge>(*other.m_edges.m_elements[i]); | |||
} | |||
for (std::size_t i = 0; i < m_loops.m_elements.size(); ++i) | |||
{ | |||
m_loops.m_elements[i] = std::make_unique<brep_loop>(*other.m_loops.m_elements[i]); | |||
} | |||
for (std::size_t i = 0; i < m_faces.m_elements.size(); ++i) | |||
{ | |||
m_faces.m_elements[i] = std::make_unique<brep_face>(*other.m_faces.m_elements[i]); | |||
} | |||
// Copy per-element attributes | |||
m_vertices.m_attribute_map = other.m_vertices.m_attribute_map; | |||
m_edges.m_attribute_map = other.m_edges.m_attribute_map; | |||
m_loops.m_attribute_map = other.m_loops.m_attribute_map; | |||
m_faces.m_attribute_map = other.m_faces.m_attribute_map; | |||
// Reassign element pointers | |||
for (const auto& vertex: m_vertices.m_elements) | |||
{ | |||
if (!vertex->edges().empty()) | |||
{ | |||
vertex->m_edges.m_head = m_edges.m_elements[vertex->m_edges.m_head->m_index].get(); | |||
} | |||
} | |||
for (const auto& edge: m_edges.m_elements) | |||
{ | |||
edge->m_vertices[0] = m_vertices.m_elements[edge->m_vertices[0]->m_index].get(); | |||
edge->m_vertices[1] = m_vertices.m_elements[edge->m_vertices[1]->m_index].get(); | |||
edge->m_vertex_next[0] = m_edges.m_elements[edge->m_vertex_next[0]->m_index].get(); | |||
edge->m_vertex_next[1] = m_edges.m_elements[edge->m_vertex_next[1]->m_index].get(); | |||
edge->m_vertex_previous[0] = m_edges.m_elements[edge->m_vertex_previous[0]->m_index].get(); | |||
edge->m_vertex_previous[1] = m_edges.m_elements[edge->m_vertex_previous[1]->m_index].get(); | |||
if (!edge->loops().empty()) | |||
{ | |||
edge->m_loops.m_head = m_loops.m_elements[edge->m_loops.m_head->m_index].get(); | |||
} | |||
} | |||
for (const auto& loop: m_loops.m_elements) | |||
{ | |||
loop->m_vertex = m_vertices.m_elements[loop->m_vertex->m_index].get(); | |||
loop->m_edge = m_edges.m_elements[loop->m_edge->m_index].get(); | |||
loop->m_face = m_faces.m_elements[loop->m_face->m_index].get(); | |||
loop->m_edge_next = m_loops.m_elements[loop->m_edge_next->m_index].get(); | |||
loop->m_edge_previous = m_loops.m_elements[loop->m_edge_previous->m_index].get(); | |||
loop->m_face_next = m_loops.m_elements[loop->m_face_next->m_index].get(); | |||
loop->m_face_previous = m_loops.m_elements[loop->m_face_previous->m_index].get(); | |||
} | |||
for (const auto& face: m_faces.m_elements) | |||
{ | |||
face->m_loops.m_head = m_loops.m_elements[face->m_loops.m_head->m_index].get(); | |||
} | |||
return *this; | |||
} | |||
void brep_mesh::clear() noexcept | |||
{ | |||
m_vertices.clear(); | |||
} | |||
} // namespace geom | |||
/** | |||
* Deserializes a mesh. | |||
* | |||
* @param[out] mesh Mesh to deserialize. | |||
* @param[in,out] ctx Deserialize context. | |||
* | |||
* @throw deserialize_error Read error. | |||
*/ | |||
template <> | |||
void deserializer<geom::brep_mesh>::deserialize(geom::brep_mesh& mesh, deserialize_context& ctx) | |||
{ | |||
// Read vertex, edge, and face counts | |||
std::uint32_t vertex_count = 0; | |||
std::uint32_t edge_count = 0; | |||
std::uint32_t face_count = 0; | |||
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(&vertex_count), 1); | |||
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(&edge_count), 1); | |||
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(&face_count), 1); | |||
// Make vertices | |||
for (std::size_t i = 0; i < vertex_count; ++i) | |||
{ | |||
mesh.vertices().emplace_back(); | |||
} | |||
// Read edge vertex indices into buffer | |||
std::vector<std::array<std::uint32_t, 2>> edges(edge_count); | |||
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(edges.data()), edge_count * 2); | |||
// Make edges | |||
for (const auto& e: edges) | |||
{ | |||
mesh.edges().emplace_back(mesh.vertices()[e[0]], mesh.vertices()[e[1]]); | |||
} | |||
edges.clear(); | |||
// Read face vertex indices into buffer | |||
std::vector<std::array<std::uint32_t, 3>> faces(face_count); | |||
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(faces.data()), face_count * 3); | |||
// Make faces | |||
for (const auto& f: faces) | |||
{ | |||
geom::brep_vertex* vertices[3] = {mesh.vertices()[f[0]], mesh.vertices()[f[1]], mesh.vertices()[f[2]]}; | |||
mesh.faces().emplace_back(vertices); | |||
} | |||
faces.clear(); | |||
// Make vertex attributes | |||
auto& vertex_positions = static_cast<geom::brep_attribute<math::fvec3>&>(*mesh.vertices().attributes().emplace<math::fvec3>("position")); | |||
// Read vertex attributes | |||
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(vertex_positions.data()), vertex_count * 3); | |||
} | |||
template <> | |||
std::unique_ptr<geom::brep_mesh> resource_loader<geom::brep_mesh>::load(::resource_manager& resource_manager, deserialize_context& ctx) | |||
{ | |||
auto resource = std::make_unique<geom::brep_mesh>(); | |||
deserializer<geom::brep_mesh>().deserialize(*resource, ctx); | |||
return resource; | |||
} |
@ -0,0 +1,126 @@ | |||
/* | |||
* Copyright (C) 2023 Christopher J. Howard | |||
* | |||
* This file is part of Antkeeper source code. | |||
* | |||
* Antkeeper source code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* Antkeeper source code is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#ifndef ANTKEEPER_GEOM_BREP_MESH_HPP | |||
#define ANTKEEPER_GEOM_BREP_MESH_HPP | |||
#include <engine/geom/brep/brep-vertex.hpp> | |||
#include <engine/geom/brep/brep-edge.hpp> | |||
#include <engine/geom/brep/brep-loop.hpp> | |||
#include <engine/geom/brep/brep-face.hpp> | |||
namespace geom { | |||
/** | |||
* Boundary representation (B-rep) of a mesh. | |||
*/ | |||
class brep_mesh | |||
{ | |||
public: | |||
/** | |||
* Constructs an empty mesh. | |||
*/ | |||
brep_mesh() noexcept; | |||
/** | |||
* Constructs a copy of another mesh. | |||
* | |||
* @param other Mesh to copy. | |||
*/ | |||
brep_mesh(const brep_mesh& other); | |||
/** | |||
* Copies another mesh into this mesh. | |||
* | |||
* @param other Mesh to copy. | |||
* | |||
* @return Reference to this mesh. | |||
*/ | |||
brep_mesh& operator=(const brep_mesh& other); | |||
/// @name Modifiers | |||
/// @{ | |||
/// Erases all vertices, edges, loops, and faces. | |||
void clear() noexcept; | |||
/// @} | |||
/// @name Element access | |||
/// @{ | |||
/// Returns the mesh vertices. | |||
/// @{ | |||
[[nodiscard]] inline const brep_vertex_container& vertices() const noexcept | |||
{ | |||
return m_vertices; | |||
} | |||
[[nodiscard]] inline brep_vertex_container& vertices() noexcept | |||
{ | |||
return m_vertices; | |||
} | |||
///@} | |||
/// Returns the mesh edges. | |||
/// @{ | |||
[[nodiscard]] inline const brep_edge_container& edges() const noexcept | |||
{ | |||
return m_edges; | |||
} | |||
[[nodiscard]] inline brep_edge_container& edges() noexcept | |||
{ | |||
return m_edges; | |||
} | |||
/// @} | |||
/// Returns the mesh loops. | |||
/// @{ | |||
[[nodiscard]] inline const brep_loop_container& loops() const noexcept | |||
{ | |||
return m_loops; | |||
} | |||
[[nodiscard]] inline brep_loop_container& loops() noexcept | |||
{ | |||
return m_loops; | |||
} | |||
/// @} | |||
/// Returns the mesh faces. | |||
/// @{ | |||
[[nodiscard]] inline const brep_face_container& faces() const noexcept | |||
{ | |||
return m_faces; | |||
} | |||
[[nodiscard]] inline brep_face_container& faces() noexcept | |||
{ | |||
return m_faces; | |||
} | |||
/// @} | |||
/// @} | |||
private: | |||
brep_vertex_container m_vertices; | |||
brep_edge_container m_edges; | |||
brep_loop_container m_loops; | |||
brep_face_container m_faces; | |||
}; | |||
} // namespace geom | |||
#endif // ANTKEEPER_GEOM_BREP_MESH_HPP |
@ -0,0 +1,106 @@ | |||
/* | |||
* Copyright (C) 2023 Christopher J. Howard | |||
* | |||
* This file is part of Antkeeper source code. | |||
* | |||
* Antkeeper source code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* Antkeeper source code is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#include <engine/geom/brep/brep-operations.hpp> | |||
#include <engine/math/vector.hpp> | |||
#include <algorithm> | |||
namespace geom { | |||
void generate_face_normals(brep_mesh& mesh) | |||
{ | |||
const auto& vertex_positions = mesh.vertices().attributes().at<math::fvec3>("position"); | |||
auto& face_normals = static_cast<brep_attribute<math::fvec3>&>(*mesh.faces().attributes().try_emplace<math::fvec3>("normal").first); | |||
for (brep_face* face: mesh.faces()) | |||
{ | |||
auto loop = face->loops().begin(); | |||
const auto& a = vertex_positions[loop->vertex()->index()]; | |||
const auto& b = vertex_positions[(++loop)->vertex()->index()]; | |||
const auto& c = vertex_positions[(++loop)->vertex()->index()]; | |||
face_normals[face->index()] = math::normalize(math::cross(b - a, c - a)); | |||
} | |||
} | |||
void generate_vertex_normals(brep_mesh& mesh) | |||
{ | |||
const auto& vertex_positions = mesh.vertices().attributes().at<math::fvec3>("position"); | |||
auto& vertex_normals = static_cast<brep_attribute<math::fvec3>&>(*mesh.vertices().attributes().try_emplace<math::fvec3>("normal").first); | |||
// Init vertex normals to zero | |||
std::fill(vertex_normals.begin(), vertex_normals.end(), math::fvec3::zero()); | |||
if (auto face_normals_it = mesh.faces().attributes().find("normals"); face_normals_it != mesh.faces().attributes().end()) | |||
{ | |||
const auto& face_normals = static_cast<const brep_attribute<math::fvec3>&>(*face_normals_it); | |||
for (brep_face* face: mesh.faces()) | |||
{ | |||
// Get face normal from face normal attribute | |||
const auto& face_normal = face_normals[face->index()]; | |||
// Accumulate vertex normals | |||
for (brep_loop* loop: face->loops()) | |||
{ | |||
vertex_normals[loop->vertex()->index()] += face_normal; | |||
} | |||
} | |||
} | |||
else | |||
{ | |||
for (brep_face* face: mesh.faces()) | |||
{ | |||
// Calculate face normal | |||
auto loop = face->loops().begin(); | |||
const auto& a = vertex_positions[loop->vertex()->index()]; | |||
const auto& b = vertex_positions[(++loop)->vertex()->index()]; | |||
const auto& c = vertex_positions[(++loop)->vertex()->index()]; | |||
const auto face_normal = math::normalize(math::cross(b - a, c - a)); | |||
// Accumulate vertex normals | |||
for (brep_loop* loop: face->loops()) | |||
{ | |||
vertex_normals[loop->vertex()->index()] += face_normal; | |||
} | |||
} | |||
} | |||
// Normalize vertex normals | |||
for (auto& vertex_normal: vertex_normals) | |||
{ | |||
vertex_normal = math::normalize(vertex_normal); | |||
} | |||
} | |||
void generate_loop_barycentric(brep_mesh& mesh) | |||
{ | |||
const auto& vertex_positions = mesh.vertices().attributes().at<math::fvec3>("position"); | |||
auto& loop_barycentric = static_cast<brep_attribute<math::fvec3>&>(*mesh.loops().attributes().try_emplace<math::fvec3>("barycentric").first); | |||
for (brep_face* face: mesh.faces()) | |||
{ | |||
auto loop = face->loops().begin(); | |||
loop_barycentric[loop->index()] = {1.0f, 0.0f, 0.0f}; | |||
loop_barycentric[(++loop)->index()] = {0.0f, 1.0f, 0.0f}; | |||
loop_barycentric[(++loop)->index()] = {0.0f, 0.0f, 1.0f}; | |||
} | |||
} | |||
} // namespace geom |
@ -0,0 +1,54 @@ | |||
/* | |||
* Copyright (C) 2023 Christopher J. Howard | |||
* | |||
* This file is part of Antkeeper source code. | |||
* | |||
* Antkeeper source code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* Antkeeper source code is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#ifndef ANTKEEPER_GEOM_BREP_OPERATIONS_HPP | |||
#define ANTKEEPER_GEOM_BREP_OPERATIONS_HPP | |||
#include <engine/geom/brep/brep-mesh.hpp> | |||
namespace geom { | |||
/** | |||
* Generates the math::fvec3 face attribute "normal" for a B-rep mesh. | |||
* | |||
* @param mesh Mesh for which to generate normals. | |||
* | |||
* @warning Requires the math::fvec3 vertex attribute "position". | |||
*/ | |||
void generate_face_normals(brep_mesh& mesh); | |||
/** | |||
* Generates the math::fvec3 vertex attribute "normal" for a B-rep mesh. | |||
* | |||
* @param mesh Mesh for which to generate normals. | |||
* | |||
* @warning Requires the math::fvec3 vertex attribute "position". | |||
*/ | |||
void generate_vertex_normals(brep_mesh& mesh); | |||
/** | |||
* Generates the math::fvec3 loop attribute "barycentric" for a B-rep mesh. | |||
* | |||
* @param mesh Mesh for which to generate barycentric coordinates. | |||
*/ | |||
void generate_loop_barycentric(brep_mesh& mesh); | |||
} // namespace geom | |||
#endif // ANTKEEPER_GEOM_BREP_OPERATIONS_HPP |
@ -0,0 +1,102 @@ | |||
/* | |||
* Copyright (C) 2023 Christopher J. Howard | |||
* | |||
* This file is part of Antkeeper source code. | |||
* | |||
* Antkeeper source code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* Antkeeper source code is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#include <engine/geom/brep/brep-vertex.hpp> | |||
#include <engine/geom/brep/brep-mesh.hpp> | |||
namespace geom { | |||
void brep_vertex_edge_list::push_back(brep_edge* edge) | |||
{ | |||
// Determine index of vertex in new edge vertex array | |||
const std::size_t i = (edge->m_vertices[1] == m_vertex); | |||
if (empty()) | |||
{ | |||
// List empty, initialize | |||
m_head = edge; | |||
edge->m_vertex_next[i] = edge; | |||
edge->m_vertex_previous[i] = edge; | |||
} | |||
else | |||
{ | |||
// Determine index of vertex in head edge vertex array | |||
const std::size_t j = (m_head->m_vertices[1] == m_vertex); | |||
// Append edge | |||
edge->m_vertex_next[i] = m_head; | |||
edge->m_vertex_previous[i] = m_head->m_vertex_previous[j]; | |||
edge->m_vertex_previous[i]->m_vertex_next[edge->m_vertex_previous[i]->m_vertices[1] == m_vertex] = edge; | |||
m_head->m_vertex_previous[j] = edge; | |||
} | |||
++m_size; | |||
} | |||
void brep_vertex_edge_list::remove(brep_edge* edge) | |||
{ | |||
// Determine index of vertex in edge vertex array | |||
const std::size_t i = (edge->m_vertices[1] == m_vertex); | |||
// Get pointers to the next and previous edges | |||
brep_edge* next = edge->m_vertex_next[i]; | |||
brep_edge* previous = edge->m_vertex_previous[i]; | |||
// Directly link next and previous edges | |||
next->m_vertex_previous[next->m_vertices[1] == m_vertex] = previous; | |||
previous->m_vertex_next[previous->m_vertices[1] == m_vertex] = next; | |||
// If edge was the list head, update head | |||
if (m_head == edge) | |||
{ | |||
m_head = next; | |||
} | |||
--m_size; | |||
} | |||
brep_vertex* brep_vertex_container::emplace_back() | |||
{ | |||
brep_vertex* vertex = brep_element_container<brep_vertex>::emplace_back(); | |||
vertex->m_index = size() - 1; | |||
vertex->m_edges.m_vertex = vertex; | |||
return vertex; | |||
}; | |||
void brep_vertex_container::erase(brep_vertex* vertex) | |||
{ | |||
// Erase all edges bounded by this vertex | |||
while (!vertex->edges().empty()) | |||
{ | |||
m_mesh->edges().erase(vertex->edges().back()); | |||
} | |||
// Erase vertex | |||
brep_element_container<brep_vertex>::erase(vertex); | |||
} | |||
void brep_vertex_container::clear() noexcept | |||
{ | |||
while (!empty()) | |||
{ | |||
erase(back()); | |||
} | |||
} | |||
} // namespace geom |
@ -0,0 +1,312 @@ | |||
/* | |||
* Copyright (C) 2023 Christopher J. Howard | |||
* | |||
* This file is part of Antkeeper source code. | |||
* | |||
* Antkeeper source code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* Antkeeper source code is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#ifndef ANTKEEPER_GEOM_BREP_VERTEX_HPP | |||
#define ANTKEEPER_GEOM_BREP_VERTEX_HPP | |||
#include <engine/geom/brep/brep-edge.hpp> | |||
#include <engine/geom/brep/brep-element-container.hpp> | |||
#include <cstddef> | |||
#include <iterator> | |||
namespace geom { | |||
class brep_mesh; | |||
class brep_edge; | |||
class brep_vertex; | |||
template <class T> | |||
class brep_element_container; | |||
/** | |||
* List of B-rep edges bounded by a common vertex. | |||
*/ | |||
class brep_vertex_edge_list | |||
{ | |||
public: | |||
friend class brep_mesh; | |||
friend class brep_vertex_container; | |||
friend class brep_edge_container; | |||
struct const_iterator | |||
{ | |||
public: | |||
friend class brep_vertex_edge_list; | |||
using iterator_category = std::bidirectional_iterator_tag; | |||
using iterator_concept = std::bidirectional_iterator_tag; | |||
using difference_type = std::ptrdiff_t; | |||
using value_type = brep_edge*; | |||
using pointer = const value_type*; | |||
using reference = const value_type&; | |||
[[nodiscard]] inline constexpr value_type operator*() const noexcept | |||
{ | |||
return m_edge; | |||
} | |||
[[nodiscard]] inline constexpr value_type operator->() const noexcept | |||
{ | |||
return m_edge; | |||
} | |||
inline const_iterator& operator++() noexcept | |||
{ | |||
m_edge = m_edge->m_vertex_next[m_edge->vertices()[1] == m_vertex]; | |||
++m_position; | |||
return *this; | |||
} | |||
[[nodiscard]] inline const_iterator operator++(int) noexcept | |||
{ | |||
const_iterator tmp = *this; | |||
++(*this); | |||
return tmp; | |||
} | |||
inline const_iterator& operator--() noexcept | |||
{ | |||
m_edge = m_edge->m_vertex_previous[m_edge->vertices()[1] == m_vertex]; | |||
--m_position; | |||
return *this; | |||
} | |||
[[nodiscard]] inline const_iterator operator--(int) noexcept | |||
{ | |||
const_iterator tmp = *this; | |||
--(*this); | |||
return tmp; | |||
} | |||
[[nodiscard]] inline bool operator==(const const_iterator& other) const noexcept | |||
{ | |||
return m_position == other.m_position; | |||
}; | |||
[[nodiscard]] inline std::weak_ordering operator<=>(const const_iterator& other) const noexcept | |||
{ | |||
return m_position <=> other.m_position; | |||
} | |||
[[nodiscard]] inline difference_type operator-(const const_iterator& rhs) const noexcept | |||
{ | |||
return m_position - rhs.m_position; | |||
} | |||
private: | |||
brep_vertex* m_vertex; | |||
brep_edge* m_edge; | |||
std::ptrdiff_t m_position; | |||
}; | |||
using const_reverse_iterator = std::reverse_iterator<const_iterator>; | |||
/// @name Element access | |||
/// @{ | |||
/// Returns the first edge. | |||
[[nodiscard]] inline brep_edge* front() const noexcept | |||
{ | |||
return m_head; | |||
} | |||
/// Returns the last edge. | |||
[[nodiscard]] inline brep_edge* back() const noexcept | |||
{ | |||
return m_head->m_vertex_previous[m_head->vertices()[1] == m_vertex]; | |||
} | |||
/// @} | |||
/// @name Iterators | |||
/// @{ | |||
/// Returns an iterator to the first edge. | |||
/// @{ | |||
[[nodiscard]] inline constexpr const_iterator begin() const noexcept | |||
{ | |||
const_iterator it; | |||
it.m_vertex = m_vertex; | |||
it.m_edge = m_head; | |||
it.m_position = 0; | |||
return it; | |||
} | |||
[[nodiscard]] inline constexpr const_iterator cbegin() const noexcept | |||
{ | |||
return begin(); | |||
} | |||
/// @} | |||
/// Returns an iterator to the edge following the last edge. | |||
/// @{ | |||
[[nodiscard]] inline constexpr const_iterator end() const noexcept | |||
{ | |||
const_iterator it; | |||
it.m_vertex = m_vertex; | |||
it.m_edge = m_head; | |||
it.m_position = static_cast<std::ptrdiff_t>(m_size); | |||
return it; | |||
} | |||
[[nodiscard]] inline constexpr const_iterator cend() const noexcept | |||
{ | |||
return end(); | |||
} | |||
/// @} | |||
/// Returns a reverse iterator to the first edge of the reversed list. | |||
/// @{ | |||
[[nodiscard]] inline constexpr const_reverse_iterator rbegin() const noexcept | |||
{ | |||
return std::make_reverse_iterator(end()); | |||
} | |||
[[nodiscard]] inline constexpr const_reverse_iterator crbegin() const noexcept | |||
{ | |||
return rbegin(); | |||
} | |||
/// @} | |||
/// Returns a reverse iterator to the edge following the last edge of the reversed list. | |||
/// @{ | |||
[[nodiscard]] inline constexpr const_reverse_iterator rend() const noexcept | |||
{ | |||
return std::make_reverse_iterator(begin()); | |||
} | |||
[[nodiscard]] inline constexpr const_reverse_iterator crend() const noexcept | |||
{ | |||
return rend(); | |||
} | |||
/// @} | |||
/// @} | |||
/// @name Capacity | |||
/// @{ | |||
/// Returns `true` if the list is empty, `false` otherwise. | |||
[[nodiscard]] inline constexpr bool empty() const noexcept | |||
{ | |||
return !m_size; | |||
} | |||
/// Returns the number of edges in the list. | |||
[[nodiscard]] inline constexpr std::size_t size() const noexcept | |||
{ | |||
return m_size; | |||
} | |||
/// @} | |||
/// @name Modifiers | |||
/// @{ | |||
/** | |||
* Appends an edge to the end of the list. | |||
* | |||
* @param edge Pointer to the edge to append. | |||
*/ | |||
void push_back(brep_edge* edge); | |||
/** | |||
* Removes an edge from the list. | |||
* | |||
* @param edge Pointer to the edge to remove. | |||
*/ | |||
void remove(brep_edge* edge); | |||
/// @} | |||
private: | |||
brep_vertex* m_vertex{}; | |||
brep_edge* m_head{}; | |||
std::size_t m_size{}; | |||
}; | |||
/** | |||
* A point in space. | |||
*/ | |||
class brep_vertex | |||
{ | |||
public: | |||
friend class brep_mesh; | |||
friend class brep_element_container<brep_vertex>; | |||
friend class brep_vertex_container; | |||
friend class brep_edge_container; | |||
/** | |||
* Returns the index of this vertex in the mesh vertex array. | |||
* | |||
* @warning This index may change if any vertices are removed from the mesh. | |||
*/ | |||
[[nodiscard]] inline constexpr std::size_t index() const noexcept | |||
{ | |||
return m_index; | |||
} | |||
/// Returns the list of edges bounded by this vertex. | |||
[[nodiscard]] inline constexpr const brep_vertex_edge_list& edges() const noexcept | |||
{ | |||
return m_edges; | |||
} | |||
private: | |||
std::size_t m_index; | |||
brep_vertex_edge_list m_edges; | |||
}; | |||
/** | |||
* B-rep vertex container. | |||
*/ | |||
class brep_vertex_container: public brep_element_container<brep_vertex> | |||
{ | |||
public: | |||
/** | |||
* Appends a new vertex to the end of the container. | |||
* | |||
* @return Pointer to the new vertex. | |||
*/ | |||
brep_vertex* emplace_back() override; | |||
/** | |||
* Erases a vertex and all dependent edges, loops, and faces. | |||
* | |||
* @param vertex Pointer to the vertex to erase. | |||
* | |||
* @warning Invalidates iterators and indices of vertices, edges, loops, and faces. | |||
*/ | |||
void erase(brep_vertex* vertex) override; | |||
/** | |||
* Erases all vertices and their dependent edges, loops, and faces. | |||
*/ | |||
void clear() noexcept; | |||
private: | |||
friend class brep_mesh; | |||
/** | |||
* Constructs a B-rep vertex container. | |||
* | |||
* @param mesh Pointer to the parent mesh. | |||
*/ | |||
inline explicit brep_vertex_container(brep_mesh* mesh) noexcept: | |||
brep_element_container<brep_vertex>(mesh) | |||
{} | |||
}; | |||
} // namespace geom | |||
#endif // ANTKEEPER_GEOM_BREP_VERTEX_HPP |
@ -0,0 +1,51 @@ | |||
/* | |||
* Copyright (C) 2023 Christopher J. Howard | |||
* | |||
* This file is part of Antkeeper source code. | |||
* | |||
* Antkeeper source code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* Antkeeper source code is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#ifndef ANTKEEPER_GEOM_BVH_NODE_HPP | |||
#define ANTKEEPER_GEOM_BVH_NODE_HPP | |||
#include <engine/geom/primitives/box.hpp> | |||
#include <cstdint> | |||
namespace geom { | |||
/** | |||
* Single node in a bounding volume hierarchy. | |||
*/ | |||
struct bvh_node | |||
{ | |||
/// Returns `true` if the node is a leaf node, `false` otherwise. | |||
[[nodiscard]] inline constexpr bool is_leaf() const noexcept | |||
{ | |||
return size; | |||
} | |||
/// Node bounds. | |||
geom::box<float> bounds; | |||
/// Number of primitives in the node. | |||
std::uint32_t size; | |||
/// Offset to the first child node (non-leaf) or primitive (leaf). | |||
std::uint32_t offset; | |||
}; | |||
} // namespace geom | |||
#endif // ANTKEEPER_GEOM_BVH_NODE_HPP |
@ -0,0 +1,199 @@ | |||
/* | |||
* Copyright (C) 2023 Christopher J. Howard | |||
* | |||
* This file is part of Antkeeper source code. | |||
* | |||
* Antkeeper source code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* Antkeeper source code is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#include <engine/geom/bvh/bvh.hpp> | |||
#include <engine/geom/intersection.hpp> | |||
#include <engine/geom/brep/brep-mesh.hpp> | |||
#include <engine/debug/log.hpp> | |||
#include <algorithm> | |||
#include <numeric> | |||
namespace geom { | |||
bvh::bvh(std::span<const bvh_primitive> primitives) | |||
{ | |||
build(primitives); | |||
} | |||
bvh::bvh(const brep_mesh& mesh) | |||
{ | |||
build(mesh); | |||
} | |||
void bvh::build(std::span<const bvh_primitive> primitives) | |||
{ | |||
if (primitives.empty()) | |||
{ | |||
m_primitive_indices.clear(); | |||
m_nodes.clear(); | |||
m_node_count = 0; | |||
} | |||
else | |||
{ | |||
// Allocate and fill primitive index array | |||
m_primitive_indices.resize(primitives.size()); | |||
std::iota(m_primitive_indices.begin(), m_primitive_indices.end(), 0); | |||
// Allocate nodes | |||
m_nodes.resize(primitives.size() << 1); | |||
// Init root node | |||
m_node_count = 1; | |||
auto& root = m_nodes.front(); | |||
root.size = static_cast<std::uint32_t>(primitives.size()); | |||
root.offset = 0; | |||
update_bounds(root, primitives); | |||
// Recursively build BVH | |||
subdivide(root, primitives); | |||
} | |||
} | |||
void bvh::build(const brep_mesh& mesh) | |||
{ | |||
// Get mesh vertex positions attribute | |||
const auto& vertex_positions = mesh.vertices().attributes().at<math::fvec3>("position"); | |||
// Allocate BVH primitives for mesh faces | |||
std::vector<bvh_primitive> primitives(mesh.faces().size()); | |||
// Calculate bounding boxes | |||
for (brep_face* face: mesh.faces()) | |||
{ | |||
auto& primitive = primitives[face->index()]; | |||
primitive.centroid = {}; | |||
primitive.bounds = {math::fvec3::infinity(), -math::fvec3::infinity()}; | |||
for (brep_loop* loop: face->loops()) | |||
{ | |||
const auto& vertex_position = vertex_positions[loop->vertex()->index()]; | |||
primitive.centroid += vertex_position; | |||
primitive.bounds.extend(vertex_position); | |||
} | |||
primitive.centroid /= static_cast<float>(face->loops().size()); | |||
} | |||
// Build BVH from the bounding boxes of the mesh faces | |||
build(primitives); | |||
} | |||
void bvh::update_bounds(bvh_node& node, const std::span<const bvh_primitive>& primitives) | |||
{ | |||
node.bounds = {math::fvec3::infinity(), -math::fvec3::infinity()}; | |||
for (std::uint32_t i = 0; i < node.size; ++i) | |||
{ | |||
const auto& primitive = primitives[m_primitive_indices[node.offset + i]]; | |||
node.bounds.extend(primitive.bounds); | |||
}; | |||
} | |||
void bvh::subdivide(bvh_node& node, const std::span<const bvh_primitive>& primitives) | |||
{ | |||
if (node.size <= 2) | |||
{ | |||
return; | |||
} | |||
// Determine index of split axis | |||
const auto extents = node.bounds.size(); | |||
std::uint8_t split_axis = 0; | |||
if (extents.y() > extents.x()) | |||
{ | |||
split_axis = 1; | |||
} | |||
if (extents.z() > extents[split_axis]) | |||
{ | |||
split_axis = 2; | |||
} | |||
// Determine split coordinate | |||
const float split_coord = node.bounds.min[split_axis] + extents[split_axis] * 0.5f; | |||
std::uint32_t i = node.offset; | |||
std::uint32_t j = (node.size) ? i + node.size - 1 : i; | |||
while (i <= j) | |||
{ | |||
const auto& primitive = primitives[m_primitive_indices[i]]; | |||
if (primitive.centroid[split_axis] < split_coord) | |||
// if (primitive.bounds.center()[split_axis] < split_coord) | |||
{ | |||
++i; | |||
} | |||
else | |||
{ | |||
std::swap(m_primitive_indices[i], m_primitive_indices[j]); | |||
if (!j) | |||
{ | |||
break; | |||
} | |||
--j; | |||
} | |||
} | |||
const std::uint32_t left_size = i - node.offset; | |||
if (!left_size || left_size == node.size) | |||
{ | |||
return; | |||
} | |||
const std::uint32_t left_index = m_node_count++; | |||
auto& left_child = m_nodes[left_index]; | |||
left_child.offset = node.offset; | |||
left_child.size = left_size; | |||
update_bounds(left_child, primitives); | |||
const std::uint32_t right_index = m_node_count++; | |||
auto& right_child = m_nodes[right_index]; | |||
right_child.offset = i; | |||
right_child.size = node.size - left_size; | |||
update_bounds(right_child, primitives); | |||
node.offset = left_index; | |||
node.size = 0; | |||
subdivide(left_child, primitives); | |||
subdivide(right_child, primitives); | |||
} | |||
void bvh::visit(const bvh_node& node, const geom::ray<float, 3>& ray, const visitor_type& f) const | |||
{ | |||
if (!geom::intersection(ray, node.bounds)) | |||
{ | |||
return; | |||
} | |||
if (node.is_leaf()) | |||
{ | |||
// Visit leaf node primitives | |||
for (std::uint32_t i = 0; i < node.size; ++i) | |||
{ | |||
f(m_primitive_indices[node.offset + i]); | |||
} | |||
} | |||
else | |||
{ | |||
// Recursively visit node children | |||
visit(m_nodes[node.offset], ray, f); | |||
visit(m_nodes[node.offset + 1], ray, f); | |||
} | |||
} | |||
} // namespace geom |
@ -0,0 +1,113 @@ | |||
/* | |||
* Copyright (C) 2023 Christopher J. Howard | |||
* | |||
* This file is part of Antkeeper source code. | |||
* | |||
* Antkeeper source code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* Antkeeper source code is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#ifndef ANTKEEPER_GEOM_BVH_HPP | |||
#define ANTKEEPER_GEOM_BVH_HPP | |||
#include <engine/geom/primitives/ray.hpp> | |||
#include <engine/geom/bvh/bvh-primitive.hpp> | |||
#include <engine/geom/bvh/bvh-node.hpp> | |||
#include <cstdint> | |||
#include <functional> | |||
#include <span> | |||
#include <vector> | |||
namespace geom { | |||
class brep_mesh; | |||
/** | |||
* Bounding volume hierarchy (BVH). | |||
*/ | |||
class bvh | |||
{ | |||
public: | |||
using visitor_type = std::function<void(std::uint32_t)>; | |||
/** | |||
* Constructs a BVH from a set of primitives. | |||
* | |||
* @param primitives Axis-aligned bounding boxes. | |||
*/ | |||
explicit bvh(std::span<const bvh_primitive> primitives); | |||
/** | |||
* Constructs a BVH from a B-rep mesh. | |||
* | |||
* @param mesh B-rep mesh from which to build the BVH. | |||
*/ | |||
explicit bvh(const brep_mesh& mesh); | |||
/// Constructs an empty BVH. | |||
constexpr bvh() noexcept = default; | |||
/** | |||
* Constructs a BVH from a set of primitives. | |||
* | |||
* @param primitives BVH primitives. | |||
*/ | |||
void build(std::span<const bvh_primitive> primitives); | |||
/** | |||
* Constructs a BVH from a B-rep mesh. | |||
* | |||
* @param mesh B-rep mesh from which to build the BVH. | |||
*/ | |||
void build(const brep_mesh& mesh); | |||
/** | |||
* Visits the primitive indices of all BVH nodes that intersect a ray. | |||
* | |||
* @param ray Query ray. | |||
* @param f Unary visitor function which operates on a BVH primitive index. | |||
*/ | |||
inline void visit(const geom::ray<float, 3>& ray, const visitor_type& f) const | |||
{ | |||
if (m_node_count) | |||
{ | |||
visit(m_nodes.front(), ray, f); | |||
} | |||
} | |||
/// Returns the BVH nodes. | |||
[[nodiscard]] inline constexpr const std::vector<bvh_node>& nodes() const noexcept | |||
{ | |||
return m_nodes; | |||
} | |||
private: | |||
void update_bounds(bvh_node& node, const std::span<const bvh_primitive>& primitives); | |||
/** | |||
* Builds the BVH through recursive subdivision. | |||
* | |||
* @param node Current node. | |||
* @param primitives BVH primitives. | |||
*/ | |||
void subdivide(bvh_node& node, const std::span<const bvh_primitive>& primitives); | |||
void visit(const bvh_node& node, const geom::ray<float, 3>& ray, const visitor_type& f) const; | |||
std::vector<std::uint32_t> m_primitive_indices; | |||
std::vector<bvh_node> m_nodes; | |||
std::uint32_t m_node_count{}; | |||
}; | |||
} // namespace geom | |||
#endif // ANTKEEPER_GEOM_BVH_HPP |
@ -1,179 +0,0 @@ | |||
/* | |||
* Copyright (C) 2023 Christopher J. Howard | |||
* | |||
* This file is part of Antkeeper source code. | |||
* | |||
* Antkeeper source code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* Antkeeper source code is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#include <engine/geom/mesh-accelerator.hpp> | |||
#include <engine/geom/mesh-functions.hpp> | |||
#include <engine/geom/morton.hpp> | |||
#include <bitset> | |||
namespace geom { | |||
mesh_accelerator::mesh_accelerator() | |||
{} | |||
void mesh_accelerator::build(const mesh& mesh) | |||
{ | |||
// Clear octree and face map | |||
octree.clear(); | |||
face_map.clear(); | |||
// Calculate mesh dimensions | |||
box<float> bounds = calculate_bounds(mesh); | |||
float3 mesh_dimensions = bounds.max - bounds.min; | |||
center_offset = mesh_dimensions * 0.5f - (bounds.min + bounds.max) * 0.5f; | |||
// Calculate node dimensions at each octree depth | |||
for (auto i = 0; i <= octree_type::max_depth; ++i) | |||
{ | |||
node_dimensions[i] = mesh_dimensions * static_cast<float>((1.0f / std::pow(2, i))); | |||
} | |||
// Add faces to octree | |||
for (mesh::face* face: mesh.get_faces()) | |||
{ | |||
// Calculate face bounds | |||
float3 min_point = reinterpret_cast<const float3&>(face->edge->vertex->position); | |||
float3 max_point = min_point; | |||
mesh::edge* edge = face->edge; | |||
do | |||
{ | |||
const auto& position = edge->vertex->position; | |||
for (int i = 0; i < 3; ++i) | |||
{ | |||
min_point[i] = std::min<float>(min_point[i], position[i]); | |||
max_point[i] = std::max<float>(max_point[i], position[i]); | |||
} | |||
edge = edge->next; | |||
} | |||
while (edge != face->edge); | |||
// 1. Find max depth node of aabb min | |||
// 2. Find max depth node of aabb max | |||
// 3. Find common ancestor of the two nodes--that's the containing node. | |||
typename octree_type::node_type min_node = find_node(min_point); | |||
typename octree_type::node_type max_node = find_node(max_point); | |||
typename octree_type::node_type containing_node = octree_type::common_ancestor(min_node, max_node); | |||
// Insert containing node into octree | |||
octree.insert(containing_node); | |||
// Add face to face map | |||
face_map[containing_node].push_back(face); | |||
} | |||
} | |||
std::optional<mesh_accelerator::ray_query_result> mesh_accelerator::query_nearest(const ray<float, 3>& ray) const | |||
{ | |||
ray_query_result result; | |||
result.t = std::numeric_limits<float>::infinity(); | |||
result.face = nullptr; | |||
query_nearest_recursive(result.t, result.face, octree.root, ray); | |||
if (result.face) | |||
return std::optional{result}; | |||
return std::nullopt; | |||
} | |||
void mesh_accelerator::query_nearest_recursive(float& nearest_t, geom::mesh::face*& nearest_face, typename octree_type::node_type node, const ray<float, 3>& ray) const | |||
{ | |||
// Get node bounds | |||
const box<float> node_bounds = get_node_bounds(node); | |||
// If ray passed through this node | |||
if (intersection(ray, node_bounds)) | |||
{ | |||
// Test all triangles in the node | |||
if (auto it = face_map.find(node); it != face_map.end()) | |||
{ | |||
const std::list<mesh::face*>& faces = it->second; | |||
for (mesh::face* face: faces) | |||
{ | |||
// Get triangle coordinates | |||
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); | |||
// Test for intersection with triangle | |||
auto triangle_intersection = intersection(ray, a, b, c); | |||
if (triangle_intersection) | |||
{ | |||
const float t = std::get<0>(*triangle_intersection); | |||
if (t < nearest_t) | |||
{ | |||
nearest_t = t; | |||
nearest_face = face; | |||
} | |||
} | |||
} | |||
} | |||
// Test all child nodes | |||
if (!octree.is_leaf(node)) | |||
{ | |||
for (int i = 0; i < 8; ++i) | |||
{ | |||
query_nearest_recursive(nearest_t, nearest_face, octree.child(node, i), ray); | |||
} | |||
} | |||
} | |||
} | |||
box<float> mesh_accelerator::get_node_bounds(typename octree_type::node_type node) const | |||
{ | |||
// Decode Morton location of node | |||
std::uint32_t x, y, z; | |||
morton::decode(octree_type::location(node), x, y, z); | |||
float3 node_location = float3{static_cast<float>(x), static_cast<float>(y), static_cast<float>(z)}; | |||
// Get node dimensions at node depth | |||
const float3& dimensions = node_dimensions[octree_type::depth(node)]; | |||
// Calculate AABB | |||
float3 min_point = (node_location * dimensions) - center_offset; | |||
return box<float>{min_point, min_point + dimensions}; | |||
} | |||
typename mesh_accelerator::octree_type::node_type mesh_accelerator::find_node(const float3& point) const | |||
{ | |||
// Transform point to octree space | |||
float3 transformed_point = (point + center_offset); | |||
// Account for floating-point tolerance | |||
const float epsilon = 0.00001f; | |||
transformed_point.x() = std::max<float>(0.0f, std::min<float>(node_dimensions[0].x() - epsilon, transformed_point.x())); | |||
transformed_point.y() = std::max<float>(0.0f, std::min<float>(node_dimensions[0].y() - epsilon, transformed_point.y())); | |||
transformed_point.z() = std::max<float>(0.0f, std::min<float>(node_dimensions[0].z() - epsilon, transformed_point.z())); | |||
// Transform point to max-depth node space | |||
transformed_point = transformed_point / node_dimensions[octree_type::max_depth]; | |||
// Encode transformed point as a Morton location code | |||
std::uint32_t location = morton::encode( | |||
static_cast<std::uint32_t>(transformed_point.x()), | |||
static_cast<std::uint32_t>(transformed_point.y()), | |||
static_cast<std::uint32_t>(transformed_point.z())); | |||
// Return max depth node at the determined location | |||
return octree_type::node(octree_type::max_depth, location); | |||
} | |||
} // namespace geom |
@ -1,80 +0,0 @@ | |||
/* | |||
* Copyright (C) 2023 Christopher J. Howard | |||
* | |||
* This file is part of Antkeeper source code. | |||
* | |||
* Antkeeper source code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* Antkeeper source code is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#ifndef ANTKEEPER_GEOM_MESH_ACCELERATOR_HPP | |||
#define ANTKEEPER_GEOM_MESH_ACCELERATOR_HPP | |||
#include <engine/geom/mesh.hpp> | |||
#include <engine/geom/octree.hpp> | |||
#include <engine/geom/primitives/box.hpp> | |||
#include <engine/geom/intersection.hpp> | |||
#include <engine/utility/fundamental-types.hpp> | |||
#include <list> | |||
#include <optional> | |||
#include <unordered_map> | |||
namespace geom { | |||
/** | |||
* Acceleration structure for querying mesh geometry. | |||
*/ | |||
class mesh_accelerator | |||
{ | |||
public: | |||
struct ray_query_result | |||
{ | |||
float t; | |||
geom::mesh::face* face; | |||
}; | |||
mesh_accelerator(); | |||
/** | |||
* Builds the acceleration structure. | |||
*/ | |||
void build(const mesh& mesh); | |||
/** | |||
* Finds the first intersection between a ray and a triangle in the mesh. | |||
* | |||
* @param ray Ray to test for intersection. | |||
* @return Ray query result on intersection, and `std::nullopt` if no intersection occurred. | |||
*/ | |||
std::optional<ray_query_result> query_nearest(const ray<float, 3>& ray) const; | |||
private: | |||
typedef unordered_octree32 octree_type; | |||
box<float> get_node_bounds(typename octree_type::node_type node) const; | |||
void query_nearest_recursive(float& nearest_t, geom::mesh::face*& nearest_face, typename octree_type::node_type node, const ray<float, 3>& ray) const; | |||
/// Returns the max-depth node in which the point is located | |||
typename octree_type::node_type find_node(const float3& point) const; | |||
octree_type octree; | |||
float3 node_dimensions[octree_type::max_depth + 1]; | |||
float3 center_offset; | |||
std::unordered_map<typename octree_type::node_type, std::list<mesh::face*>> face_map; | |||
}; | |||
} // namespace geom | |||
#endif // ANTKEEPER_GEOM_MESH_ACCELERATOR_HPP | |||
@ -1,222 +0,0 @@ | |||
/* | |||
* Copyright (C) 2023 Christopher J. Howard | |||
* | |||
* This file is part of Antkeeper source code. | |||
* | |||
* Antkeeper source code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* Antkeeper source code is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#include <engine/geom/mesh-functions.hpp> | |||
#include <engine/utility/fundamental-types.hpp> | |||
#include <unordered_map> | |||
#include <vector> | |||
namespace geom { | |||
struct edge_hasher | |||
{ | |||
std::size_t operator()(const std::array<std::size_t, 2>& v) const noexcept | |||
{ | |||
std::size_t hash = std::hash<std::size_t>()(v[0]); | |||
return hash ^ (std::hash<std::size_t>()(v[1]) + 0x9e3779b9 + (hash << 6) + (hash >> 2)); | |||
} | |||
}; | |||
void create_triangle_mesh(mesh& mesh, const std::vector<float3>& vertices, const std::vector<std::array<std::uint_fast32_t, 3>>& triangles) | |||
{ | |||
for (const auto& vertex: vertices) | |||
mesh.add_vertex(vertex); | |||
std::unordered_map<std::array<std::size_t, 2>, geom::mesh::edge*, edge_hasher> edge_map; | |||
const std::vector<mesh::vertex*>& mesh_vertices = mesh.get_vertices(); | |||
std::vector<geom::mesh::edge*> loop(3); | |||
for (const auto& triangle: triangles) | |||
{ | |||
geom::mesh::vertex* triangle_vertices[3] = | |||
{ | |||
mesh_vertices[triangle[0]], | |||
mesh_vertices[triangle[1]], | |||
mesh_vertices[triangle[2]] | |||
}; | |||
for (int j = 0; j < 3; ++j) | |||
{ | |||
geom::mesh::vertex* start = triangle_vertices[j]; | |||
geom::mesh::vertex* end = triangle_vertices[(j + 1) % 3]; | |||
if (auto it = edge_map.find({start->index, end->index}); it != edge_map.end()) | |||
{ | |||
loop[j] = it->second; | |||
} | |||
else | |||
{ | |||
loop[j] = mesh.add_edge(start, end); | |||
edge_map[{start->index, end->index}] = loop[j]; | |||
edge_map[{end->index, start->index}] = loop[j]->symmetric; | |||
} | |||
} | |||
mesh.add_face(loop); | |||
} | |||
} | |||
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]); | |||
const float3& a = face.edge->vertex->position; | |||
const float3& b = face.edge->next->vertex->position; | |||
const float3& c = face.edge->previous->vertex->position; | |||
normals[i] = math::normalize(math::cross(b - a, c - a)); | |||
} | |||
} | |||
float3 calculate_face_normal(const mesh::face& face) | |||
{ | |||
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 | |||
std::vector<float3> tangent_buffer(vertices.size(), float3{0.0f, 0.0f, 0.0f}); | |||
std::vector<float3> bitangent_buffer(vertices.size(), float3{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}; | |||
} | |||
} | |||
box<float> calculate_bounds(const mesh& mesh) | |||
{ | |||
box<float> bounds; | |||
for (int i = 0; i < 3; ++i) | |||
{ | |||
bounds.min[i] = std::numeric_limits<float>::infinity(); | |||
bounds.max[i] = -std::numeric_limits<float>::infinity(); | |||
} | |||
for (const mesh::vertex* vertex: mesh.get_vertices()) | |||
{ | |||
const auto& position = vertex->position; | |||
bounds.extend(position); | |||
} | |||
return bounds; | |||
} | |||
mesh::vertex* poke_face(mesh& mesh, std::size_t index) | |||
{ | |||
mesh::face* face = mesh.get_faces()[index]; | |||
// Collect face edges and sum edge vertex positions | |||
mesh::loop loop = {face->edge}; | |||
float3 sum_positions = face->edge->vertex->position; | |||
for (mesh::edge* edge = face->edge->next; edge != face->edge; edge = edge->next) | |||
{ | |||
loop.push_back(edge); | |||
sum_positions += edge->vertex->position; | |||
} | |||
if (loop.size() <= 2) | |||
return nullptr; | |||
// Remove face | |||
mesh.remove_face(face); | |||
// Add vertex in center | |||
mesh::vertex* center = mesh.add_vertex(sum_positions / static_cast<float>(loop.size())); | |||
// Create first triangle | |||
geom::mesh::edge* ab = loop[0]; | |||
geom::mesh::edge* bc = mesh.add_edge(ab->next->vertex, center); | |||
geom::mesh::edge* ca = mesh.add_edge(center, ab->vertex); | |||
mesh.add_face({ab, bc, ca}); | |||
// Save first triangle CA edge | |||
geom::mesh::edge* first_triangle_ca = ca; | |||
// Create remaining triangles | |||
for (std::size_t i = 1; i < loop.size(); ++i) | |||
{ | |||
ab = loop[i]; | |||
ca = bc->symmetric; | |||
if (i == loop.size() - 1) | |||
bc = first_triangle_ca->symmetric; | |||
else | |||
bc = mesh.add_edge(ab->next->vertex, center); | |||
mesh.add_face({ab, bc, ca}); | |||
} | |||
return center; | |||
} | |||
} // namespace geom |
@ -1,76 +0,0 @@ | |||
/* | |||
* Copyright (C) 2023 Christopher J. Howard | |||
* | |||
* This file is part of Antkeeper source code. | |||
* | |||
* Antkeeper source code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* Antkeeper source code is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#ifndef ANTKEEPER_GEOM_MESH_FUNCTIONS_HPP | |||
#define ANTKEEPER_GEOM_MESH_FUNCTIONS_HPP | |||
#include <engine/geom/primitives/box.hpp> | |||
#include <engine/geom/mesh.hpp> | |||
#include <engine/utility/fundamental-types.hpp> | |||
#include <array> | |||
#include <vector> | |||
namespace geom { | |||
/** | |||
* Creates a triangle mesh from a list of vertices and indices. | |||
* | |||
* @param[out] mesh Mesh to which vertices, edges, and faces will be added. | |||
* @param vertices Vertex positions | |||
* @param triangles Triangle indices | |||
* @return Newly created triangle mesh. | |||
*/ | |||
void create_triangle_mesh(mesh& mesh, const std::vector<float3>& vertices, const std::vector<std::array<std::uint_fast32_t, 3>>& triangles); | |||
/** | |||
* Calculates normals for each face. | |||
* | |||
* @param[out] Array in which faceted normals will be stored. | |||
*/ | |||
void calculate_face_normals(float3* normals, const mesh& mesh); | |||
float3 calculate_face_normal(const mesh::face& face); | |||
/** | |||
* 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); | |||
/** | |||
* Calculates the AABB bounds of a mesh. | |||
*/ | |||
box<float> calculate_bounds(const mesh& mesh); | |||
/** | |||
* Triangulates a face by adding a new vertex in the center, then creating triangles between the edges of the original face and the new vertex. | |||
* | |||
* @param mesh Mesh containing the face to poke. | |||
* @param index Index of the face to poke. | |||
* @return Pointer to the newly-created vertex in the center of the face, or `nullptr` if the face could not be poked. | |||
*/ | |||
mesh::vertex* poke_face(mesh& mesh, std::size_t index); | |||
} // namespace geom | |||
#endif // ANTKEEPER_GEOM_MESH_FUNCTIONS_HPP | |||
@ -1,436 +0,0 @@ | |||
/* | |||
* Copyright (C) 2023 Christopher J. Howard | |||
* | |||
* This file is part of Antkeeper source code. | |||
* | |||
* Antkeeper source code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* Antkeeper source code is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#include <engine/geom/mesh.hpp> | |||
#include <stdexcept> | |||
namespace geom { | |||
mesh::mesh(const mesh& other) | |||
{ | |||
*this = other; | |||
} | |||
mesh::~mesh() | |||
{ | |||
clear(); | |||
} | |||
mesh& mesh::operator=(const mesh& other) | |||
{ | |||
// Clear the mesh | |||
clear(); | |||
// Resize vertices, edges, and faces | |||
vertices.resize(other.vertices.size()); | |||
edges.resize(other.edges.size()); | |||
faces.resize(other.faces.size()); | |||
// Allocate vertices | |||
for (std::size_t i = 0; i < vertices.size(); ++i) | |||
vertices[i] = new vertex(); | |||
// Allocate edges | |||
for (std::size_t i = 0; i < edges.size(); ++i) | |||
{ | |||
edges[i] = new edge(); | |||
edges[i]->symmetric = new edge(); | |||
edges[i]->symmetric->symmetric = edges[i]; | |||
} | |||
// Allocate faces | |||
for (std::size_t i = 0; i < faces.size(); ++i) | |||
faces[i] = new face(); | |||
// Copy vertices | |||
for (std::size_t i = 0; i < vertices.size(); ++i) | |||
{ | |||
vertex* va = vertices[i]; | |||
const vertex* vb = other.vertices[i]; | |||
va->index = vb->index; | |||
va->position = vb->position; | |||
va->edge = nullptr; | |||
if (vb->edge) | |||
{ | |||
va->edge = edges[vb->edge->index]; | |||
if (vb->edge != other.edges[vb->edge->index]) | |||
va->edge = va->edge->symmetric; | |||
} | |||
} | |||
// Copy edges | |||
for (std::size_t i = 0; i < edges.size(); ++i) | |||
{ | |||
edge* ea = edges[i]; | |||
const edge* eb = other.edges[i]; | |||
for (std::size_t j = 0; j < 2; ++j) | |||
{ | |||
ea->index = eb->index; | |||
ea->vertex = vertices[eb->vertex->index]; | |||
ea->face = nullptr; | |||
if (eb->face) | |||
ea->face = faces[eb->face->index]; | |||
ea->previous = edges[eb->previous->index]; | |||
if (eb->previous != other.edges[eb->previous->index]) | |||
ea->previous = ea->previous->symmetric; | |||
ea->next = edges[eb->next->index]; | |||
if (eb->next != other.edges[eb->next->index]) | |||
ea->next = ea->next->symmetric; | |||
ea = ea->symmetric; | |||
eb = eb->symmetric; | |||
} | |||
} | |||
// Copy faces | |||
for (std::size_t i = 0; i < faces.size(); ++i) | |||
{ | |||
face* fa = faces[i]; | |||
const face* fb = other.faces[i]; | |||
fa->index = fb->index; | |||
fa->edge = edges[fb->edge->index]; | |||
if (fb->edge != other.edges[fb->edge->index]) | |||
fa->edge = fa->edge->symmetric; | |||
} | |||
return *this; | |||
} | |||
void mesh::clear() noexcept | |||
{ | |||
// Deallocate vertices | |||
for (mesh::vertex* vertex: vertices) | |||
delete vertex; | |||
// Deallocate edges | |||
for (mesh::edge* edge: edges) | |||
{ | |||
delete edge->symmetric; | |||
delete edge; | |||
} | |||
// Deallocate faces | |||
for (mesh::face* face: faces) | |||
delete face; | |||
vertices.clear(); | |||
edges.clear(); | |||
faces.clear(); | |||
} | |||
mesh::vertex* mesh::add_vertex(const float3& position) | |||
{ | |||
mesh::vertex* vertex = new mesh::vertex | |||
{ | |||
vertices.size(), | |||
nullptr, | |||
position | |||
}; | |||
vertices.push_back(vertex); | |||
return vertex; | |||
} | |||
mesh::edge* mesh::add_edge(mesh::vertex* a, mesh::vertex* b) | |||
{ | |||
mesh::edge* ab = new mesh::edge(); | |||
mesh::edge* ba = new mesh::edge(); | |||
ab->index = edges.size(); | |||
ab->vertex = a; | |||
ab->face = nullptr; | |||
ab->previous = ba; | |||
ab->next = ba; | |||
ab->symmetric = ba; | |||
ba->index = edges.size(); | |||
ba->vertex = b; | |||
ba->face = nullptr; | |||
ba->previous = ab; | |||
ba->next = ab; | |||
ba->symmetric = ab; | |||
if (!a->edge) | |||
{ | |||
a->edge = ab; | |||
} | |||
else | |||
{ | |||
mesh::edge* a_in = find_free_incident(a); | |||
mesh::edge* a_out = a_in->next; | |||
a_in->next = ab; | |||
ab->previous = a_in; | |||
ba->next = a_out; | |||
a_out->previous = ba; | |||
} | |||
if (!b->edge) | |||
{ | |||
b->edge = ba; | |||
} | |||
else | |||
{ | |||
mesh::edge* b_in = find_free_incident(b); | |||
mesh::edge* b_out = b_in->next; | |||
b_in->next = ba; | |||
ba->previous = b_in; | |||
ab->next = b_out; | |||
b_out->previous = ab; | |||
} | |||
// Add edge | |||
edges.push_back(ab); | |||
return ab; | |||
} | |||
mesh::face* mesh::add_face(const loop& loop) | |||
{ | |||
if (loop.empty()) | |||
{ | |||
throw std::runtime_error("Empty edge loop"); | |||
} | |||
// Validate edge loop | |||
for (std::size_t i = 0; i < loop.size(); ++i) | |||
{ | |||
mesh::edge* current = loop[i]; | |||
mesh::edge* next = loop[(i + 1) % loop.size()]; | |||
if (current->symmetric->vertex != next->vertex) | |||
{ | |||
// Disconnected edge loop | |||
throw std::runtime_error("Disconnected edge loop"); | |||
} | |||
if (current->face) | |||
{ | |||
// This edge already has a face | |||
throw std::runtime_error("Non-manifold mesh 1"); | |||
} | |||
} | |||
// Make edges adjacent | |||
for (std::size_t i = 0; i < loop.size(); ++i) | |||
{ | |||
if (!make_adjacent(loop[i], loop[(i + 1) % loop.size()])) | |||
{ | |||
throw std::runtime_error("Non-manifold mesh 2"); | |||
} | |||
} | |||
// Create face | |||
mesh::face* face = new mesh::face(); | |||
face->edge = loop[0]; | |||
face->index = faces.size(); | |||
// Add face | |||
faces.push_back(face); | |||
// Connect edges to the face | |||
for (mesh::edge* edge: loop) | |||
{ | |||
edge->face = face; | |||
} | |||
return face; | |||
} | |||
void mesh::remove_face(mesh::face* face) | |||
{ | |||
// Nullify pointers to this face | |||
mesh::edge* edge = face->edge; | |||
do | |||
{ | |||
edge->face = nullptr; | |||
edge = edge->next; | |||
} | |||
while (edge != face->edge); | |||
// Adjust indices of faces after this face | |||
for (std::size_t i = face->index + 1; i < faces.size(); ++i) | |||
{ | |||
--faces[i]->index; | |||
} | |||
// Remove face from the faces vector | |||
faces.erase(faces.begin() + face->index); | |||
// Deallocate face | |||
delete face; | |||
} | |||
void mesh::remove_edge(mesh::edge* edge) | |||
{ | |||
mesh::edge* ab = edge; | |||
mesh::edge* ba = edge->symmetric; | |||
mesh::vertex* a = ab->vertex; | |||
mesh::edge* a_in = ab->previous; | |||
mesh::edge* a_out = ba->next; | |||
mesh::vertex* b = ba->vertex; | |||
mesh::edge* b_in = ba->previous; | |||
mesh::edge* b_out = ab->next; | |||
// Remove dependent faces | |||
if (ab->face) | |||
remove_face(ab->face); | |||
if (ba->face) | |||
remove_face(ba->face); | |||
// Re-link edges | |||
if (a->edge == ab) | |||
a->edge = (a_out == ab) ? nullptr : a_out; | |||
if (b->edge == ba) | |||
b->edge = (b_out == ba) ? nullptr : b_out; | |||
a_in->next = a_out; | |||
a_out->previous = a_in; | |||
b_in->next = b_out; | |||
b_out->previous = b_in; | |||
// Adjust indices of edges after this edge | |||
for (std::size_t i = edge->index + 1; i < edges.size(); ++i) | |||
{ | |||
--edges[i]->index; | |||
--edges[i]->symmetric->index; | |||
} | |||
// Remove edge from the edges vector | |||
edges.erase(edges.begin() + edge->index); | |||
// Deallocate edge | |||
delete edge->symmetric; | |||
delete edge; | |||
} | |||
void mesh::remove_vertex(mesh::vertex* vertex) | |||
{ | |||
// Remove connected edges | |||
if (vertex->edge) | |||
{ | |||
mesh::edge* current = nullptr; | |||
mesh::edge* next = vertex->edge; | |||
do | |||
{ | |||
current = next; | |||
next = next->symmetric->next; | |||
if (next == current) | |||
{ | |||
next = next->symmetric->next; | |||
} | |||
remove_edge(current); | |||
} | |||
while (current != next); | |||
} | |||
// Adjust indices of vertices after this vertex | |||
for (std::size_t i = vertex->index + 1; i < vertices.size(); ++i) | |||
{ | |||
--vertices[i]->index; | |||
} | |||
// Remove vertex from the vertices vector | |||
vertices.erase(vertices.begin() + vertex->index); | |||
// Deallocate vertex | |||
delete vertex; | |||
} | |||
mesh::edge* mesh::find_free_incident(mesh::vertex* vertex) const | |||
{ | |||
mesh::edge* begin = vertex->edge->symmetric; | |||
mesh::edge* current = begin; | |||
do | |||
{ | |||
if (!current->face) | |||
{ | |||
return current; | |||
} | |||
current = current->next->symmetric; | |||
} | |||
while (current != begin); | |||
return nullptr; | |||
} | |||
mesh::edge* mesh::find_free_incident(mesh::edge* start_edge, mesh::edge* end_edge) const | |||
{ | |||
if (start_edge == end_edge) | |||
{ | |||
return nullptr; | |||
} | |||
mesh::edge* current = start_edge; | |||
do | |||
{ | |||
if (!current->face) | |||
{ | |||
return current; | |||
} | |||
current = current->next->symmetric; | |||
} | |||
while (current != end_edge); | |||
return nullptr; | |||
} | |||
bool mesh::make_adjacent(mesh::edge* in, mesh::edge* out) | |||
{ | |||
if (in->next == out) | |||
{ | |||
return true; | |||
} | |||
mesh::edge* b = in->next; | |||
mesh::edge* d = out->previous; | |||
mesh::edge* g = find_free_incident(out->symmetric, in); | |||
if (!g) | |||
{ | |||
return false; | |||
} | |||
mesh::edge* h = g->next; | |||
in->next = out; | |||
out->previous = in; | |||
g->next = b; | |||
b->previous = g; | |||
d->next = h; | |||
h->previous = d; | |||
return true; | |||
} | |||
} // namespace geom |
@ -1,216 +0,0 @@ | |||
/* | |||
* Copyright (C) 2023 Christopher J. Howard | |||
* | |||
* This file is part of Antkeeper source code. | |||
* | |||
* Antkeeper source code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* Antkeeper source code is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#ifndef ANTKEEPER_GEOM_MESH_HPP | |||
#define ANTKEEPER_GEOM_MESH_HPP | |||
#include <engine/utility/fundamental-types.hpp> | |||
#include <vector> | |||
namespace geom { | |||
/** | |||
* Half-edge mesh. | |||
* | |||
* @see http://kaba.hilvi.org/homepage/blog/halfedge/halfedge.htm | |||
*/ | |||
class mesh | |||
{ | |||
public: | |||
struct vertex; | |||
struct edge; | |||
struct face; | |||
/** | |||
* Half-edge mesh vertex, containing its index, a pointer to its parent edge, and its position vector. | |||
*/ | |||
struct vertex | |||
{ | |||
/// Index of this vertex. | |||
std::size_t index; | |||
/// Pointer to the edge to which this vertex belongs. | |||
mesh::edge* edge; | |||
/// Vertex position. | |||
float3 position; | |||
}; | |||
/** | |||
* Half-edge mesh edge, containing its index and pointers to its starting vertex, parent face, and related edges. | |||
*/ | |||
struct edge | |||
{ | |||
/// Index of this edge. | |||
std::size_t index; | |||
/// Pointer to the vertex at which the edge starts. | |||
mesh::vertex* vertex; | |||
/// Pointer to the face on the left of this edge. | |||
mesh::face* face; | |||
/// Pointer to the previous edge in the parent face. | |||
mesh::edge* previous; | |||
/// Pointer to the next edge in the parent face. | |||
mesh::edge* next; | |||
/// Pointer to the symmetric edge. | |||
mesh::edge* symmetric; | |||
}; | |||
/** | |||
* Half-edge mesh face, containing its index and a pointer to its first edge. | |||
*/ | |||
struct face | |||
{ | |||
/// Index of this face. | |||
std::size_t index; | |||
/// Pointer to the first edge in this face. | |||
mesh::edge* edge; | |||
}; | |||
/** | |||
* List of edges which form a face. | |||
*/ | |||
typedef std::vector<edge*> loop; | |||
/** | |||
* Constructs a mesh. | |||
*/ | |||
constexpr mesh() noexcept = default; | |||
/** | |||
* Copy-constructs a mesh. | |||
*/ | |||
mesh(const mesh& other); | |||
/** | |||
* Destructs a mesh. | |||
*/ | |||
~mesh() noexcept; | |||
/** | |||
* Copies another mesh into this mesh. | |||
* | |||
* @param other Mesh to copy. | |||
* | |||
* @return Reference to this mesh. | |||
*/ | |||
mesh& operator=(const mesh& other); | |||
/** | |||
* Removes all vertices, edges, and faces from the mesh. | |||
*/ | |||
void clear() noexcept; | |||
/** | |||
* Adds a vertex to the mesh. | |||
* | |||
* @param position Position of the vertex. | |||
* | |||
* @return Pointer to the added vertex. | |||
* | |||
* @warning The added vertex will initially have a null edge. | |||
*/ | |||
mesh::vertex* add_vertex(const float3& position); | |||
/** | |||
* Adds two half edges to the mesh. | |||
* | |||
* @param a Vertex from which the edge originates. | |||
* @param b Vertex at which the edge ends. | |||
* | |||
* @return Pointer to the half edge `ab`. The symmetric half edge `ba` can be accessed through `ab->symmetric`. | |||
*/ | |||
mesh::edge* add_edge(mesh::vertex* a, mesh::vertex* b); | |||
/** | |||
* Adds a face to the mesh. | |||
* | |||
* @param loop List of edges which form the face. | |||
* @return Pointer to the added face. | |||
* | |||
* @exception std::runtime_error Empty edge loop. | |||
* @exception std::runtime_error Disconnected edge loop. | |||
* @exception std::runtime_error Non-manifold mesh 1. | |||
* @exception std::runtime_error Non-manifold mesh 2. | |||
*/ | |||
mesh::face* add_face(const loop& loop); | |||
/** | |||
* Removes a face from the mesh. | |||
* | |||
* @param face Face to be removed. This face will be deallocated after removal. | |||
*/ | |||
void remove_face(mesh::face* face); | |||
/** | |||
* Removes an edge and all dependent faces from the mesh. | |||
* | |||
* @param edge Edge to be removed. This edge will be deallocated after removal. | |||
*/ | |||
void remove_edge(mesh::edge* edge); | |||
/** | |||
* Removes a vertex, all dependent edges, and all dependent faces from the mesh. | |||
* | |||
* @param vertex Vertex to be removed. This vertex will be deallocated after removal. | |||
*/ | |||
void remove_vertex(mesh::vertex* vertex); | |||
/// Returns the mesh vertices. | |||
const std::vector<mesh::vertex*>& get_vertices() const; | |||
/// Returns the mesh edges. | |||
const std::vector<mesh::edge*>& get_edges() const; | |||
/// Returns the mesh faces. | |||
const std::vector<mesh::face*>& get_faces() const; | |||
private: | |||
mesh::edge* find_free_incident(mesh::vertex* vertex) const; | |||
mesh::edge* find_free_incident(mesh::edge* start_edge, mesh::edge* end_edge) const; | |||
bool make_adjacent(mesh::edge* in_edge, mesh::edge* out_edge); | |||
std::vector<mesh::vertex*> vertices; | |||
std::vector<mesh::edge*> edges; | |||
std::vector<mesh::face*> faces; | |||
}; | |||
inline const std::vector<mesh::vertex*>& mesh::get_vertices() const | |||
{ | |||
return vertices; | |||
} | |||
inline const std::vector<mesh::edge*>& mesh::get_edges() const | |||
{ | |||
return edges; | |||
} | |||
inline const std::vector<mesh::face*>& mesh::get_faces() const | |||
{ | |||
return faces; | |||
} | |||
} // namespace geom | |||
#endif // ANTKEEPER_GEOM_MESH_HPP |
@ -1,102 +0,0 @@ | |||
/* | |||
* Copyright (C) 2023 Christopher J. Howard | |||
* | |||
* This file is part of Antkeeper source code. | |||
* | |||
* Antkeeper source code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* Antkeeper source code is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#include <engine/geom/meshes/grid.hpp> | |||
#include <array> | |||
#include <cmath> | |||
#include <map> | |||
namespace geom { | |||
namespace meshes { | |||
std::unique_ptr<geom::mesh> grid_xy(float length, std::size_t subdivisions_x, std::size_t subdivisions_y) | |||
{ | |||
// Allocate new mesh | |||
std::unique_ptr<geom::mesh> mesh = std::make_unique<geom::mesh>(); | |||
// Determine vertex count and placement | |||
std::size_t columns = subdivisions_x + 1; | |||
std::size_t rows = subdivisions_y + 1; | |||
float column_length = length / static_cast<float>(columns); | |||
float row_length = length / static_cast<float>(rows); | |||
// Generate mesh vertices | |||
float3 position; | |||
position.z() = 0.0f; | |||
position.y() = -length * 0.5f; | |||
for (std::size_t i = 0; i <= rows; ++i) | |||
{ | |||
position.x() = -length * 0.5f; | |||
for (std::size_t j = 0; j <= columns; ++j) | |||
{ | |||
mesh->add_vertex(position); | |||
position.x() += column_length; | |||
} | |||
position.y() += row_length; | |||
} | |||
// Function to eliminate duplicate edges | |||
std::map<std::array<std::size_t, 2>, geom::mesh::edge*> edge_map; | |||
auto add_or_find_edge = [&](geom::mesh::vertex* start, geom::mesh::vertex* end) -> geom::mesh::edge* | |||
{ | |||
geom::mesh::edge* edge; | |||
if (auto it = edge_map.find({start->index, end->index}); it != edge_map.end()) | |||
{ | |||
edge = it->second; | |||
} | |||
else | |||
{ | |||
edge = mesh->add_edge(start, end); | |||
edge_map[{start->index, end->index}] = edge; | |||
edge_map[{end->index, start->index}] = edge->symmetric; | |||
} | |||
return edge; | |||
}; | |||
// Connect vertices with edges and faces | |||
const std::vector<geom::mesh::vertex*>& vertices = mesh->get_vertices(); | |||
for (std::size_t i = 0; i < rows; ++i) | |||
{ | |||
for (std::size_t j = 0; j < columns; ++j) | |||
{ | |||
geom::mesh::vertex* a = vertices[i * (columns + 1) + j]; | |||
geom::mesh::vertex* b = vertices[(i + 1) * (columns + 1) + j]; | |||
geom::mesh::vertex* c = vertices[i * (columns + 1) + j + 1]; | |||
geom::mesh::vertex* d = vertices[(i + 1) * (columns + 1) + j + 1]; | |||
// a---c | |||
// | | | |||
// b---d | |||
geom::mesh::edge* ab = add_or_find_edge(a, b); | |||
geom::mesh::edge* bd = add_or_find_edge(b, d); | |||
geom::mesh::edge* dc = add_or_find_edge(d, c); | |||
geom::mesh::edge* ca = add_or_find_edge(c, a); | |||
mesh->add_face({ab, bd, dc, ca}); | |||
} | |||
} | |||
return mesh; | |||
} | |||
} // namespace meshes | |||
} // namespace geom |
@ -1,217 +0,0 @@ | |||
/* | |||
* Copyright (C) 2023 Christopher J. Howard | |||
* | |||
* This file is part of Antkeeper source code. | |||
* | |||
* Antkeeper source code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* Antkeeper source code is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#ifndef ANTKEEPER_MATH_GLSL_HPP | |||
#define ANTKEEPER_MATH_GLSL_HPP | |||
#include <engine/math/vector.hpp> | |||
#include <engine/math/matrix.hpp> | |||
#include <engine/math/quaternion.hpp> | |||
namespace math { | |||
namespace glsl { | |||
/** | |||
* Linear algebra data types with GLSL naming conventions. | |||
*/ | |||
namespace types | |||
{ | |||
/// @name Vector types | |||
/// @{ | |||
/** | |||
* *n*-dimensional vector of booleans. | |||
* | |||
* @tparam N Number of elements | |||
*/ | |||
/// @{ | |||
template <std::size_t N> | |||
using bvec = math::vector<bool, N>; | |||
using bvec2 = bvec<2>; | |||
using bvec3 = bvec<3>; | |||
using bvec4 = bvec<4>; | |||
/// @} | |||
/** | |||
* *n*-dimensional vector of signed integers. | |||
* | |||
* @tparam N Number of elements | |||
*/ | |||
/// @{ | |||
template <std::size_t N> | |||
using ivec = math::vector<int, N>; | |||
using ivec2 = ivec<2>; | |||
using ivec3 = ivec<3>; | |||
using ivec4 = ivec<4>; | |||
/// @} | |||
/** | |||
* *n*-dimensional vector of unsigned integers. | |||
* | |||
* @tparam N Number of elements | |||
*/ | |||
/// @{ | |||
template <std::size_t N> | |||
using uvec = math::vector<unsigned int, N>; | |||
using uvec2 = uvec<2>; | |||
using uvec3 = uvec<3>; | |||
using uvec4 = uvec<4>; | |||
/// @} | |||
/** | |||
* *n*-dimensional vector of floating-point numbers. | |||
* | |||
* @tparam N Number of elements | |||
*/ | |||
/// @{ | |||
template <std::size_t N> | |||
using fvec = math::vector<float, N>; | |||
using fvec2 = fvec<2>; | |||
using fvec3 = fvec<3>; | |||
using fvec4 = fvec<4>; | |||
using vec2 = fvec2; | |||
using vec3 = fvec3; | |||
using vec4 = fvec4; | |||
/// @} | |||
/** | |||
* *n*-dimensional vector of double-precision floating-point numbers. | |||
* | |||
* @tparam N Number of elements | |||
*/ | |||
/// @{ | |||
template <std::size_t N> | |||
using dvec = math::vector<double, N>; | |||
using dvec2 = dvec<2>; | |||
using dvec3 = dvec<3>; | |||
using dvec4 = dvec<4>; | |||
/// @} | |||
/// @} | |||
/// @name Matrix types | |||
/// @{ | |||
/** | |||
* *n* by *m* matrix of floating-point numbers. | |||
* | |||
* @tparam N Number of columns. | |||
* @tparam M Number of rows. | |||
*/ | |||
/// @{ | |||
template <std::size_t N, std::size_t M> | |||
using fmat = math::matrix<float, N, M>; | |||
using fmat2x2 = fmat<2, 2>; | |||
using fmat2x3 = fmat<2, 3>; | |||
using fmat2x4 = fmat<2, 4>; | |||
using fmat3x2 = fmat<3, 2>; | |||
using fmat3x3 = fmat<3, 3>; | |||
using fmat3x4 = fmat<3, 4>; | |||
using fmat4x2 = fmat<4, 2>; | |||
using fmat4x3 = fmat<4, 3>; | |||
using fmat4x4 = fmat<4, 4>; | |||
using mat2x2 = fmat2x2; | |||
using mat2x3 = fmat2x3; | |||
using mat2x4 = fmat2x4; | |||
using mat3x2 = fmat3x2; | |||
using mat3x3 = fmat3x3; | |||
using mat3x4 = fmat3x4; | |||
using mat4x2 = fmat4x2; | |||
using mat4x3 = fmat4x3; | |||
using mat4x4 = fmat4x4; | |||
/// @} | |||
/** | |||
* *n* by *n* square matrix of floating-point numbers. | |||
* | |||
* @tparam N Number of columns and rows. | |||
*/ | |||
/// @{ | |||
using fmat2 = fmat2x2; | |||
using fmat3 = fmat3x3; | |||
using fmat4 = fmat4x4; | |||
using mat2 = fmat2; | |||
using mat3 = fmat3; | |||
using mat4 = fmat4; | |||
/// @} | |||
/** | |||
* *n* by *m* matrix of double-precision floating-point numbers. | |||
* | |||
* @tparam N Number of columns. | |||
* @tparam M Number of rows. | |||
*/ | |||
/// @{ | |||
template <std::size_t N, std::size_t M> | |||
using dmat = math::matrix<double, N, M>; | |||
using dmat2x2 = dmat<2, 2>; | |||
using dmat2x3 = dmat<2, 3>; | |||
using dmat2x4 = dmat<2, 4>; | |||
using dmat3x2 = dmat<3, 2>; | |||
using dmat3x3 = dmat<3, 3>; | |||
using dmat3x4 = dmat<3, 4>; | |||
using dmat4x2 = dmat<4, 2>; | |||
using dmat4x3 = dmat<4, 3>; | |||
using dmat4x4 = dmat<4, 4>; | |||
/// @} | |||
/** | |||
* *n* by *n* square matrix of double-precision floating-point numbers. | |||
* | |||
* @tparam N Number of columns and rows. | |||
*/ | |||
/// @{ | |||
using dmat2 = dmat2x2; | |||
using dmat3 = dmat3x3; | |||
using dmat4 = dmat4x4; | |||
/// @} | |||
/// @} | |||
/// @name Quaternion types | |||
/// @{ | |||
/** | |||
* Quaternion with floating-point scalars. | |||
* | |||
* @tparam T Scalar type. | |||
*/ | |||
/// @{ | |||
using fquat = math::quaternion<float>; | |||
using quat = fquat; | |||
/// @} | |||
/** | |||
* Quaternion with double-precision floating-point scalars. | |||
* | |||
* @tparam T Scalar type. | |||
*/ | |||
using dquat = math::quaternion<double>; | |||
/// @} | |||
} // namespace types | |||
// Bring GLSL types into glsl namespace | |||
using namespace types; | |||
} // namespace glsl | |||
} // namespace math | |||
#endif // ANTKEEPER_MATH_GLSL_HPP |
@ -0,0 +1,36 @@ | |||
/* | |||
* Copyright (C) 2023 Christopher J. Howard | |||
* | |||
* This file is part of Antkeeper source code. | |||
* | |||
* Antkeeper source code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* Antkeeper source code is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#ifndef ANTKEEPER_MATH_TYPES_HPP | |||
#define ANTKEEPER_MATH_TYPES_HPP | |||
namespace math { | |||
/** | |||
* Linear algebra data types. | |||
*/ | |||
namespace types {} | |||
} // namespace math | |||
#include <engine/math/matrix.hpp> | |||
#include <engine/math/quaternion.hpp> | |||
#include <engine/math/vector.hpp> | |||
#endif // ANTKEEPER_MATH_TYPES_HPP |