Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
545 changes: 422 additions & 123 deletions src/engine/utils/meshloader.cpp

Large diffs are not rendered by default.

91 changes: 77 additions & 14 deletions src/engine/utils/meshloader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,30 @@
#include <array>
#include <optional>
#include <vector>
#include <unordered_map>
#include <string>
#include <concepts>

namespace utils
{

/**
namespace utils {
/**
* @brief Collection of raw mesh data.
* triangle data can be found in: faces
*/
struct MeshData
struct MeshData
{
using Handle = const MeshData*;
using group_t = uint32_t;

/**
* @brief Collection of vertices describing one face.
* @brief Collection of vertices describing one face.
* contains 3 ordered vertices
*/
struct FaceData
struct FaceData
{
/**
* @brief Collection of ids describing one vertex.
* @brief Collection of ids describing one vertex.
*/
struct VertexIndices
struct VertexIndices
{
int positionIdx;
///< id to fetch the position from MeshData::positions
Expand All @@ -38,25 +43,83 @@ namespace utils
///< may be not set for a vertex
};
std::array<VertexIndices, 3> indices;
group_t groups;
};

using Handle = const MeshData*;


/**
* @brief load mesh data from an file
* @param _fileName path to file
* @brief load mesh data from an file
* @param _fileName path to file
*/
static Handle load( const char* _fileName );
static Handle load( const char* _fileName ) ;

static void unload( Handle _meshData );

/// list of all positions contains values used by FaceData
std::vector<glm::vec3> positions;
std::vector<glm::vec2> textureCoordinates;
std::vector<glm::vec3> normals;
/// list of all faces described in .obj in order
std::vector<FaceData> faces;

struct MaterialData {
struct {
glm::vec3 ambient;
glm::vec3 diffuse;
glm::vec3 spectral;
} color;
struct TextureData {
glm::vec3 offset;
glm::vec3 scale;
bool clamp;
std::string name;
};
struct {
TextureData ambient;
TextureData diffuse;
TextureData spectral;
TextureData spectralExponent;
TextureData reflection;
} textures;
float spectralExponent;
float opacity;
enum USED_LIGHT {
DIFFUSE_ONLY,
DIFFUSE_AND_AMBIENT,
FULL,
END
} illumination;
};
struct ObjectData {
std::string name;
size_t begin; ///< first face part of Object
size_t end; ///< first face not part of Object
size_t materialIdx; ///< material of Object
};
std::vector<MaterialData> material;
std::vector<ObjectData> objects;

std::unordered_map<std::string,group_t> group_names;
std::unordered_map<std::string,size_t> material_names;

using VertexPosition = glm::vec3;
struct VertexTexture {
glm::vec3 position;
glm::vec2 texture;
};
struct VertexNormal {
glm::vec3 position;
glm::vec3 normal;
};
struct VertexTextureNormal {
glm::vec3 position;
glm::vec3 normal;
glm::vec2 texture;
};

};


using MeshLoader = utils::ResourceManager<MeshData>;

} // end utils
269 changes: 269 additions & 0 deletions src/engine/utils/triangulation.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
#include "./triangulation.hpp"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove "./"



#include <glm/mat3x3.hpp>
#include <glm/vec2.hpp>
#include <glm/geometric.hpp>

#include <tuple>
#include <optional>
#include <array>
#include <cassert>

enum Orientation : char {NONE = 0b00, POSITIVE = 0b01, NEGATIVE = 0b10, STRAIGHT = 0b11};
enum Direction {CURRENT, NEXT, PREVIOUS };

/// calculate orientation factor of three following points
float orientationF(
const glm::vec2& a,
const glm::vec2& b,
const glm::vec2& c)
{
return ((b.x*c.y + a.x*b.y + a.y*c.x) - (a.y*b.x + b.y*c.x + a.x*c.y));
}

/// returns Orientation value
Orientation orientation(
const glm::vec2& a,
const glm::vec2& b,
const glm::vec2& c)
{
float v = orientationF(a, b, c);
return static_cast<Orientation>(
(v <= 0 ? Orientation::NEGATIVE : Orientation::NONE)
| (v >= 0 ? Orientation::POSITIVE : Orientation::NONE));
}

/// checks if p is inside triangle a,b,c
bool isIn(
const glm::vec2& p,
const glm::vec2& a,
const glm::vec2& b,
const glm::vec2& c)
{
Orientation orient[] = {
orientation(a,b,p),
orientation(b,c,p),
orientation(c,a,p)
};
return (orient[0] & Orientation::POSITIVE && orient[1] & Orientation::POSITIVE && orient[2] & Orientation::POSITIVE)
|| (orient[0] & Orientation::NEGATIVE && orient[1] & Orientation::NEGATIVE && orient[2] & Orientation::NEGATIVE);
}

/// get next valid id in relation of a valid id
int getId (
std::vector<std::optional<glm::vec2>>& data,
int id,
Direction dir = Direction::CURRENT) {
if(dir == Direction::PREVIOUS)
{
const std::optional<glm::vec2>* res;
do {
res = id == 0 // underflow check
? &data[id = data.size() - 1]
: &data[--id];
} while(*res == std::nullopt);

} else if (dir == Direction::NEXT){
const std::optional<glm::vec2>* res;
do {
res = (++id == static_cast<int>(data.size())) // overflow check
? &data[id = 0]
: &data[id];
} while(*res == std::nullopt);
}
return id;
};

/// get next vertex in relation of current vertex
const glm::vec2& get(
std::vector<std::optional<glm::vec2>>& data,
int id,
Direction dir = Direction::CURRENT)
{
return *data[getId(data, id, dir)];
}

/// runs fun on each triplet of vertices
/** \param fun callable object with arguments: itr to current vertex, vertex before, vertex, vertex behind
* \attention only works when still all vertices are set
*/
void slide(auto data, auto fun)
{
auto itr = data.begin();
fun(itr - data.begin(), *data.back(), *itr[0], *itr[1]);
++itr;
for(;itr != data.end() - 1; ++itr) {
fun(itr - data.begin(), *itr[-1], *itr[0], *itr[1]);
}
fun(itr - data.begin(), *data.end()[-2], *data.back(), *data.front());
}

/// calculate primary orientation of polygone
Orientation determineMainOrientation(auto data) {
Comment on lines +91 to +103
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

curly brackets

float sum = 0;
slide(data, [&sum](auto itr, auto a, auto b, auto c) {
sum += orientationF(a, b, c);
});
assert(sum != 0);
return sum < 0 ? Orientation::NEGATIVE : Orientation::POSITIVE;
}

class EarCutTriangulation {
public:
EarCutTriangulation(const std::vector<glm::vec3>& polygone) : m_polygone{polygone} {}
std::vector<int> operator()();
private:
void insertReflex(int i) { m_types.insert(m_types.begin(), i); ++m_reflexEnd; }
void insertConvex(int i) { m_types.push_back(i); }
void promoteReflex(int i); ///< moves convex to reflex vertices

/// searches and for ear vertex and removes it from m_types
/** \return index of ear
* \throws const char* if no ear is found */
int findEar();

/// classifies all vertices between reflex and convex, stores results in m_types,m_reflexEnd
/** \param mainOrientation primary orientation of polygone (needed to determined inside for classification of reflex and convex */
void classifyVertices(Orientation mainOrientation);

const std::vector<glm::vec3>& m_polygone;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

polygone -> polygon

std::vector<std::optional<glm::vec2>> m_projected{}; ///< polygone projected to planar coordinate system, optional to remove vertices without break indices
std::vector<int> m_types{}; ///< polygone types[0,m_reflexEnd[ reflex vertices, types[m_reflexEnd,-1] convex vertices
int m_reflexEnd = 0;
};

void EarCutTriangulation::promoteReflex(int i)
{
--m_reflexEnd;
auto buff = m_types[m_reflexEnd];
m_types[m_reflexEnd] = m_types[i];
m_types[i] = buff;
}


int EarCutTriangulation::findEar()
{ // check for each convex vertex if non reflex vertex is inside it's ear
for (auto itr = m_types.begin() + m_reflexEnd;
itr != m_types.end(); ++itr)
{
bool isEar = true;
if (m_projected[*itr])
{
for (auto refl = m_types.begin();
refl != m_types.begin() + m_reflexEnd; ++refl)
{
int ids[] = {
getId(m_projected, *refl),
getId(m_projected, *itr, Direction::PREVIOUS),
getId(m_projected, *itr),
getId(m_projected, *itr, Direction::NEXT)
};
if (ids[1] != ids[0] && ids[2] != ids[0] && ids[3] != ids[0] // check if ear is part of the triangle
&& isIn(
*m_projected[ids[0]],
*m_projected[ids[1]],
*m_projected[ids[2]],
*m_projected[ids[3]]))
{
isEar = false; break;
}
}
}
if (isEar)
{
int ear = *itr;
m_types.erase(itr);
return ear;
}
}
throw "no ear found!";
};

void EarCutTriangulation::classifyVertices(Orientation mainOrientation)
{
slide(m_projected, [&](auto i, auto a, auto b, auto c) {
if(orientation(a, b, c) & mainOrientation) {
insertConvex(i);
} else {
insertReflex(i);
}
});
}

namespace utils {

std::vector<int> triangulateEarCut(const std::vector<glm::vec3>& polygone) {
return EarCutTriangulation(polygone)();
}
} /// namespace utils

std::vector<int> EarCutTriangulation::operator()() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

curly bracket position

glm::vec3 x,y; ///< orthonormal base for planar coordinate system
int i = 0;
do { // find 3 vertices which not on a line
x = glm::normalize(m_polygone[i+1] - m_polygone[i]),
y = glm::normalize(m_polygone[i+2] - m_polygone[i+1]);
++i;
} while (glm::dot(x-y,x-y) < 0.0001f);

glm::vec3 up = glm::normalize(glm::cross(x,y));
y = glm::normalize(glm::cross(up, x));

m_projected.reserve(m_polygone.size());
for (const glm::vec3& v : m_polygone)
{
m_projected.push_back({{glm::dot(x,v), glm::dot(y,v)}});
}

Orientation mainOrientation = determineMainOrientation(m_projected);
classifyVertices(mainOrientation);

std::vector<int> res{};
size_t finalSize = (m_projected.size() - 2) * 3;
res.reserve(finalSize);
while (res.size() != finalSize)
{
int ear = 0;
try
{
ear = findEar();
}
catch (const char*)
{
res.clear();
return res;
}
int p = getId(m_projected, ear, Direction::PREVIOUS);
int n = getId(m_projected, ear, Direction::NEXT);
res.push_back(p);
res.push_back(ear);
res.push_back(n);

// check if previous vertex was an an reflex an if, if it now an convex
auto match = std::find(m_types.begin(), m_types.end(), p) - m_types.begin();
if (match < m_reflexEnd)
{
int pp = getId(m_projected, p, Direction::PREVIOUS);
if (orientation(*m_projected[pp], *m_projected[p], *m_projected[n]) & mainOrientation)
{
promoteReflex(match);
}
}

// check if next vertex was an an reflex an if, if it now an convex
match = std::find(m_types.begin(), m_types.end(), n) - m_types.begin();
if (match < m_reflexEnd)
{
int nn = getId(m_projected, n, Direction::NEXT);
if (orientation(*m_projected[p], *m_projected[n], *m_projected[nn]) & mainOrientation)
{
promoteReflex(match);
}
}

m_projected[ear] = std::nullopt;
}
return res;
}

Loading