port from perforce

This commit is contained in:
2026-04-18 22:31:51 +02:00
commit 8d0ab5b7cc
8409 changed files with 3972376 additions and 0 deletions

31
meshTools/meshTools.sln Normal file
View File

@@ -0,0 +1,31 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.31101.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "meshTools", "meshTools\meshTools.vcxproj", "{A07570FA-501F-400E-823E-00B5459C7E8D}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test", "test\test.vcxproj", "{6E7DF6F1-481D-41A2-84CD-27B2626BC21D}"
ProjectSection(ProjectDependencies) = postProject
{A07570FA-501F-400E-823E-00B5459C7E8D} = {A07570FA-501F-400E-823E-00B5459C7E8D}
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A07570FA-501F-400E-823E-00B5459C7E8D}.Debug|Win32.ActiveCfg = Debug|Win32
{A07570FA-501F-400E-823E-00B5459C7E8D}.Debug|Win32.Build.0 = Debug|Win32
{A07570FA-501F-400E-823E-00B5459C7E8D}.Release|Win32.ActiveCfg = Release|Win32
{A07570FA-501F-400E-823E-00B5459C7E8D}.Release|Win32.Build.0 = Release|Win32
{6E7DF6F1-481D-41A2-84CD-27B2626BC21D}.Debug|Win32.ActiveCfg = Debug|Win32
{6E7DF6F1-481D-41A2-84CD-27B2626BC21D}.Debug|Win32.Build.0 = Debug|Win32
{6E7DF6F1-481D-41A2-84CD-27B2626BC21D}.Release|Win32.ActiveCfg = Release|Win32
{6E7DF6F1-481D-41A2-84CD-27B2626BC21D}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

BIN
meshTools/meshTools.v12.suo Normal file

Binary file not shown.

View File

@@ -0,0 +1,299 @@
#pragma once
#include "List.h"
#include "Map.h"
#include "Performancer.h"
template<class TKey, class TValue>
struct Pair
{
Pair()
{
}
Pair(TKey key, TValue value)
: Key{ key }
, Value{ value }
{
}
TKey Key;
TValue Value;
};
namespace Linq
{
template<class T>
Map<T, T> ToSet(const List<T>& what)
{
Map<T, T> result;
for (const auto& element : what)
result.Add(element, element);
return result;
}
template<class T>
List<T> ToList(const T& what)
{
List<T> result;
result.Add(what);
return result;
}
template<class T>
List<T> ToList(T array[], size_t size)
{
List<T> result;
for (int i = 0; i < size; ++i)
result.Add(array[i]);
return result;
}
template<class T>
List<T> Concat(const List<T>& a, const List<T>& b)
{
List<T> result(a.Count() + b.Count());
for (auto i = 0; i < a.Count(); ++i)
result.Add(a[i]);
for (auto i = 0; i < b.Count(); ++i)
result.Add(b[i]);
return result;
}
template<class T>
List<T> Reverse(const List<T>& what)
{
List<T> result;
for (auto i = what.Count() - 1; i >= 0; --i)
result.Add(what[i]);
return result;
}
template<class T>
List<T> Distinct(const List<T>& what)
{
List<T> result(what.Count());
Map<T, bool> map;
for (const auto& element : what)
if (!map.ContainsKey(element))
{
map.Add(element, false);
result.Add(element);
}
return result;
}
template<class T>
void AddRange(List<T>& destination, const List<T>& what)
{
for (const auto& element : what)
destination.Add(element);
}
template<class T>
List<T> Except(const List<T>& what, const List<T>& except)
{
List<T> result;
for (const auto& element : what)
if (except.IndexOf(element) == -1)
result.Add(element);
return result;
}
template<class T>
List<T> Union(const List<T>& what, const List<T>& other)
{
List<T> result = what;
Linq::AddRange(result, other);
result = Linq::Distinct(result);
return result;
}
template<class T>
List<T> Intersect(const List<T>& what, const List<T>& other)
{
List<T> result;
for (const auto& element : what)
if (other.IndexOf(element) != -1)
result.Add(element);
return result;
}
template<class T, class TGroupValue, typename Func>
void GroupBy(const List<T>& what, Map<TGroupValue, List<T>>& dest, Func groupByFunc)
{
for (const auto& element : what)
{
auto key = groupByFunc(element);
List<T>* values;
if (dest.TryGetValue(key, values))
values->Add(element);
else
{
List<T> value(1);
value.Add(element);
dest.Add(key, value);
}
}
}
template<class T>
Map<T, int> Group(const List<T>& what)
{
Map<T, int> result;
for (const auto& element : what)
{
int cnt;
if (result.TryGetValue(element, cnt))
result[element] = ++cnt;
else
result.Add(element, 1);
}
return result;
}
template<class T>
Map<T, int> GroupValues(const List<T>& what)
{
Map<T, int> result;
for (const auto& element : what)
{
int cnt;
if (result.TryGetValue(element, cnt))
result[element] = ++cnt;
else
result.Add(element, 1);
}
return result;
}
template<class T, class Func>
T Max(const List<T>& what, Func sortCallback)
{
List<Pair<T, float>> sortData;
int maxId = 0;
float maxSortValue = -Math::Infinity;
for (int i = 0; i < what.Count(); ++i)
{
auto sortValue = sortCallback(what[i]);
if (i == 0 || maxSortValue < sortValue)
{
maxId = i;
maxSortValue = sortValue;
}
}
return what[maxId];
}
template<class T, class Func>
T Min(const List<T>& what, Func sortCallback)
{
List<Pair<T, float>> sortData;
int index = 0;
float sortValue = Math::Infinity;
for (int i = 0; i < what.Count(); ++i)
{
auto currentSortValue = sortCallback(what[i]);
if (i == 0 || sortValue > currentSortValue)
{
index = i;
sortValue = currentSortValue;
}
}
return what[index];
}
template<class T, class Func>
List<T> OrderByDescending(const List<T>& what, Func sortCallback)
{
List<T> result;
List<T> tmp = what;
while (tmp.Count() > 0)
{
auto max = Linq::Max(tmp, sortCallback);
tmp.Remove(max);
result.Add(max);
}
return result;
}
template<class T, class Func>
List<T> Where(const List<T>& what, Func whereCallback)
{
List<T> result;
for (auto& element : what)
if (whereCallback(element))
result.Add(element);
return result;
}
template<class T2, class T, class Func>
List<T2> Select(const List<T>& what, Func selectCallback)
{
List<T2> result;
for (auto& element : what)
result.Add(selectCallback(element));
return result;
}
template<class TResult, class T, class Func>
List<TResult> SelectMany(const List<T>& what, Func selectCallback)
{
List<TResult> result;
for (auto& element : what)
Linq::AddRange<TResult>(result, selectCallback(element));
return result;
}
template<class TResult, class T, class Func>
List<TResult> SelectUniqueMany(const List<T>& what, Func selectCallback)
{
List<TResult> result(what.Count());
Map<TResult, bool> map;
for (auto& element : what)
{
auto subResult = selectCallback(element);
for (const auto& subElement : subResult)
{
if (!map.ContainsKey(subElement))
{
map.Add(subElement, false);
result.Add(subElement);
}
}
}
return result;
}
template<class T, class Func>
bool Any(const List<T>& what, Func callback)
{
for (auto& element : what)
if (callback(element))
return true;
return false;
}
};

View File

@@ -0,0 +1,202 @@
#pragma once
#if _DEBUG
//#include <assert.h>
#define assert(a) if (!(a)) { throw #a; }
#endif
template<class T>
class List;
template<typename TSrc, typename TDest>
struct convert
{
static TDest Convert(const TSrc& srx)
{
static_assert(false, "Conversion is not possible, implement a custom conversion method.");
}
};
template<class T>
class List
{
public:
List(int capacity = 1)
: capacity{ capacity }
, count{0}
, data{ new T[capacity] }
{
}
List(List<T>&& other)
: count{ other.count }
, capacity{ other.capacity }
, data{ other.data }
{
other.count = 0;
other.capacity = 0;
other.data = nullptr;
}
List(const List<T>& other)
: count{other.count}
, capacity{ other.count }
, data{ new T[other.count] }
{
for (int index = 0; index < count; ++index)
data[index] = other.data[index];
}
~List()
{
delete[] data;
data = nullptr;
}
List<T>& operator=(List<T>&& other)
{
if (this != &other)
{
delete[] data;
count = other.count;
capacity = other.capacity;
data = other.data;
other.count = 0;
other.capacity = 0;
other.data = nullptr;
}
return *this;
}
List<T>& operator=(const List<T>& other)
{
count = other.count;
capacity = other.count;
data = new T[other.count];
for (int index = 0; index < count; ++index)
data[index] = other.data[index];
return *this;
}
void Add(const T& value)
{
if (count == capacity)
Grow();
data[count] = value;
count++;
}
void Remove(const T& value)
{
auto index = IndexOf(value);
if (index == -1)
return;
RemoveAt(index);
}
void RemoveAt(int index)
{
#if _DEBUG
assert(index >= 0 && index < count);
#endif
//data[index++].~T();
index++;
for (; index < count; ++index)
data[index - 1] = data[index];
count--;
}
bool Contains(const T& value) const
{
return IndexOf(value) != -1;
}
int IndexOf(const T& value) const
{
int i = 0;
for (; i < count; ++i)
if (data[i] == value)
break;
if (i == count)
return -1;
return i;
}
int Count() const
{
return count;
}
void Clear()
{
/*for (int i = 0; i < count; ++i)
data[i].~T();*/
count = 0;
/*capacity = 1;
delete[] data;
data = new T[capacity];*/
}
T& operator[](int index)
{
#if _DEBUG
assert(index >= 0 && index < count);
#endif
return data[index];
}
const T& operator[](int index) const
{
#if _DEBUG
assert(index >= 0 && index < count);
#endif
return data[index];
}
T* Get() const
{
return data;
}
T* begin() const
{
return data;
}
T* end() const
{
return data + count;
}
template<typename TDest>
operator TDest() const
{
return convert<List<T>, TDest>::Convert(*this);
}
private:
void Grow()
{
auto newCapacity = capacity ? capacity * 2 : 1;
T* newData = new T[newCapacity];
for (int i = 0; i < count; ++i)
newData[i] = data[i]; //newData[i] = (T&&)data[i];
capacity = newCapacity;
delete[] data;
data = newData;
}
private:
T* data;
int count;
int capacity;
};

View File

@@ -0,0 +1,318 @@
#pragma once
#include "List.h"
#if _DEBUG
//#include <assert.h>
#define assert(a) if (!(a)) { throw #a; }
#endif
template<typename T>
struct equality { static bool Equals(T a, T b) { return a == b; } };
template<typename T>
struct hashing
{
static int Generate(T a)
{
return (int)a;
}
};
// Finetune this value for better performance.
#define MAXBUCKETSIZE 10
template<typename TKey, typename TValue>
class Map
{
public:
struct Pair
{
int Hash;
TKey Key;
TValue Value;
};
private:
class HashBucket
{
public:
HashBucket(const List<Pair*> pairs)
: left{ nullptr }
, right{ nullptr }
, pairs { pairs }
{
if (pairs.Count() > 0)
centerHash = pairs[0]->Hash;
}
HashBucket()
: left{ nullptr }
, right{ nullptr }
{
}
~HashBucket()
{
delete left;
delete right;
}
HashBucket* GetValue(const TKey& key, int hash, bool& success, TValue*& result) const
{
if ((hash == centerHash || left == nullptr) && pairs.Count() != 0)
{
for (const auto& pair : pairs)
if (equality<const TKey&>::Equals(pair->Key, key))
{
success = true;
result = &pair->Value;
return nullptr;
}
return nullptr;
}
else if (left != nullptr)
return hash < centerHash ? left : right;
else
return nullptr;
}
HashBucket* Remove(const TKey key, int hash, Pair*& pairResult)
{
if (left == nullptr || hash == centerHash)
{
for (int i = 0, iLength = pairs.Count(); i < iLength; ++i)
{
auto pair = pairs[i];
if (equality<const TKey&>::Equals(pair->Key, key))
{
pairs.RemoveAt(i);
pairResult = pair;
return nullptr;
}
}
return nullptr;
}
else if (left != nullptr)
return hash < centerHash ? left : right;
else
return nullptr;
}
HashBucket* Add(Pair* pair)
{
if (pairs.Count() == 0)
centerHash = pair->Hash;
if (centerHash == pair->Hash || (left == nullptr && pairs.Count() < MAXBUCKETSIZE))
{
#ifdef _DEBUG
for (const auto& oldPair : pairs)
if (equality<const TKey&>::Equals(oldPair->Key, pair->Key))
assert("Key already exists in map" && false);
#endif
pairs.Add(pair);
}
else if (left == nullptr)
{
centerHash = pairs[pairs.Count() / 2]->Hash;
List<Pair*> leftPairs;
List<Pair*> centerPairs;
List<Pair*> rightPairs;
for (const auto& oldPair : pairs)
{
if (oldPair->Hash < centerHash)
leftPairs.Add(oldPair);
if (oldPair->Hash > centerHash)
rightPairs.Add(oldPair);
else
centerPairs.Add(oldPair);
}
left = new HashBucket(leftPairs);
right = new HashBucket(rightPairs);
pairs = centerPairs;
return Add(pair);
}
else
return pair->Hash < centerHash ? left : right;
return nullptr;
}
private:
int centerHash;
HashBucket* left;
HashBucket* right;
List<Pair*> pairs;
};
public:
Map()
: rootBucket{ new HashBucket() }
{
}
Map(Map&& other)
: pairs{ other.pairs }
, rootBucket{ other.rootBucket }
{
other.rootBucket = nullptr;
other.pairs.Clear();
}
Map(const Map& other)
{
throw "Implement me bitch!";
}
~Map()
{
for (int i = 0, iLength = pairs.Count(); i < iLength; ++i)
delete pairs[i];
delete rootBucket;
}
Map& operator=(Map&& other)
{
if (this != &other)
{
delete rootBucket;
pairs = other.pairs;
rootBucket = other.rootBucket;
other.rootBucket = nullptr;
other.pairs.Clear();
}
return *this;
}
void Add(const TKey& key, const TValue& value)
{
auto pair = new Pair();
pair->Key = key;
pair->Value = value;
pair->Hash = hashing<const TKey&>::Generate(key);
auto bucket = rootBucket;
do
{
bucket = bucket->Add(pair);
} while (bucket != nullptr);
pairs.Add(pair);
}
bool TryGetValue(const TKey& key, TValue& value) const
{
bool success = false;
auto hash = hashing<const TKey&>::Generate(key);
TValue* result;
auto bucket = rootBucket;
do
{
bucket = bucket->GetValue(key, hash, success, result);
} while (bucket != nullptr);
if (success)
value = *result;
return success;
}
bool TryGetValue(const TKey& key, TValue*& value) const
{
bool success = false;
auto hash = hashing<const TKey&>::Generate(key);
TValue* result;
auto bucket = rootBucket;
do
{
bucket = bucket->GetValue(key, hash, success, result);
} while (bucket != nullptr);
if (success)
value = result;
return success;
}
void Remove(const TKey& key)
{
auto hash = hashing<const TKey&>::Generate(key);
auto bucket = rootBucket;
Pair* pair;
do
{
bucket = bucket->Remove(key, hash, pair);
} while (bucket != nullptr);
if (pair != nullptr)
{
pairs.Remove(pair);
}
}
TValue& GetValue(const TKey& key) const
{
TValue* result;
bool success = false;
auto hash = hashing<const TKey&>::Generate(key);
auto bucket = rootBucket;
do
{
bucket = bucket->GetValue(key, hash, success, result);
} while (bucket != nullptr);
#if _DEBUG
assert("No value found!" && success);
#endif
return *result;
}
Pair** begin() const
{
return pairs.begin();
}
Pair** end() const
{
return pairs.end();
}
int Count() const
{
return pairs.Count();
}
bool ContainsKey(const TKey& key) const
{
TValue tmp;
return TryGetValue(key, tmp);
}
TValue& operator[](const TKey& key)
{
return GetValue(key);
}
const TValue& operator[](const TKey& key) const
{
return GetValue(key);
}
List<TValue> GetValues() const
{
List<TValue> result;
for (const auto& pair : pairs)
result.Add(pair->Value);
return result;
}
private:
HashBucket* rootBucket;
List<Pair*> pairs;
};

View File

@@ -0,0 +1,74 @@
#pragma once
#ifdef _DEBUG
#include <vector>
#include <algorithm>
#include <sstream>
class PerformancerData
{
public:
PerformancerData(const std::string& name, const std::string& fullName);
void Add(long long duration);
std::string Name;
std::string FullName;
long long Calls;
long long Elapsed;
long long Max;
long long Min;
};
#include <chrono>
struct HighResClock
{
typedef long long rep;
typedef std::nano period;
typedef std::chrono::duration<rep, period> duration;
typedef std::chrono::time_point<HighResClock> time_point;
static const bool is_steady = true;
static time_point now();
};
class Performancer
{
public:
int RegisterMethod(const std::string& name, const std::string& fullName);
void AddData(int index, long long duration);
void Dump();
static Performancer Instance;
private:
std::vector<PerformancerData> data;
};
class PerformancerEntry
{
public:
PerformancerEntry(int index);
~PerformancerEntry();
private:
std::chrono::time_point<HighResClock> start;
int index;
};
#define PERFORMANCER \
static int __performancerIndex = Performancer::Instance.RegisterMethod( __FUNCTION__, __FUNCSIG__); \
PerformancerEntry __performancerEntry(__performancerIndex);
#define PERFORMANCER_DUMP Performancer::Instance.Dump();
#else
#define PERFORMANCER
#define PERFORMANCER_DUMP
#endif

View File

@@ -0,0 +1,6 @@
#pragma once
#include "Linq.h"
#include "Map.h"
#include "List.h"
#include "Performancer.h"

View File

@@ -0,0 +1,30 @@
#pragma once
#include "../common/common.h"
#include "../math/math.h"
#include "typedefs.h"
class Edge
{
friend class Mesh;
public:
Edge();
Edge(Vertex* a, Vertex* b);
~Edge();
Vertex* GetOtherVertex(Vertex* vertex) const;
const VertexList& GetVertices() const;
const FaceList& GetFaces() const;
EdgeList GetEdges() const;
vec3 GetDirection() const;
vec3 GetCenter() const;
float GetLength() const;
Edge* GetHalfEdge(Face* face) const;
private:
VertexList vertices;
FaceList faces;
Mesh* mesh;
};

View File

@@ -0,0 +1,66 @@
#pragma once
#include "../common/common.h"
#include "../math/math.h"
#include "typedefs.h"
#include "Triangle.h"
class Vertex;
class UVGeneratorBase;
class Face
{
friend class Mesh;
public:
Face();
Face(Vertex* a, Vertex* b, Vertex* c, Vertex* d = nullptr);
Face(const VertexList& vertices);
~Face();
vec3 GetNormal() const;
Plane GetPlane() const;
bool IsTriangle() const;
void FlipNormal();
void FlipNormal(const vec3& dir);
FaceList Triangulate();
void FlipEdge();
bool IsDegenerated() const;
bool RegainIntegrity();
const VertexList& GetVertices() const;
FaceList GetFaces() const;
const EdgeList& GetEdges() const;
bool HasTag(int tag) const;
bool HasTagsAny(const List<int>& tagsToCheck) const;
bool HasTagsAll(const List<int>& tagsToCheck) const;
void AddTag(int tag);
void RemoveTag(int tag);
List<int> GetTags() const;
void SetMaterialId(int materialId);
int GetMaterialId() const;
void SetUVGenerator(UVGeneratorBase* generator);
UVGeneratorBase* GetUVGenerator() const;
List<int> GetIndices(bool allowQuads) const;
void CopyProperties(Face* other, bool merge = false);
Mesh* GetMesh() const;
operator FaceList() { FaceList result; result.Add(const_cast<Face*>(this)); return result; }
private:
List<Triangle> GetTriangles() const;
List<int> GetMetaIndices() const;
List<int> tags;
UVGeneratorBase* uvGenerator;
VertexList vertices;
EdgeList edges;
int materialId;
Mesh* mesh;
};

View File

@@ -0,0 +1,65 @@
#pragma once
#include "../common/common.h"
#include "../math/math.h"
#include "typedefs.h"
class Mesh
{
public:
Mesh();
~Mesh();
const VertexList& GetVertices() const;
const FaceList& GetFaces() const;
EdgeList GetEdges() const;
void AddFace(Face* face);
void RemoveFace(Face* face);
FaceList SplitEdge(Edge* edgeToSplit, Vertex* splitVertex);
void ReplaceVertexFromFaces(const FaceList& faces, Vertex* oldVertex, Vertex* newVertex);
void ReplaceVertices(const VertexList& verticesToReplace, Vertex* newVertex);
List<int> GetIndices(bool allowQuads);
Map<int, FaceList> GetSubMeshs();
void StartAutoUpdate(FaceList& faceList);
void StopAutoUpdate(FaceList& faceList);
void TriggerFaceReplaced(Face* oldFace, const FaceList& newFaces);
private:
Edge* FindEdge(Vertex* a, Vertex* b);
void UpdateIndices();
void RemoveFaceFromEdge(Edge* edge, Face* face);
void RemoveEdgeFromVertex(Vertex* vertex, Edge* edge);
private:
VertexList vertices;
FaceList faces;
List<FaceList*> autoUpdateHandles;
};
// AutoFaceList will watch for changes (replace & remove) on the content of this list and will reflect these.
// This means, when an operation removes a face which is contained in the AutoFaceList it will removed from the
// list even when the operation was performed on a different list.
// Example:
// FaceList a;
// .. // fill a
// AutoFaceList b(a);
// mt::Delete(a); // b.Count() == 0;
class AutoFaceList : public FaceList
{
public:
AutoFaceList(FaceList&& other);
AutoFaceList(const FaceList& other);
~AutoFaceList();
private:
void Init();
Mesh* mesh;
};

View File

@@ -0,0 +1,44 @@
#pragma once
#include "../common/common.h"
#include "../math/math.h"
class Path
{
public:
void SetPoints(const List<vec3>& points);
void SetScale(const List<float>& scale);
void SetRotation(const List<float>& rotation);
vec3 GetPoint(float t) const;
float GetScale(float t) const;
float GetRotation(float t) const;
vec3 GetDirection(float t) const;
private:
template <typename T> static void GetValues(const List<T>& source, float& t, T& p0, T& p1, T& p2, T& p3)
{
auto maxT = source.Count();
auto p1Index = (int)Math::Min(t * maxT, maxT - 1.0f);
auto p0Index = (int)Math::Max(p1Index - 1.0f, 0.0f);
auto p2Index = (int)Math::Min(p1Index + 1.0f, maxT - 1.0f);
auto p3Index = (int)Math::Min(p2Index + 1.0f, maxT - 1.0f);
p1 = source[p1Index];
p2 = p1Index == p2Index ? p1 + (p1 - source[p0Index]) : source[p2Index];
p0 = p0Index == p1Index ? p1 - (p2 - p1) : source[p0Index];
p3 = p3Index == p2Index ? p1 + (p2 - p1) : source[p3Index];
t = (t - (1.0f / maxT * p1Index)) / (1.0f / maxT);
}
static float Spline(float p0, float p1, float p2, float p3, float t);
static vec3 Spline(const vec3& p0, const vec3& p1, const vec3& p2, const vec3& p3, float t);
private:
List<vec3> points;
List<float> rotation;
List<float> scale;
};

View File

@@ -0,0 +1,26 @@
#pragma once
#include "../common/common.h"
#include "../math/math.h"
#include "typedefs.h"
class Vertex;
class Triangle
{
public:
Triangle();
Triangle(Vertex* a, Vertex* b, Vertex* c);
~Triangle();
vec3 GetNormal() const;
const VertexList& GetVertices() const;
bool IsDegenerated() const;
float GetArea() const;
private:
VertexList vertices;
};

View File

@@ -0,0 +1,43 @@
#pragma once
#include "../common/common.h"
#include "../math/math.h"
#include "typedefs.h"
class Vertex
{
friend class Mesh;
friend class Selection;
public:
Vertex(const vec3& position);
Vertex(Vertex* other);
//Vertex();
~Vertex();
int GetIndex() const;
vec3 GetNormal() const;
void SetNormal(const vec3& normal);
bool HasManualNormal() const { return normal != nullptr; }
VertexList GetVertices() const;
FaceList GetFaces() const;
const EdgeList& GetEdges() const;
Mesh* GetMesh() const;
void Interpolate(Vertex* other, float t);
public:
vec3 Position;
float UV[4];
float UV2[4];
private:
vec3* normal;
int index;
EdgeList edges;
Mesh* mesh;
};

View File

@@ -0,0 +1,12 @@
#pragma once
#include "../common/common.h"
#include "../math/math.h"
#include "Mesh.h"
#include "Face.h"
#include "Vertex.h"
#include "Triangle.h"
#include "Edge.h"
#include "Path.h"
#include "typedefs.h"

View File

@@ -0,0 +1,10 @@
#pragma once
#include "../common/common.h"
class Vertex;
class Edge;
class Face;
typedef List<Vertex*> VertexList;
typedef List<Edge*> EdgeList;
typedef List<Face*> FaceList;

View File

@@ -0,0 +1,28 @@
#pragma once
#include "vec3.h"
class Bounds
{
public:
Bounds(vec3 min, vec3 max)
: Min{min}
, Max{max}
{
}
vec3 Min;
vec3 Max;
float GetRadius() const
{
auto length = (Max - Min).GetLength();
return length / 2;
}
vec3 GetCenter() const
{
return (Min + Max) * 0.5f;
}
};

View File

@@ -0,0 +1,23 @@
#pragma once
#include "vec3.h"
class Plane
{
public:
Plane(const vec3& normal, const vec3& point);
Plane(const vec3& a, const vec3& b, const vec3& c);
~Plane();
float GetDistanceToPoint(const vec3& point) const;
vec3 GetNormal() const;
float GetDistance() const;
bool GetSide(const vec3& point) const;
void Flip();
float IntersectLinesegment(vec3 a, vec3 b, vec3& result) const;
private:
vec3 normal;
float distance;
};

View File

@@ -0,0 +1,24 @@
#pragma once
class vec3;
class Quaternion
{
public:
static Quaternion identity;
static Quaternion LookRotation(const vec3& normal);
static Quaternion CreateFromAxisAngle(const vec3& axis, float angle);
static Quaternion FromTo(vec3 v0, vec3 v1);
public:
Quaternion();
void Inverse();
void Normalize();
public:
float x;
float y;
float z;
float w;
};

View File

@@ -0,0 +1,101 @@
#pragma once
#include <cmath> // acos and shit
#include "Bounds.h"
#include "Plane.h"
#include "Quaternion.h"
#include "vec3.h"
class Math
{
public:
static const float Rad2Deg;
static const float Deg2Rad;
static const float FloatEpsilon;
static const float Pi;
static const float Infinity;
static float Clamp01(float a)
{
return a > 1 ? 1 : a < 0 ? 0 : a;
}
static float Pow(float a, float b)
{
return std::pow(a, b);
}
static float Max(float a, float b)
{
return a > b ? a : b;
}
static float Min(float a, float b)
{
return a > b ? b : a;
}
static float Abs(float a)
{
return a > 0 ? a : -a;
}
static float Sin(float x)
{
/*_asm fld x
_asm fsin*/
return std::sin(x);
}
static float Cos(float x)
{
/*_asm fld x
_asm fcos*/
return std::cos(x);
}
static float Acos(float x)
{
/*_asm
{
fld x
fld x
fmul
fld1
fsubr
fsqrt
fxch
fpatan
}*/
return std::acos(x);
}
static float Atan2(float x, float y)
{
return std::atan2(x, y);
}
static float Lerp(float a, float b, float t)
{
return a + t * (b - a);
}
static float Sqrt(float x)
{
#if _DEBUG
return std::sqrt(x);
#else
float xhalf = 0.5f*x;
int i = *(int*)&x;
i = 0x5f375a86 - (i >> 1);
x = *(float*)&i;
x = x*(1.5f - xhalf*x*x);
x = x*(1.5f - xhalf*x*x);
x = x*(1.5f - xhalf*x*x);
x = 1 / x;
return x;
#endif
}
};

View File

@@ -0,0 +1,53 @@
#pragma once
#include "Quaternion.h"
class vec3
{
public:
static const vec3 zero;
static const vec3 one;
static const vec3 up;
static const vec3 down;
static const vec3 left;
static const vec3 right;
static const vec3 forward;
static const vec3 back;
vec3();
vec3(float x, float y, float z);
vec3(const vec3& other);
~vec3();
float GetLength() const;
float GetLengthSq() const;
void Normalize();
static vec3 Mul(const vec3& a, const vec3& b);
static float Dot(const vec3& a, const vec3& b);
static vec3 Cross(const vec3& a, const vec3& b);
static vec3 Min(const vec3& a, const vec3& b);
static vec3 Max(const vec3& a, const vec3& b);
static float Distance(const vec3& a, const vec3& b);
static float DistanceSq(const vec3& a, const vec3& b);
vec3 operator- () const;
bool operator== (const vec3& other) const;
bool operator!= (const vec3& other) const;
vec3 operator+ (const vec3& other) const;
void operator+= (const vec3& other);
void operator-= (const vec3& other);
void operator/= (float other);
void operator*= (float other);
vec3 operator- (const vec3& other) const;
vec3 operator* (float other) const;
vec3 operator/ (float other) const;
vec3 operator* (const Quaternion& q) const;
public:
float x;
float y;
float z;
};

View File

@@ -0,0 +1,18 @@
#pragma once
#include "../../include/core/core.h"
#include "typedefs.h"
namespace mt
{
// Generates the CSG Union of a and b.
// All faces will be triangulated.
Mesh* CsgUnion(const FaceList& a, const FaceList& b);
// Generates the CSG subtraction of a and b.
// All faces will be triangulated.
Mesh* CsgSubtract(const FaceList& a, const FaceList& b);
// Generates the CSG intersection of a and b.
// All faces will be triangulated.
Mesh* CsgIntersect(const FaceList& a, const FaceList& b);
}

View File

@@ -0,0 +1,45 @@
#pragma once
#include "../../include/core/core.h"
#include "typedefs.h"
namespace mt
{
class CSG
{
public:
static CSG* FromPolygons(const FaceList& faces);
CSG* Clone();
CSG* Inverse();
const FaceList& GetFaces() const;
CSG* Union(CSG* other);
CSG* Subtract(CSG* other);
CSG* Intersect(CSG* other);
private:
FaceList faces;
};
class CSGNode
{
public:
CSGNode();
CSGNode(const FaceList& faces);
~CSGNode();
CSGNode* Clone();
void Invert();
FaceList ClipPolygons(const FaceList& other);
void ClipTo(CSGNode* other);
FaceList AllPolygons();
void Build(const FaceList& faces);
private:
FaceList faces;
Plane* plane;
CSGNode* front;
CSGNode* back;
};
}

View File

@@ -0,0 +1,20 @@
#pragma once
#include "../core/core.h"
namespace mt
{
// Returns the bounding box of the vertices.
Bounds GetBoundingBox(const VertexList& vertices);
// Returns the average normal of the vertices.
vec3 GetNormal(const VertexList& vertices);
// Returns the center of the bounding box of the vertices.
vec3 GetCenter(const VertexList& vertices);
// Generates half edges from the given edges and faces.
EdgeList GetHalfEdges(const EdgeList& edges, const FaceList& faces);
// Returns the outline edges of the faces. An outline is an edge which has only 1 face (excluding faces not in the provided faces list).
EdgeList GetOutlineEdges(const FaceList& faces);
}

View File

@@ -0,0 +1,15 @@
#pragma once
#include "../../include/core/core.h"
#include "typedefs.h"
namespace mt
{
// Clones the faces which do not belong to the target mesh and transfers the result to the target mesh.
FaceList CopyToMesh(const FaceList& faces, Mesh* target);
// Clones the faces and returns the copy.
FaceList Copy(const FaceList& faces);
// Clones the faces into a new mesh.
Mesh* ToMesh(const FaceList& faces);
}

View File

@@ -0,0 +1,9 @@
#pragma once
#include "../../include/core/core.h"
#include "typedefs.h"
namespace mt
{
// Deletes the faces and removes them from the mesh.
void Delete(const FaceList& faces);
}

View File

@@ -0,0 +1,12 @@
#pragma once
#include "../../include/core/core.h"
#include "typedefs.h"
namespace mt
{
// Removes the faces from attaching faces.
Map<Vertex*, Vertex*> Detach(const FaceList& faces, bool preserveVertexNormals = false, EdgeList* outline = nullptr);
// Removes each face from attaching faces.
void DetachAll(const FaceList& faces, bool preserveVertexNormals = false);
}

View File

@@ -0,0 +1,24 @@
#pragma once
#include "../../include/core/core.h"
#include "typedefs.h"
namespace mt
{
// Extrudes the given vertices.
FaceList ExtrudeVertices(const VertexList& vertices, float sideEdgeLength, float extrudeHeight);
// Detaches the faces from the mesh and moves the vertices by the given factor and move mode. Then generates side faces to combine the outlines of the hole of the detached faces to the detached faces.
FaceList Extrude(const FaceList& faces, float factor, MoveMode mode = MoveMode::UseVertexNormal);
// Detaches the faces from the mesh and moves the vertices by the given extrude direction. Then generates side faces to combine the outlines of the hole of the detached faces to the detached faces.
FaceList Extrude(const FaceList& faces, const vec3& extrudeDirection);
// Detaches the faces from the mesh, moves the vertices by the given extrude direction and scales the detached faces. Then generates side faces to combine the outlines of the hole of the detached faces to the detached faces.
FaceList Bevel(const FaceList& faces, const vec3& extrudeDirection, float capScale);
// Detaches the faces from the mesh, moves the vertices by the given factor and move mode and scales the detached faces. Then generates side faces to combine the outlines of the hole of the detached faces to the detached faces.
FaceList Bevel(const FaceList& faces, float factor, float capScale, MoveMode mode = MoveMode::UseVertexNormal, const vec3* extrudeDirectionOverride = nullptr);
// Extrudes the given faces along the given path.
List<FaceList> Loft(const FaceList& faces, const Path& path, int segments);
}

View File

@@ -0,0 +1,95 @@
#pragma once
#include "../../include/core/core.h"
#include "typedefs.h"
namespace mt
{
// Free Form Deformation
// Creates an 3D control point array of (L+1) * (M+1) * (N+1) Elements.
// Each control point goes from -HalfBounds to +HalfBounds along each axis of control points.
// Changing the position of the control points will move the vertices based upon a coeficient to the new position.
class FFD
{
public:
enum FFDControlPointMode
{
All,
Edges,
Surface
};
private:
class Vector3Param
{
public:
///bernstein polynomial packing
List<List<float>> bernPolyPack;
///Point after applying s,t,u to p0, should result in original point
vec3 p;
///Origin
vec3 p0;
///Distances along S/T/U axes
float s, t, u;
Vector3Param()
{
s = 0.0f;
t = 0.0f;
u = 0.0f;
}
Vector3Param(const Vector3Param& v)
{
s = v.s;
t = v.t;
u = v.u;
p = v.p;
p0 = v.p0;
}
};
private:
vec3 S;
vec3 T;
vec3 U;
List<Vector3Param> vertexParams;
const FaceList& faces;
VertexList originalVertices;
FFDControlPointMode ffdControlPointMode;
public:
/// <summary>
/// Number of controls for S, T, & U respectively. (L,M, and N MUST be >= 1)
/// </summary>
const int L;
const int M;
const int N;
// The control points of the FFD Box in unnormalized point space.
// ControlPoints[0][0][0] results in a vec3(-0.5f, -0.5f, -0.5f) position when the FFD Box encapsulates a Cube with an edge length of 1.0f.
vec3*** ControlPoints;
FFD(const FaceList& faces, int l, int m, int n, FFDControlPointMode ffdControlPointMode = FFDControlPointMode::All);
~FFD();
void Apply();
List<vec3*> SelectPoints(int startX, int endX, int startY, int endY, int startZ, int endZ);
private:
float binomialCoeff(int n, int k);
float bernsteinPoly(int n, int v, float x);
void calculateSTU(const vec3& max, const vec3& min);
void calculateTrivariateBernsteinPolynomial(const vec3& p0);
void Parameterize();
void createControlPoints(const vec3& origin);
vec3 createControlPoint(const vec3& p0, int i, int j, int k);
vec3 getWorldVector3(const Vector3Param& r);
};
}

View File

@@ -0,0 +1,9 @@
#pragma once
#include "../../include/core/core.h"
#include "typedefs.h"
namespace mt
{
// Flips the facing direction of all faces.
void FlipSides(const FaceList& faces);
}

View File

@@ -0,0 +1,12 @@
#pragma once
#include "../../include/core/core.h"
#include "typedefs.h"
namespace mt
{
// Groups the faces into adjancing faces. Adjancing faces are faces attached to each other with the same face direction.
// This function does not weld the faces to each other before performing and does not detach the groups from each other after performing.
// Use a threshold >= 2 to get groups of connected faces
// Use any other threshold to get groups of connected faces who are connected to each other by a certain angle
List<FaceList> GetGroups(const FaceList& faces, float threshold);
}

View File

@@ -0,0 +1,19 @@
#pragma once
#include "../../include/core/core.h"
#include "typedefs.h"
namespace mt
{
// Creates a quad face with the given size, direction and center position.
Face* CreateQuad(const vec3& size, const vec3& position, PlaneDirection direction);
// Creates a plane mesh with the given size, direction and center position.
Mesh* CreatePlane(const vec3& position = vec3::zero, float edgeSize = 1, PlaneDirection direction = PlaneDirection::Up);
// Creates a cube mesh with the given size, direction and center position, the cube may be optionally smooth or sharp.
Mesh* CreateCube(bool weldVertices = true, const vec3& position = vec3::zero, float edgeSize = 1);
// Creates a sphere mesh with the given size, tesselation and center position.
// The tesselationLevel is the number of times a start cube will be subdivided and smoothed, so using a level of 0 will result to a welded cube.
Mesh* CreateSphere(int tesselationLevel = 1, const vec3& position = vec3::zero, float baseSize = 1);
}

View File

@@ -0,0 +1,18 @@
#pragma once
#include "../../include/core/core.h"
#include "typedefs.h"
namespace mt
{
// Projects all vertices to the plane generated of the center and average normal of all vertices.
void Flatten(const VertexList& vertices);
// Projects all vertices to the plane generated of the center and the given normal.
void Flatten(const VertexList& vertices, const vec3& normal);
// Projects all vertices to the given plane.
void Project(const VertexList& vertices, const Plane& plane);
// Projects all vertices to the given plane when the vertex is at the negative side of the plane.
void Clamp(const VertexList& vertices, const Plane& plane);
}

View File

@@ -0,0 +1,49 @@
#pragma once
#include "../../include/core/core.h"
namespace mt
{
// Returns the vertices associated to the elements.
template<class T>
VertexList GetVertices(const List<T>& elements)
{
PERFORMANCER;
return Linq::SelectUniqueMany<Vertex*>(elements, [](T element) { return element->GetVertices(); });
}
// Returns the elements associated to the vertices.
template<class T>
FaceList GetFaces(const List<T>& elements)
{
PERFORMANCER;
return Linq::SelectUniqueMany<Face*>(elements, [](T element) { return element->GetFaces(); });
}
// Returns all faces which face to the given direction. An threshold may be used to allow faces which face the direction nearly.
// Mirroring is optionally appplied which also uses the negated direction to check for mathing faces.
FaceList GetFacesByDirection(const FaceList& faces, const vec3 direction, float threshold = 0.0f, bool mirror = false);
// Returns all faces which intersect the given plane or are on the positive side of it.
FaceList GetFacesOnPositivePlane(const FaceList& faces, const Plane& plane);
// Returns all vertices which intersect the given plane or are on the positive side of it.
VertexList GetVerticesOnPositivePlane(const VertexList& vertices, const Plane& plane);
// Returns all faces with the given tag.
FaceList GetFacesWithTag(const FaceList& faces, int tag);
// Returns all faces with one or all (optionally) tags from the given tag list.
FaceList GetFacesWithTags(const FaceList& faces, List<int> tags, bool canMatchAnyTag = true);
// Returns all faces inside or intersecting the sphere at the given postion and radius.
FaceList GetFacesAt(const FaceList& faces, const vec3& position, float radius);
// Returns all vertices inside or intersecting the sphere at the given postion and radius.
VertexList GetVerticesAt(const VertexList& vertices, const vec3& position, float radius);
// Returns the nearest face to the given point in the given range.
Face* GetFaceAt(const FaceList& faces, const vec3& position, float range = Math::Infinity);
// Returns the nearest vertex to the given point in the given range.
Vertex* GetVertexAt(const VertexList& vertices, const vec3& position, float range = Math::Infinity);
}

View File

@@ -0,0 +1,12 @@
#pragma once
#include "../../include/core/core.h"
#include "typedefs.h"
namespace mt
{
// Splits the provided faces by the given plane.
FaceList Split(const FaceList& faces, const Plane& plane);
// Merges two triangles and returns the new quad face.
Face* MergeTriangles(Face* a, Face* b);
}

View File

@@ -0,0 +1,10 @@
#pragma once
#include "../../include/core/core.h"
#include "typedefs.h"
namespace mt
{
// Splits the faces into 3 (for triangles) or 4 (for quads) faces and applies the optional smoothing step.
// Warning: This method removes the provided faces.
FaceList Subdivide(const FaceList& faces, bool smooth);
}

View File

@@ -0,0 +1,11 @@
#pragma once
#include "../../include/core/core.h"
#include "typedefs.h"
namespace mt
{
// Adds the given tag to all faces.
void AddTag(const FaceList& faces, int tag);
// Removes the given tag from all faces.
void RemoveTag(const FaceList& faces, int tag);
}

View File

@@ -0,0 +1,45 @@
#pragma once
#include "../core/core.h"
#include "typedefs.h"
namespace mt
{
// Sets the position of each vertex relative to their center to the given position.
void MoveTo(const VertexList& vertices, const vec3& position);
// Moves each vertex about the given offset.
void MoveBy(const VertexList& vertices, const vec3& offset);
// Moves each vertex about the given offset.
void MoveBy(const VertexList& vertices, float factor, MoveMode mode = MoveMode::UseCollectionNormal);
// Scales each vertex around the given center.
void Scale(const VertexList& vertices, float factor, CenterMode mode = CenterMode::CollectionCenter);
// Scales each vertex around the given center.
void Scale(const VertexList& vertices, const vec3& center, float factor);
// Scales each vertex around the given center.
void Scale(const VertexList& vertices, const vec3& factor, const vec3& center);
// Scales each vertex around the center of the given centermode.
void Scale(const VertexList& vertices, const vec3& factor, CenterMode mode = mt::CenterMode::CollectionCenter);
// Rotates the vertices to the given direction, based upon the average of the vertices normals.
void Rotate(const VertexList& vertices, const vec3& startNormal, const vec3& direction);
// Rotates the vertices to the given direction, based upon the average of the vertices normals.
void Rotate(const VertexList& vertices, const vec3& center, const vec3& startNormal, const vec3& direction);
// Rotates the vertices through the given axis at the center of the vertices by the given radian angle.
void Rotate(const VertexList& vertices, const vec3& axis, float angle);
// Rotates the vertices through the given axis at the given center by the given radian angle.
void Rotate(const VertexList& vertices, const vec3& center, const vec3& axis, float angle);
// Rotates the vertices through by the given quaternion at the given center.
void Rotate(const VertexList& vertices, const vec3& center, const Quaternion& rotation);
// Mirrors the faces on the given plane
void Mirror(const FaceList& faces, const Plane& plane);
}

View File

@@ -0,0 +1,9 @@
#pragma once
#include "../../include/core/core.h"
#include "typedefs.h"
namespace mt
{
// Transforms each quad into two triangles.
FaceList Triangulate(const FaceList& faces);
}

View File

@@ -0,0 +1,13 @@
#pragma once
#include "../core/core.h"
#include "typedefs.h"
namespace mt
{
// Combines vertices which lay near to each other.
// Warning: This method may remove the faces of the provided vertices.
void Weld(const VertexList& vertices, float range = 0);
// Moves all vertices of the faces to the center and welds them.
void Collapse(const FaceList& faces, CenterMode mode = CenterMode::CollectionCenter);
}

View File

@@ -0,0 +1,22 @@
#pragma once
#include "../core/core.h"
#include "Prefab.h"
#include "Select.h"
#include "Transform.h"
#include "Calculate.h"
#include "Extrude.h"
#include "Flip.h"
#include "Copy.h"
#include "Detach.h"
#include "Weld.h"
#include "Group.h"
#include "Triangulate.h"
#include "Project.h"
#include "Delete.h"
#include "Tags.h"
#include "Subdivide.h"
#include "CSG.h"
#include "FFD.h"
#include "Split.h"
#include "typedefs.h"

View File

@@ -0,0 +1,76 @@
#pragma once
#include "Select.h"
namespace mt
{
enum MoveMode
{
UseCollectionNormal,
UseVertexNormal
};
enum CenterMode
{
FaceCenter,
MeshCenter,
CollectionCenter
};
enum PlaneDirection
{
Up,
Down,
Left,
Right,
Forward,
Backward
};
enum Tags
{
ReservedTagsStartId = 1 << 16,
DirectionUp,
DirectionDown,
DirectionLeft,
DirectionRight,
DirectionBack,
DirectionFront
};
}
template<>
struct convert<FaceList, VertexList>
{
static VertexList Convert(const FaceList& src)
{
return mt::GetVertices(src);
}
};
template<>
struct convert<EdgeList, VertexList>
{
static VertexList Convert(const EdgeList& src)
{
return mt::GetVertices(src);
}
};
template<>
struct convert<VertexList, FaceList>
{
static FaceList Convert(const VertexList& src)
{
return mt::GetFaces(src);
}
};
template<>
struct convert<EdgeList, FaceList>
{
static FaceList Convert(const EdgeList& src)
{
return mt::GetFaces(src);
}
};

View File

@@ -0,0 +1,164 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\common\common.h" />
<ClInclude Include="include\core\core.h" />
<ClInclude Include="include\tools\Delete.h" />
<ClInclude Include="include\tools\Flip.h" />
<ClInclude Include="include\common\Linq.h" />
<ClInclude Include="include\common\List.h" />
<ClInclude Include="include\common\Map.h" />
<ClInclude Include="include\common\Performancer.h" />
<ClInclude Include="include\core\Edge.h" />
<ClInclude Include="include\core\Face.h" />
<ClInclude Include="include\core\Mesh.h" />
<ClInclude Include="include\core\Path.h" />
<ClInclude Include="include\core\Triangle.h" />
<ClInclude Include="include\core\typedefs.h" />
<ClInclude Include="include\core\Vertex.h" />
<ClInclude Include="include\tools\Copy.h" />
<ClInclude Include="include\tools\CSG.h" />
<ClInclude Include="include\tools\CSGBase.h" />
<ClInclude Include="include\tools\Detach.h" />
<ClInclude Include="include\tools\Extrude.h" />
<ClInclude Include="include\tools\FFD.h" />
<ClInclude Include="include\math\Bounds.h" />
<ClInclude Include="include\math\Plane.h" />
<ClInclude Include="include\math\Quaternion.h" />
<ClInclude Include="include\math\vec3.h" />
<ClInclude Include="include\tools\Group.h" />
<ClInclude Include="include\tools\Project.h" />
<ClInclude Include="include\tools\Select.h" />
<ClInclude Include="include\tools\Calculate.h" />
<ClInclude Include="include\tools\tools.h" />
<ClInclude Include="include\tools\Prefab.h" />
<ClInclude Include="include\tools\Split.h" />
<ClInclude Include="include\tools\Subdivide.h" />
<ClInclude Include="include\tools\Tags.h" />
<ClInclude Include="include\tools\Transform.h" />
<ClInclude Include="include\tools\Triangulate.h" />
<ClInclude Include="include\tools\typedefs.h" />
<ClInclude Include="include\tools\Weld.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\tools\Delete.cpp" />
<ClCompile Include="src\tools\Flip.cpp" />
<ClCompile Include="src\common\Performancer.cpp" />
<ClCompile Include="src\core\Edge.cpp" />
<ClCompile Include="src\core\Face.cpp" />
<ClCompile Include="src\core\Mesh.cpp" />
<ClCompile Include="src\core\Path.cpp" />
<ClCompile Include="src\core\Triangle.cpp" />
<ClCompile Include="src\core\Vertex.cpp" />
<ClCompile Include="src\tools\Copy.cpp" />
<ClCompile Include="src\tools\CSG.cpp" />
<ClCompile Include="src\tools\CSGBase.cpp" />
<ClCompile Include="src\tools\Detach.cpp" />
<ClCompile Include="src\tools\Extrude.cpp" />
<ClCompile Include="src\tools\FFD.cpp" />
<ClCompile Include="src\math\Math.cpp" />
<ClCompile Include="src\math\Plane.cpp" />
<ClCompile Include="src\math\Quaternion.cpp" />
<ClCompile Include="src\math\vec3.cpp" />
<ClCompile Include="src\tools\Group.cpp" />
<ClCompile Include="src\tools\Project.cpp" />
<ClCompile Include="src\tools\Select.cpp" />
<ClCompile Include="src\tools\Calculate.cpp" />
<ClCompile Include="src\tools\Prefab.cpp" />
<ClCompile Include="src\tools\Split.cpp" />
<ClCompile Include="src\tools\Subdivide.cpp" />
<ClCompile Include="src\tools\Tags.cpp" />
<ClCompile Include="src\tools\Transform.cpp" />
<ClCompile Include="src\tools\Triangulate.cpp" />
<ClCompile Include="src\tools\Weld.cpp" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{A07570FA-501F-400E-823E-00B5459C7E8D}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>meshTools</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<OutDir>$(SolutionDir)Bin\$(Configuration)\lib\</OutDir>
<LibraryPath>$(SolutionDir)Bin\$(Configuration)\lib\;C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(LibraryPath)</LibraryPath>
<IncludePath>$(SolutionDir)math/include;$(SolutionDir)common/include;$(SolutionDir)meshCore/include;C:\Program Files %28x86%29\Visual Leak Detector\include;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<OutDir>$(SolutionDir)Bin\$(Configuration)\lib\</OutDir>
<LibraryPath>$(SolutionDir);C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(LibraryPath)</LibraryPath>
<IncludePath>C:\Program Files %28x86%29\Visual Leak Detector\include;$(IncludePath)</IncludePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
<Lib>
<AdditionalDependencies>
</AdditionalDependencies>
</Lib>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
<Lib>
<AdditionalDependencies>msvcrt_old.LIB</AdditionalDependencies>
</Lib>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@@ -0,0 +1,225 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\common\common.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\tools\Calculate.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\tools\Copy.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\tools\CSG.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\tools\CSGBase.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\tools\Delete.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\tools\Detach.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\tools\Extrude.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\tools\FFD.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\tools\Flip.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\tools\Group.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\tools\Prefab.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\tools\Project.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\tools\Select.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\tools\Split.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\tools\Subdivide.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\tools\Tags.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\tools\Transform.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\tools\Triangulate.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\tools\typedefs.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\tools\Weld.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\math\Bounds.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\math\Plane.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\math\Quaternion.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\math\vec3.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\tools\tools.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\core\Edge.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\core\Face.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\core\Mesh.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\core\Path.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\core\Triangle.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\core\Vertex.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\core\typedefs.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\core\core.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\common\Performancer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\common\Linq.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\common\List.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\common\Map.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\core\Edge.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\core\Face.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\core\Mesh.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\core\Path.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\core\Triangle.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\core\Vertex.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\math\Math.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\math\Plane.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\math\Quaternion.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\math\vec3.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\common\Performancer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\tools\Calculate.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\tools\CSG.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\tools\CSGBase.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\tools\Extrude.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\tools\FFD.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\tools\Prefab.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\tools\Select.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\tools\Subdivide.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\tools\Tags.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\tools\Transform.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\tools\Split.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\tools\Triangulate.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\tools\Weld.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\tools\Project.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\tools\Group.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\tools\Detach.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\tools\Copy.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\tools\Flip.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\tools\Delete.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ShowAllFiles>true</ShowAllFiles>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,161 @@
#if _DEBUG
#include "../../include/common/Performancer.h"
#include "Windows.h"
#include <iostream>
namespace
{
const long long g_Frequency = []() -> long long
{
LARGE_INTEGER frequency;
QueryPerformanceFrequency(&frequency);
return frequency.QuadPart;
}();
}
HighResClock::time_point HighResClock::now()
{
LARGE_INTEGER count;
QueryPerformanceCounter(&count);
return time_point(duration(count.QuadPart * static_cast<rep>(period::den) / g_Frequency));
}
Performancer Performancer::Instance;
int Performancer::RegisterMethod(const std::string& name, const std::string& fullName)
{
data.push_back({ name, fullName });
return data.size() - 1;
}
void Performancer::AddData(int index, long long duration)
{
data[index].Add(duration);
}
struct my_numpunct : std::numpunct<char> {
std::string do_grouping() const { return "\03"; }
};
void Performancer::Dump()
{
auto sorted = data;
std::sort(sorted.begin(), sorted.end(), [](const PerformancerData& lhs, const PerformancerData& rhs)
{
return lhs.Elapsed > rhs.Elapsed;
});
long long total = 0;
long long average = 0;
size_t maxNameLength = 0;
for (const auto& data : sorted)
{
maxNameLength = data.Name.size() > maxNameLength ? data.Name.size() : maxNameLength;
total += data.Elapsed;
average += data.Elapsed / data.Calls;
}
maxNameLength += 2;
const int usWidth = 20;
std::locale loc(std::cout.getloc(), new my_numpunct);
std::stringstream ss;
ss.imbue(loc);
ss << "Performancer summary:\n";
ss.width(10);
ss << std::left << "Rank";
ss.width(maxNameLength);
ss << std::left << "Name";
ss.width(usWidth);
ss << std::left << "Calls";
ss.width(usWidth);
ss << std::left << "Min (us)";
ss.width(usWidth);
ss << std::left << "Max (us)";
ss.width(usWidth);
ss << std::left << "Average (us)";
ss.width(usWidth);
ss << std::left << "Average (%)";
ss.width(usWidth);
ss << std::left << "Total (us)";
ss.width(usWidth);
ss << std::left << "Total (%)";
ss.width(usWidth);
ss << "Full Name";
ss << "\n";
int i = 0;
for (const auto& data : sorted)
{
i++;
ss.width(10);
ss << std::left << i;
ss.width(maxNameLength);
ss << std::left << data.Name;
ss.width(usWidth);
ss << std::left << data.Calls;
ss.width(usWidth);
ss << std::left << data.Min;
ss.width(usWidth);
ss << std::left << data.Max;
ss.width(usWidth);
ss << std::left << data.Elapsed / data.Calls;
ss.width(usWidth);
ss << std::left << (int)(((data.Elapsed / data.Calls) / (double)average) * 100);
ss.width(usWidth);
ss << std::left << data.Elapsed;
ss.width(usWidth);
ss << std::left << (int)((data.Elapsed / (double)total) * 100);
ss << data.FullName;
ss << "\n";
}
ss << "\n";
OutputDebugStringA(ss.str().c_str());
}
PerformancerEntry::PerformancerEntry(int index)
: index{ index }
, start{ HighResClock::now() }
{
}
PerformancerEntry::~PerformancerEntry()
{
auto end = HighResClock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
Performancer::Instance.AddData(index, duration);
}
PerformancerData::PerformancerData(const std::string& name, const std::string& fullName)
: Name(name)
, FullName(fullName)
, Calls{ 0 }
, Elapsed{ 0 }
, Max{ 0 }
, Min{ 0 }
{
}
void PerformancerData::Add(long long duration)
{
if (Calls == 0)
{
Min = duration;
Max = duration;
}
else
{
Min = Min > duration ? duration : Min;
Max = Max < duration ? duration : Max;
}
Elapsed += duration;
Calls++;
}
#endif

View File

@@ -0,0 +1,99 @@
#include "../../include/core/Edge.h"
#include "../../include/core/Face.h"
#include "../../include/core/Vertex.h"
Edge::Edge()
: mesh{ nullptr }
, vertices{ 2 }
{
PERFORMANCER;
}
Edge::Edge(Vertex* a, Vertex* b)
: mesh{ nullptr }
, vertices{ 2 }
{
PERFORMANCER;
vertices.Add(a);
vertices.Add(b);
}
Edge::~Edge()
{
PERFORMANCER;
}
Vertex* Edge::GetOtherVertex(Vertex* vertex) const
{
PERFORMANCER;
return vertices[0] == vertex ? vertices[1] : vertices[0];
}
const VertexList& Edge::GetVertices() const
{
PERFORMANCER;
return vertices;
}
const FaceList& Edge::GetFaces() const
{
PERFORMANCER;
return faces;
}
EdgeList Edge::GetEdges() const
{
PERFORMANCER;
auto result = Linq::SelectUniqueMany<Edge*>(faces, [](Face* face){ return face->GetEdges(); });
result.Remove(const_cast<Edge*>(this));
return result;
}
vec3 Edge::GetDirection() const
{
PERFORMANCER;
auto direction = vertices[1]->Position - vertices[0]->Position;
direction.Normalize();
return direction;
}
vec3 Edge::GetCenter() const
{
PERFORMANCER;
return (vertices[1]->Position + vertices[0]->Position) * 0.5f;
}
float Edge::GetLength() const
{
PERFORMANCER;
auto length = vertices[0]->Position - vertices[1]->Position;
return length.GetLength();
}
Edge* Edge::GetHalfEdge(Face* face) const
{
PERFORMANCER;
auto edges = face->GetEdges();
auto index = edges.IndexOf(const_cast<Edge*>(this));
auto faceVertices = face->GetVertices();
auto next = (index + 1) % faceVertices.Count();
auto newEdge = new Edge(faceVertices[index], faceVertices[next]);
newEdge->faces = faces;
return newEdge;
}

View File

@@ -0,0 +1,303 @@
#include "../../include/core/Mesh.h"
#include "../../include/core/Face.h"
#include "../../include/core/Vertex.h"
#include "../../include/core/Edge.h"
Face::Face()
{
PERFORMANCER;
}
Face::Face(Vertex* a, Vertex* b, Vertex* c, Vertex* d)
: materialId{ 0 }
, uvGenerator{ nullptr }
, mesh{ nullptr }
{
PERFORMANCER;
vertices.Add(a);
vertices.Add(b);
vertices.Add(c);
if (d != nullptr)
vertices.Add(d);
}
Face::Face(const VertexList& otherVertices)
: materialId{ 0 }
, uvGenerator{ nullptr }
, mesh{ nullptr }
{
PERFORMANCER;
vertices = otherVertices;
}
Face::~Face()
{
PERFORMANCER;
}
vec3 Face::GetNormal() const
{
PERFORMANCER;
vec3 normal;
auto cnt = vertices.Count();
for (int i = 0; i < cnt; i++)
normal += vec3::Cross(vertices[i]->Position, vertices[(i + 1) % cnt]->Position); // cross product
normal.Normalize();
return normal;
}
Plane Face::GetPlane() const
{
return Plane(vertices[0]->Position, vertices[1]->Position, vertices[2]->Position);
}
bool Face::IsTriangle() const
{
PERFORMANCER;
return vertices.Count() == 3;
}
List<Triangle> Face::GetTriangles() const
{
PERFORMANCER;
List<Triangle> result(2);
if (vertices.Count() == 3)
result.Add({ vertices[0], vertices[1], vertices[2] });
else
{
result.Add({ vertices[0], vertices[1], vertices[2] });
result.Add({ vertices[2], vertices[3], vertices[0] });
}
return result;
}
List<int> Face::GetIndices(bool allowQuads) const
{
PERFORMANCER;
if (allowQuads)
return Linq::Select<int>(vertices, [](Vertex* vertex) { return vertex->GetIndex(); });
const int quadIndices[6] =
{
0, 1, 2,
2, 3, 0
};
const int triangleIndices[3] =
{
0, 1, 2
};
const int* metaIndices = vertices.Count() == 3 ? triangleIndices : quadIndices;
List<int> result;
for (int i = 0; i < vertices.Count(); ++i)
result.Add(vertices[metaIndices[i]]->GetIndex());
return result;
}
void Face::FlipNormal()
{
PERFORMANCER;
vertices = Linq::Reverse(vertices);
}
void Face::FlipNormal(const vec3& dir)
{
PERFORMANCER;
if (vec3::Dot(GetNormal(), dir) < 0)
FlipNormal();
}
FaceList Face::Triangulate()
{
PERFORMANCER;
FaceList result;
if (IsTriangle() && !IsDegenerated())
{
result.Add(this);
return result;
}
auto triangles = GetTriangles();
for (const auto& triangle : triangles)
{
if (triangle.IsDegenerated())
continue;
auto newFace = new Face(triangle.GetVertices());
newFace->CopyProperties(this);
mesh->AddFace(newFace);
result.Add(newFace);
}
mesh->RemoveFace(this);
return result;
}
void Face::FlipEdge()
{
if (IsTriangle())
return;
auto oldVertices = vertices;
vertices.Clear();
vertices.Add(oldVertices[1]);
vertices.Add(oldVertices[2]);
vertices.Add(oldVertices[3]);
vertices.Add(oldVertices[0]);
}
bool Face::IsDegenerated() const
{
PERFORMANCER;
auto triangles = GetTriangles();
for (const auto& triangle : triangles)
if (!triangle.IsDegenerated())
return false;
return true;
}
bool Face::RegainIntegrity()
{
PERFORMANCER;
auto triangles = GetTriangles();
for (const auto& triangle : triangles)
if (triangle.IsDegenerated())
{
vertices = Linq::Distinct(vertices);
return vertices.Count() > 2;
}
return true;
}
void Face::CopyProperties(Face* other, bool merge)
{
PERFORMANCER;
if (!merge)
tags = other->tags;
else
tags = Linq::Union(tags, other->tags);
materialId = other->materialId;
}
Mesh* Face::GetMesh() const
{
PERFORMANCER;
return mesh;
}
const VertexList& Face::GetVertices() const
{
PERFORMANCER;
return vertices;
}
FaceList Face::GetFaces() const
{
PERFORMANCER;
auto result = Linq::SelectUniqueMany<Face*>(edges, [](Edge* edge){ return edge->GetFaces(); });
result.Remove(const_cast<Face*>(this));
return result;
}
const EdgeList& Face::GetEdges() const
{
PERFORMANCER;
return edges;
}
bool Face::HasTag(int tag) const
{
PERFORMANCER;
return tags.IndexOf(tag) != -1;
}
bool Face::HasTagsAny(const List<int>& tagsToCheck) const
{
PERFORMANCER;
return Linq::Intersect(tags, tagsToCheck).Count() > 0;
}
bool Face::HasTagsAll(const List<int>& tagsToCheck) const
{
PERFORMANCER;
return Linq::Intersect(tags, tagsToCheck).Count() == tags.Count();
}
void Face::AddTag(int tag)
{
PERFORMANCER;
if (HasTag(tag))
return;
tags.Add(tag);
}
void Face::RemoveTag(int tag)
{
PERFORMANCER;
if (!HasTag(tag))
return;
tags.Remove(tag);
}
List<int> Face::GetTags() const
{
PERFORMANCER;
return tags;
}
void Face::SetMaterialId(int materialId)
{
PERFORMANCER;
this->materialId = materialId;
}
int Face::GetMaterialId() const
{
PERFORMANCER;
return materialId;
}
void Face::SetUVGenerator(UVGeneratorBase* generator)
{
PERFORMANCER;
uvGenerator = generator;
}
UVGeneratorBase* Face::GetUVGenerator() const
{
PERFORMANCER;
return uvGenerator;
}

View File

@@ -0,0 +1,287 @@
#include "../../include/core/Mesh.h"
#include "../../include/core/Face.h"
#include "../../include/core/Edge.h"
#include "../../include/core/Vertex.h"
Mesh::Mesh()
{
PERFORMANCER;
}
Mesh::~Mesh()
{
PERFORMANCER;
}
const VertexList& Mesh::GetVertices() const
{
PERFORMANCER;
return vertices;
}
const FaceList& Mesh::GetFaces() const
{
PERFORMANCER;
return faces;
}
EdgeList Mesh::GetEdges() const
{
PERFORMANCER;
return Linq::SelectUniqueMany<Edge*>(faces, [](Face* face) { return face->GetEdges(); });
}
void Mesh::AddFace(Face* faceToAdd)
{
PERFORMANCER;
faceToAdd->mesh = this;
faces.Add(faceToAdd);
auto& faceVertices = faceToAdd->vertices;
auto& faceEdges = faceToAdd->edges;
faceEdges.Add(FindEdge(faceVertices[0], faceVertices[1]));
faceEdges.Add(FindEdge(faceVertices[1], faceVertices[2]));
if (faceToAdd->IsTriangle())
faceEdges.Add(FindEdge(faceVertices[2], faceVertices[0]));
else
{
faceEdges.Add(FindEdge(faceVertices[2], faceVertices[3]));
faceEdges.Add(FindEdge(faceVertices[3], faceVertices[0]));
}
for (const auto& edge : faceEdges)
edge->faces.Add(faceToAdd);
}
// Removing a face results in an update to all affected elements of the face
// We remove the face from the meshs face list and from all neighbourfaces, edges and vertices.
void Mesh::RemoveFace(Face* faceToRemove)
{
PERFORMANCER;
faceToRemove->mesh = nullptr;
faces.Remove(faceToRemove);
auto& faceEdges = faceToRemove->edges;
for (const auto& edge : faceEdges)
RemoveFaceFromEdge(edge, faceToRemove);
faceEdges.Clear();
}
Edge* Mesh::FindEdge(Vertex* a, Vertex* b)
{
PERFORMANCER;
for (const auto& existingEdge : a->edges)
for (const auto& edgeVertex : existingEdge->vertices)
if (edgeVertex == b)
return existingEdge;
Edge* newEdge = new Edge{ a, b };
newEdge->mesh = this;
a->edges.Add(newEdge);
b->edges.Add(newEdge);
if (a->mesh == nullptr)
{
a->mesh = this;
vertices.Add(a);
}
if (b->mesh == nullptr)
{
b->mesh = this;
vertices.Add(b);
}
return newEdge;
}
List<int> Mesh::GetIndices(bool allowQuads)
{
PERFORMANCER;
UpdateIndices();
return Linq::SelectMany<int>(faces, [allowQuads](Face* face){ return face->GetIndices(allowQuads); });
}
void Mesh::UpdateIndices()
{
PERFORMANCER;
for (int i = 0, iLength = vertices.Count(); i < iLength; ++i)
vertices[i]->index = i;
}
FaceList Mesh::SplitEdge(Edge* edgeToSplit, Vertex* splitVertex)
{
PERFORMANCER;
auto attachedFaces = edgeToSplit->faces;
FaceList allNewFaces;
for (const auto& attachedFace : attachedFaces)
{
auto normal = attachedFace->GetNormal();
auto faceEdges = attachedFace->edges;
FaceList newFaces;
for (const auto& faceEdge : faceEdges)
{
if (faceEdge != edgeToSplit)
{
auto newFace = new Face(faceEdge->vertices[0], faceEdge->vertices[1], splitVertex);
newFace->FlipNormal(normal);
newFace->CopyProperties(attachedFace);
AddFace(newFace);
allNewFaces.Add(newFace);
newFaces.Add(newFace);
}
}
TriggerFaceReplaced(attachedFace, newFaces);
RemoveFace(attachedFace);
}
return allNewFaces;
}
void Mesh::ReplaceVertexFromFaces(const FaceList& faces, Vertex* oldVertex, Vertex* newVertex)
{
PERFORMANCER;
for (const auto& face : faces)
{
bool isRemoved = false;
for (int i = 0; i < face->vertices.Count(); ++i)
if (face->vertices[i] == oldVertex)
{
if (!isRemoved)
{
RemoveFace(face);
isRemoved = true;
}
face->vertices[i] = newVertex;
}
if (isRemoved)
AddFace(face);
}
}
void Mesh::ReplaceVertices(const VertexList& verticesToReplace, Vertex* newVertex)
{
PERFORMANCER;
for (const auto& vertex : verticesToReplace)
{
auto faces = vertex->GetFaces();
ReplaceVertexFromFaces(faces, vertex, newVertex);
}
}
void Mesh::RemoveFaceFromEdge(Edge* edge, Face* face)
{
PERFORMANCER;
edge->faces.Remove(face);
if (edge->faces.Count() == 0)
{
edge->mesh = nullptr;
for (const auto& vertex : edge->vertices)
RemoveEdgeFromVertex(vertex, edge);
delete edge;
}
}
void Mesh::RemoveEdgeFromVertex(Vertex* vertex, Edge* edge)
{
PERFORMANCER;
vertex->edges.Remove(edge);
if (vertex->edges.Count() == 0)
{
vertex->mesh = nullptr;
vertices.Remove(vertex);
}
}
Map<int, FaceList> Mesh::GetSubMeshs()
{
PERFORMANCER;
UpdateIndices();
Map<int, FaceList> groups;
Linq::GroupBy(this->GetFaces(), groups, [](Face* face)
{
return face->GetMaterialId();
});
return groups;
}
void Mesh::StartAutoUpdate(FaceList& faceList)
{
PERFORMANCER;
autoUpdateHandles.Add(&faceList);
}
void Mesh::StopAutoUpdate(FaceList& handle)
{
PERFORMANCER;
autoUpdateHandles.Remove(&handle);
}
void Mesh::TriggerFaceReplaced(Face* oldFace, const FaceList& newFaces)
{
PERFORMANCER;
for (const auto& faces : autoUpdateHandles)
{
auto index = faces->IndexOf(oldFace);
if (index == -1)
continue;
faces->RemoveAt(index);
for (auto const& newFace : newFaces)
if (!faces->Contains(newFace))
faces->Add(newFace);
}
}
///////////////////////////////////////////////
AutoFaceList::AutoFaceList(FaceList&& other)
: FaceList(other)
{
Init();
}
AutoFaceList::AutoFaceList(const FaceList& other)
: FaceList(other)
{
Init();
}
AutoFaceList::~AutoFaceList()
{
mesh->StopAutoUpdate(*this);
}
void AutoFaceList::Init()
{
mesh = (*this)[0]->GetMesh();
mesh->StartAutoUpdate(*this);
}

View File

@@ -0,0 +1,98 @@
#include "../../include/core/Path.h"
void Path::SetPoints(const List<vec3>& points)
{
PERFORMANCER;
this->points = points;
}
void Path::SetScale(const List<float>& scale)
{
PERFORMANCER;
this->scale = scale;
}
void Path::SetRotation(const List<float>& rotation)
{
PERFORMANCER;
this->rotation = rotation;
}
vec3 Path::GetPoint(float t) const
{
PERFORMANCER;
vec3 p1;
vec3 p2;
vec3 p0;
vec3 p3;
Path::GetValues(points, t, p0, p1, p2, p3);
return Spline(p0, p1, p2, p3, t);
}
float Path::GetScale(float t) const
{
PERFORMANCER;
if (scale.Count() == 0)
return 1.0f;
float p1;
float p2;
float p0;
float p3;
Path::GetValues(scale, t, p0, p1, p2, p3);
return Math::Max(0, Spline(p0, p1, p2, p3, t));
}
float Path::GetRotation(float t) const
{
PERFORMANCER;
if (rotation.Count() == 0)
return 0.0f;
float p1;
float p2;
float p0;
float p3;
Path::GetValues(rotation, t, p0, p1, p2, p3);
return Spline(p0, p1, p2, p3, t);
}
vec3 Path::GetDirection(float t) const
{
PERFORMANCER;
const float delta = 0.0001f;
auto a = GetPoint(t);
auto b = GetPoint(t + delta);
b -= a;
b.Normalize();
return b;
}
float Path::Spline(float p0, float p1, float p2, float p3, float t)
{
PERFORMANCER;
return 0.5f *((2 * p1) +
(-p0 + p2) * t +
(2 * p0 - 5 * p1 + 4 * p2 - p3) * (t*t) +
(-p0 + 3 * p1 - 3 * p2 + p3) * (t*t*t));
}
vec3 Path::Spline(const vec3& p0, const vec3& p1, const vec3& p2, const vec3& p3, float t)
{
PERFORMANCER;
return
{
Spline(p0.x, p1.x, p2.x, p3.x, t),
Spline(p0.y, p1.y, p2.y, p3.y, t),
Spline(p0.z, p1.z, p2.z, p3.z, t)
};
}

View File

@@ -0,0 +1,78 @@
#include "../../include/core/Triangle.h"
#include "../../include/core/Vertex.h"
Triangle::Triangle()
{
PERFORMANCER;
}
Triangle::Triangle(Vertex* a, Vertex* b, Vertex* c)
{
PERFORMANCER;
vertices.Add(a);
vertices.Add(b);
vertices.Add(c);
}
Triangle::~Triangle()
{
PERFORMANCER;
}
vec3 Triangle::GetNormal() const
{
PERFORMANCER;
auto v1 = vertices[0]->Position;
auto v2 = vertices[1]->Position;
auto v3 = vertices[2]->Position;
auto normal = vec3::Cross(v2 - v1, v3 - v1);
normal.Normalize();
return normal;
}
const List<Vertex*>& Triangle::GetVertices() const
{
PERFORMANCER;
return vertices;
}
bool Triangle::IsDegenerated() const
{
PERFORMANCER;
if (vertices[0] == vertices[1] || vertices[1] == vertices[2] || vertices[2] == vertices[0])
return true;
auto d = (vertices[0]->Position - vertices[1]->Position).GetLengthSq();
if (d <= Math::FloatEpsilon)
return true;
d = (vertices[1]->Position - vertices[2]->Position).GetLengthSq();
if (d <= Math::FloatEpsilon)
return true;
d = (vertices[2]->Position - vertices[0]->Position).GetLengthSq();
if (d <= Math::FloatEpsilon)
return true;
return false;
}
float Triangle::GetArea() const
{
PERFORMANCER;
// http://www.iquilezles.org/blog/?p=1579
auto a = (vertices[0]->Position - vertices[1]->Position).GetLengthSq();
auto b = (vertices[1]->Position - vertices[2]->Position).GetLengthSq();
auto c = (vertices[2]->Position - vertices[0]->Position).GetLengthSq();
auto area = (2 * a*b + 2 * b*c + 2 * c*a - a*a - b*b - c*c) / 16;
return Math::Sqrt(area);
}

View File

@@ -0,0 +1,136 @@
#include "../../include/core/Vertex.h"
#include "../../include/core/Face.h"
#include "../../include/core/Edge.h"
#include "../../include/core/Mesh.h"
Vertex::Vertex(const vec3& position)
: index{ -1 }
, normal{ nullptr }
, Position{position}
, mesh{ nullptr }
{
PERFORMANCER;
for (int i = 0; i < 4; ++i)
{
UV[i] = 0;
UV2[i] = 0;
}
}
Vertex::Vertex(Vertex* other)
: index{ -1 }
, normal{ nullptr }
, Position{ other->Position }
, mesh{ nullptr }
{
PERFORMANCER;
for (int i = 0; i < 4; ++i)
{
UV[i] = other->UV[i];
UV2[i] = other->UV2[i];
}
if (other->normal != nullptr)
normal = new vec3{*other->normal};
}
Vertex::~Vertex()
{
PERFORMANCER;
delete normal;
}
int Vertex::GetIndex() const
{
PERFORMANCER;
return index;
}
vec3 Vertex::GetNormal() const
{
PERFORMANCER;
if (normal == nullptr)
{
vec3 n;
for (const auto& face : GetFaces())
n += face->GetNormal();
n.Normalize();
return n;
}
else
return *normal;
}
void Vertex::SetNormal(const vec3& newNormal)
{
PERFORMANCER;
delete normal;
if (newNormal == vec3::zero)
normal = nullptr;
else
normal = new vec3{ newNormal };
}
VertexList Vertex::GetVertices() const
{
PERFORMANCER;
auto result = Linq::SelectUniqueMany<Vertex*>(edges, [](Edge* edge){ return edge->GetVertices(); });
result.Remove(const_cast<Vertex*>(this));
return result;
}
FaceList Vertex::GetFaces() const
{
PERFORMANCER;
FaceList result;
for (const auto& edge : edges)
for (const auto& face : edge->GetFaces())
if (!result.Contains(face))
result.Add(face);
//auto result = Linq::SelectUniqueMany<Face*>(edges, [](Edge* edge){ return edge->GetFaces(); });
return result;
}
const EdgeList& Vertex::GetEdges() const
{
PERFORMANCER;
return edges;
}
Mesh* Vertex::GetMesh() const
{
PERFORMANCER;
return mesh;
}
void Vertex::Interpolate(Vertex* other, float t)
{
Position.x = Math::Lerp(Position.x, other->Position.x, t);
Position.y = Math::Lerp(Position.y, other->Position.y, t);
Position.z = Math::Lerp(Position.z, other->Position.z, t);
if (other->HasManualNormal() && HasManualNormal())
{
normal->x = Math::Lerp(normal->x, other->normal->x, t);
normal->y = Math::Lerp(normal->y, other->normal->y, t);
normal->z = Math::Lerp(normal->z, other->normal->z, t);
}
for (int i = 0; i < 4; ++i)
{
UV[i] = Math::Lerp(UV[i], other->UV[i], t);
UV2[i] = Math::Lerp(UV2[i], other->UV2[i], t);
}
}

View File

@@ -0,0 +1,7 @@
#include "../../include/math/Math.h"
const float Math::Rad2Deg = 57.2957795f;
const float Math::Deg2Rad = 3.14159265359f / 180;
const float Math::FloatEpsilon = 1E-5f;
const float Math::Pi = 3.14159265359f;
const float Math::Infinity = 3.402823466e+38F;

View File

@@ -0,0 +1,63 @@
#include "../../include/math/Plane.h"
Plane::Plane(const vec3& normal, const vec3& point)
:normal { normal}
,distance { vec3::Dot(normal, point) }
{
}
Plane::Plane(const vec3& a, const vec3& b, const vec3& c)
{
normal = vec3::Cross(b-a, c - a);
normal.Normalize();
distance = vec3::Dot(normal, a);
}
Plane::~Plane()
{
}
float Plane::GetDistanceToPoint(const vec3& point) const
{
return vec3::Dot(normal, point) - distance;
}
vec3 Plane::GetNormal() const
{
return normal;
}
float Plane::GetDistance() const
{
return distance;
}
bool Plane::GetSide(const vec3& point) const
{
return GetDistanceToPoint(point) > 0.0;
}
void Plane::Flip()
{
normal = -normal;
distance = -distance;
}
float Plane::IntersectLinesegment(vec3 a, vec3 b, vec3& result) const
{
auto ba = b - a;
float nDotA = vec3::Dot(normal, a);
float nDotBA = vec3::Dot(normal, ba);
auto d = (distance - nDotA) / nDotBA;
result = a + (ba * d);
return d;
}

View File

@@ -0,0 +1,112 @@
#include "../../include/math/Quaternion.h"
#include "../../include/math/Math.h"
#include "../../include/math/vec3.h"
Quaternion Quaternion::identity;
Quaternion::Quaternion()
: x{ 0 }
, y{ 0 }
, z{ 0 }
, w{ 1 }
{
}
Quaternion Quaternion::CreateFromAxisAngle(const vec3& axis, float angle)
{
float halfAngle = angle * .5f;
float s = (float)Math::Sin(halfAngle);
Quaternion q;
q.x = axis.x * s;
q.y = axis.y * s;
q.z = axis.z * s;
q.w = (float)Math::Cos(halfAngle);
return q;
}
Quaternion Quaternion::LookRotation(const vec3& forward)
{
float dot = vec3::Dot(vec3::up, forward);
if (Math::Abs(dot - (-1.0f)) < 0.000001f)
{
return CreateFromAxisAngle(vec3::forward, Math::Pi);
}
if (Math::Abs(dot - (1.0f)) < 0.000001f)
{
return Quaternion::identity;
}
float rotAngle = (float)Math::Acos(dot);
vec3 rotAxis = vec3::Cross(vec3::up, forward);
rotAxis.Normalize();
return CreateFromAxisAngle(rotAxis, rotAngle);
}
Quaternion Quaternion::FromTo(vec3 v0, vec3 v1)
{
Quaternion q;
v0.Normalize();
v1.Normalize();
float d = vec3::Dot(v0, v1);
// If dot == 1, vectors are the same
if (d >= 1.0f)
{
return Quaternion::identity;
}
if (d < (1e-6f - 1.0f))
{
vec3 axis = vec3::Cross(vec3::right, v0);
if (axis.GetLengthSq() < Math::FloatEpsilon) // pick another if colinear
axis = vec3::Cross(vec3::up, v0);
axis.Normalize();
return Quaternion::CreateFromAxisAngle(axis, Math::Pi);
}
else
{
auto s = Math::Sqrt((1 + d) * 2);
auto invs = 1 / s;
auto c = vec3::Cross(v0, v1);
q.x = c.x * invs;
q.y = c.y * invs;
q.z = c.z * invs;
q.w = s * 0.5f;
q.Normalize();
}
return q;
}
void Quaternion::Inverse()
{
auto newX = w;
auto newY = -x;
auto newZ = -y;
auto newW = -z;
x = newX;
y = newY;
z = newZ;
w = newW;
Normalize();
}
void Quaternion::Normalize()
{
auto norm = w*w + x*x + y*y + z*z;
auto s = 1 / norm;
x *= s;
y *= s;
z *= s;
w *= s;
}

View File

@@ -0,0 +1,207 @@
#include "../../include/math/vec3.h"
#include "../../include/math/Math.h"
vec3 const vec3::zero = {0, 0, 0};
vec3 const vec3::one = {1,1,1};
vec3 const vec3::up = {0, 1, 0};
vec3 const vec3::down = {0, -1, 0};
vec3 const vec3::left = {-1, 0, 0};
vec3 const vec3::right = {1, 0, 0};
vec3 const vec3::forward = {0, 0, 1};
vec3 const vec3::back = {0, 0, -1};
vec3::vec3()
: x{0}
, y{0}
, z{0}
{
}
vec3::vec3(float x, float y, float z)
: x{ x }
, y{ y }
, z{ z }
{
}
vec3::vec3(const vec3& other)
: x{ other.x }
, y{ other.y }
, z{ other.z }
{
}
vec3::~vec3()
{
}
float vec3::GetLength() const
{
return Math::Sqrt(x*x + y*y + z*z);
}
float vec3::GetLengthSq() const
{
return x*x + y*y + z*z;
}
void vec3::Normalize()
{
auto length = GetLength();
if (length == 0)
{
x = y = z = 0;
return;
}
x /= length;
y /= length;
z /= length;
}
vec3 vec3::Mul(const vec3& a, const vec3& b)
{
return vec3(a.x * b.x, a.y * b.y, a.z * b.z);
}
float vec3::Dot(const vec3& a, const vec3& b)
{
float result = 0;
result += a.x * b.x;
result += a.y * b.y;
result += a.z * b.z;
return result;
}
vec3 vec3::Cross(const vec3& a, const vec3& b)
{
return vec3(
a.y * b.z - a.z * b.y,
a.z * b.x - a.x * b.z,
a.x * b.y - a.y * b.x
);
}
vec3 vec3::Min(const vec3& a, const vec3& b)
{
return {
Math::Min(a.x, b.x),
Math::Min(a.y, b.y),
Math::Min(a.z, b.z),
};
}
vec3 vec3::Max(const vec3& a, const vec3& b)
{
return{
Math::Max(a.x, b.x),
Math::Max(a.y, b.y),
Math::Max(a.z, b.z),
};
}
float vec3::Distance(const vec3& a, const vec3& b)
{
return (a - b).GetLength();
}
float vec3::DistanceSq(const vec3& a, const vec3& b)
{
return (a - b).GetLengthSq();
}
vec3 vec3::operator- () const
{
return{ -x, -y, -z };
}
bool vec3::operator== (const vec3& other) const
{
return x == other.x && y == other.y && z == other.z;
}
bool vec3::operator!= (const vec3& other) const
{
return x != other.x || y != other.y || z != other.z;
}
vec3 vec3::operator+ (const vec3& other) const
{
return{ x + other.x, y + other.y, z + other.z };
}
void vec3::operator+= (const vec3& other)
{
x += other.x;
y += other.y;
z += other.z;
}
void vec3::operator-= (const vec3& other)
{
x -= other.x;
y -= other.y;
z -= other.z;
}
void vec3::operator/= (float other)
{
x /= other;
y /= other;
z /= other;
}
void vec3::operator*= (float other)
{
x *= other;
y *= other;
z *= other;
}
vec3 vec3::operator- (const vec3& other) const
{
return{ x - other.x, y - other.y, z - other.z };
}
vec3 vec3::operator* (float other) const
{
return{ x * other, y * other, z * other };
}
vec3 vec3::operator/ (float other) const
{
return{ x / other, y / other, z / other };
}
vec3 vec3::operator* (const Quaternion& q) const
{
vec3 qxyz = { q.x, q.y, q.z };
auto t = vec3::Cross(qxyz, *this) * 2;
return *this + t * q.w + vec3::Cross(qxyz, t);
}

View File

@@ -0,0 +1,51 @@
#include "../../include/tools/CSG.h"
#include "../../include/tools/Delete.h"
#include "../../include/tools/Triangulate.h"
#include "../../include/tools/Copy.h"
#include "../../include/tools/CSGBase.h"
namespace mt
{
Mesh* CsgOperation(const FaceList& a, const FaceList& b, mt::CSG* (mt::CSG::*func)(mt::CSG*))
{
PERFORMANCER;
if (a.Count() == 0 || b.Count() == 0)
return{};
auto tmpA = mt::Triangulate(mt::Copy(a));
auto tmpB = mt::Triangulate(mt::Copy(b));
auto csgA = mt::CSG::FromPolygons(tmpA);
auto csgB = mt::CSG::FromPolygons(tmpB);
auto csgResult = (csgA->*func)(csgB);
auto result = mt::ToMesh(csgResult->GetFaces());
delete csgA;
delete csgB;
delete csgResult;
mt::Delete(tmpA);
mt::Delete(tmpB);
return result;
}
Mesh* CsgUnion(const FaceList& a, const FaceList& b)
{
PERFORMANCER;
return CsgOperation(a, b, &CSG::Union);
}
Mesh* CsgSubtract(const FaceList& a, const FaceList& b)
{
PERFORMANCER;
return CsgOperation(a, b, &CSG::Subtract);
}
Mesh* CsgIntersect(const FaceList& a, const FaceList& b)
{
PERFORMANCER;
return CsgOperation(a, b, &CSG::Intersect);
}
}

View File

@@ -0,0 +1,296 @@
#include "../../include/tools/CSGBase.h"
#include "../../include/tools/Flip.h"
#include "../../include/tools/Calculate.h"
namespace mt
{
enum FaceSplitState : int
{
Coplanar,
Front,
Back,
Spanning
};
void SplitFace(Face* face, const Plane& plane, FaceList& coplanarFront, FaceList& coplanarBack, FaceList& front, FaceList& back)
{
PERFORMANCER;
const float EPSILON = 0.00001f;
const auto& vertices = face->GetVertices();
int polygonType = 0;
FaceSplitState types[4];
for (int i = 0; i < vertices.Count(); ++i)
{
auto vertex = vertices[i];
auto t = vec3::Dot(plane.GetNormal(), vertex->Position) - plane.GetDistance();
auto type = (t < -EPSILON) ? FaceSplitState::Back : (t > EPSILON) ? FaceSplitState::Front : FaceSplitState::Coplanar;
polygonType |= type;
types[i] = type;
}
switch (polygonType)
{
case FaceSplitState::Coplanar:
(vec3::Dot(plane.GetNormal(), face->GetPlane().GetNormal()) > 0 ? coplanarFront : coplanarBack).Add(face);
break;
case FaceSplitState::Front:
front.Add(face);
break;
case FaceSplitState::Back:
back.Add(face);
break;
case FaceSplitState::Spanning:
VertexList f;
VertexList b;
for (int i = 0; i < vertices.Count(); ++i)
{
auto j = (i + 1) % vertices.Count();
auto ti = types[i];
auto tj = types[j];
auto vi = vertices[i];
auto vj = vertices[j];
if (ti != FaceSplitState::Back)
f.Add(vi);
if (ti != FaceSplitState::Front)
b.Add(vi);// ti != FaceSplitState::Back ? new Vertex(vi) : vi);
if ((ti | tj) == FaceSplitState::Spanning)
{
auto t = (plane.GetDistance() - vec3::Dot(plane.GetNormal(), vi->Position)) / vec3::Dot(plane.GetNormal(), vj->Position - vi->Position);
auto v = new Vertex(vi);
v->Interpolate(vj, t);
f.Add(v);
b.Add(v);// new Vertex(v));
}
}
if (f.Count() == 3)
front.Add(new Face(f));
else if (f.Count() == 4)
{
front.Add(new Face(f[0], f[1], f[2]));
front.Add(new Face(f[2], f[3], f[0]));
}
if (b.Count() == 3)
back.Add(new Face(b));
else if (b.Count() == 4)
{
back.Add(new Face(b[0], b[1], b[2]));
back.Add(new Face(b[2], b[3], b[0]));
}
break;
}
}
CSG* CSG::FromPolygons(const FaceList& faces)
{
auto csg = new CSG();
csg->faces = faces;
return csg;
}
CSG* CSG::Clone()
{
auto csg = new CSG();
csg->faces = faces;// mt::Clone(faces);
return csg;
}
CSG* CSG::Inverse()
{
auto csg = Clone();
for (const auto& face : csg->faces)
face->FlipNormal();
return csg;
}
const FaceList& CSG::GetFaces() const
{
return faces;
}
CSG* CSG::Union(CSG* other)
{
auto a = new CSGNode(Clone()->faces);
auto b = new CSGNode(other->Clone()->faces);
a->ClipTo(b);
b->ClipTo(a);
b->Invert();
b->ClipTo(a);
b->Invert();
a->Build(b->AllPolygons());
return CSG::FromPolygons(a->AllPolygons());
}
CSG* CSG::Subtract(CSG* other)
{
auto a = new CSGNode(Clone()->faces);
auto b = new CSGNode(other->Clone()->faces);
a->Invert();
a->ClipTo(b);
b->ClipTo(a);
b->Invert();
b->ClipTo(a);
b->Invert();
a->Build(b->AllPolygons());
a->Invert();
return CSG::FromPolygons(a->AllPolygons());
}
CSG* CSG::Intersect(CSG* other)
{
auto a = new CSGNode(Clone()->faces);
auto b = new CSGNode(other->Clone()->faces);
a->Invert();
b->ClipTo(a);
b->Invert();
a->ClipTo(b);
b->ClipTo(a);
a->Build(b->AllPolygons());
a->Invert();
return CSG::FromPolygons(a->AllPolygons());
}
/////////////////////////////////////////////////////////////////////
CSGNode::CSGNode()
: front{ nullptr }
, back{ nullptr }
, plane{ nullptr }
{
PERFORMANCER;
}
CSGNode::CSGNode(const FaceList& faces)
: CSGNode()
{
PERFORMANCER;
this->Build(faces);
}
CSGNode::~CSGNode()
{
PERFORMANCER;
if (plane)
delete plane;
if (front)
delete front;
if (back)
delete back;
}
CSGNode* CSGNode::Clone()
{
PERFORMANCER;
auto node = new CSGNode();
node->plane = plane ? new Plane(*plane) : nullptr;
node->front = front ? front->Clone() : nullptr;
node->back = back ? back->Clone() : nullptr;
node->faces = faces;// mt::Clone(faces);
return node;
}
void CSGNode::Invert()
{
PERFORMANCER;
mt::FlipSides(faces);
if (plane)
plane->Flip();
if (front)
front->Invert();
if (back)
back->Invert();
auto temp = front;
front = back;
back = temp;
}
FaceList CSGNode::ClipPolygons(const FaceList& other)
{
PERFORMANCER;
if (!plane)
return other;
FaceList front;
FaceList back;
for (const auto& face : other)
SplitFace(face, *plane, front, back, front, back);
if (this->front)
front = this->front->ClipPolygons(front);
if (this->back)
back = this->back->ClipPolygons(back);
else
back.Clear();
return Linq::Concat(front, back);
}
void CSGNode::ClipTo(CSGNode* other)
{
PERFORMANCER;
faces = other->ClipPolygons(faces);
if (front)
front->ClipTo(other);
if (back)
back->ClipTo(other);
}
FaceList CSGNode::AllPolygons()
{
PERFORMANCER;
auto result = faces;
if (front)
result = Linq::Concat(result, front->AllPolygons());
if (back)
result = Linq::Concat(result, back->AllPolygons());
return result;
}
void CSGNode::Build(const FaceList& faces)
{
PERFORMANCER;
if (faces.Count() == 0)
return;
if (!plane)
plane = new Plane(faces[0]->GetPlane());
FaceList front;
FaceList back;
for (const auto& face : faces)
SplitFace(face, *plane, this->faces, this->faces, front, back);
if (front.Count() != 0)
{
if (!this->front)
this->front = new CSGNode();
this->front->Build(front);
}
if (back.Count() != 0)
{
if (!this->back)
this->back = new CSGNode();
this->back->Build(back);
}
}
}

View File

@@ -0,0 +1,110 @@
#include "../../include/tools/Calculate.h"
namespace mt
{
Bounds GetBoundingBox(const VertexList& vertices)
{
PERFORMANCER;
if (vertices.Count() == 0)
return{ vec3::zero, vec3::zero };
auto min = vertices[0]->Position;
auto max = min;
for (const auto& vertex : vertices)
{
min = vec3::Min(vertex->Position, min);
max = vec3::Max(vertex->Position, max);
}
return{ min, max };
}
vec3 GetNormal(const VertexList& vertices)
{
PERFORMANCER;
vec3 normal;
if (vertices.Count() != 0)
{
for (const auto& vertex : vertices)
normal += vertex->GetNormal();
normal.Normalize();
}
return normal;
}
vec3 GetNormal(const FaceList& faces)
{
PERFORMANCER;
vec3 normal;
if (faces.Count() != 0)
{
for (const auto& face: faces)
normal += face->GetNormal();
normal.Normalize();
}
return normal;
}
vec3 GetCenter(const VertexList& vertices)
{
PERFORMANCER;
auto bounds = GetBoundingBox(vertices);
return bounds.GetCenter();
}
EdgeList GetHalfEdges(const EdgeList& edges, const FaceList& faces)
{
PERFORMANCER;
List<Edge*> result;
for (const auto& edge : edges)
{
auto edgeFaces = edge->GetFaces();
Face* relevantFace = nullptr;
for (const auto& edgeFace : edgeFaces)
{
for (const auto& face : faces)
{
if (face == edgeFace)
{
relevantFace = edgeFace;
break;
}
}
if (relevantFace != nullptr)
break;
}
result.Add(edge->GetHalfEdge(relevantFace));
}
return result;
}
EdgeList GetOutlineEdges(const FaceList& faces)
{
PERFORMANCER;
auto faceSet = Linq::ToSet(faces);
EdgeList outlineEdges;
for (const auto& face : faces)
{
for (const auto& edge : face->GetEdges())
{
auto edgeFaces = edge->GetFaces();
if (edgeFaces.Count() > 1)
for (const auto& edgeFace : edgeFaces)
if (edgeFace != face && faceSet.ContainsKey(edgeFace))
goto fastBreak;
outlineEdges.Add(edge);
fastBreak:; // soooo ugly
}
}
return outlineEdges;
}
}

View File

@@ -0,0 +1,109 @@
#include "../../include/tools/Copy.h"
#include "../../include/tools/Calculate.h"
#include "../../include/tools/Select.h"
#include "../../include/tools/Transform.h"
namespace mt
{
FaceList CreateCopy(const FaceList& faces, Mesh* outMesh)
{
PERFORMANCER;
if (faces.Count() == 0)
return{};
auto vertices = mt::GetVertices(faces);
Map<Vertex*, Vertex*> originalToClone;
for (const auto& vertex : vertices)
originalToClone.Add(vertex, new Vertex(vertex));
FaceList newFaces;
auto mesh = outMesh ? outMesh : faces[0]->GetMesh();
//mesh.Materials = Owner.Materials;
for (const auto& face : (FaceList)faces)
{
Face* newFace;
auto vertices = face->GetVertices();
if (face->IsTriangle())
newFace = new Face(
originalToClone[vertices[0]],
originalToClone[vertices[1]],
originalToClone[vertices[2]]
);
else
newFace = new Face(
originalToClone[vertices[0]],
originalToClone[vertices[1]],
originalToClone[vertices[2]],
originalToClone[vertices[3]]
);
newFace->CopyProperties(face);
newFaces.Add(newFace);
mesh->AddFace(newFace);
}
return newFaces;
}
FaceList CopyToMesh(const FaceList& faces, Mesh* target)
{
PERFORMANCER;
const auto& vertices = mt::GetVertices(faces);
Map<Vertex*, Vertex*> originalToClone;
for (const auto& vertex : vertices)
originalToClone.Add(vertex, new Vertex(vertex));
FaceList newFaces;
for (const auto& face : faces)
{
if (face->GetMesh() == target)
{
newFaces.Add(face);
continue;
}
auto faceVertices = face->GetVertices();
Face* newFace;
if (face->IsTriangle())
newFace = new Face(
originalToClone[faceVertices[0]],
originalToClone[faceVertices[1]],
originalToClone[faceVertices[2]]);
else
newFace = new Face(
originalToClone[faceVertices[0]],
originalToClone[faceVertices[1]],
originalToClone[faceVertices[2]],
originalToClone[faceVertices[3]]);
newFace->CopyProperties(face);
newFaces.Add(newFace);
target->AddFace(newFace);
}
return newFaces;
}
FaceList Copy(const FaceList& faces)
{
PERFORMANCER;
return CreateCopy(faces, nullptr);
}
Mesh* ToMesh(const FaceList& faces)
{
PERFORMANCER;
auto mesh = new Mesh();
CreateCopy(faces, mesh);
return mesh;
}
}

View File

@@ -0,0 +1,14 @@
#include "../../include/tools/Delete.h"
#include "../../include/tools/Calculate.h"
#include "../../include/tools/Select.h"
#include "../../include/tools/Transform.h"
namespace mt
{
void Delete(const FaceList& faces)
{
PERFORMANCER;
for (const auto& face : (FaceList)faces)
face->GetMesh()->RemoveFace(face);
}
}

View File

@@ -0,0 +1,96 @@
#include "../../include/tools/Detach.h"
#include "../../include/tools/Calculate.h"
#include "../../include/tools/Select.h"
#include "../../include/tools/Transform.h"
namespace mt
{
Vertex* CloneVertex(Map<Vertex*, Vertex*>& parentToCloneVertices, Vertex* oldVertex, bool& isNewVertex)
{
PERFORMANCER;
Vertex* newVertex;
if (parentToCloneVertices.TryGetValue(oldVertex, newVertex))
return newVertex;
newVertex = new Vertex(oldVertex);
parentToCloneVertices.Add(oldVertex, newVertex);
return newVertex;
}
Map<Vertex*, Vertex*> Detach(const FaceList& faces, bool preserveVertexNormals, EdgeList* outline)
{
PERFORMANCER;
bool customOutline = outline != nullptr;
if (!customOutline)
{
outline = new List<Edge*>();
*outline = mt::GetOutlineEdges(faces);
}
Map<Vertex*, vec3> parentToNormals;
Map<Vertex*, Vertex*> parentToCloneVertices;
if (preserveVertexNormals)
{
auto vertices = Linq::SelectUniqueMany<Vertex*>(*outline, [](Edge* edge){ return edge->GetVertices(); });
for (const auto& vertex : vertices)
parentToNormals.Add(vertex, vertex->GetNormal());
}
for (const auto& edge : *outline)
{
auto edgeVertices = edge->GetVertices();
auto original0 = edgeVertices[0];
auto original1 = edgeVertices[1];
bool isNewVertex0;
bool isNewVertex1;
auto clone0 = CloneVertex(parentToCloneVertices, original0, isNewVertex0);
auto clone1 = CloneVertex(parentToCloneVertices, original1, isNewVertex1);
if (preserveVertexNormals)
{
if (isNewVertex0 && parentToNormals.ContainsKey(original0))
clone0->SetNormal(parentToNormals[original0]);
if (isNewVertex1 && parentToNormals.ContainsKey(original1))
clone1->SetNormal(parentToNormals[original1]);
}
original0->GetMesh()->ReplaceVertexFromFaces(faces, original0, clone0);
original1->GetMesh()->ReplaceVertexFromFaces(faces, original1, clone1);
}
if (!customOutline)
delete outline;
return parentToCloneVertices;
}
void DetachAll(const FaceList& faces, bool preserveVertexNormals)
{
PERFORMANCER;
Map<Vertex*, vec3> parentToNormals;
if (preserveVertexNormals)
{
for (const auto& vertex : mt::GetVertices(faces))
parentToNormals.Add(vertex, vertex->GetNormal());
}
for (const auto& face : faces)
{
auto vertices = face->GetVertices();
for (const auto& vertex : vertices)
{
auto newVertex = new Vertex(vertex);
if (preserveVertexNormals && parentToNormals.ContainsKey(vertex))
newVertex->SetNormal(parentToNormals[vertex]);
List<Face*> tmpFaces;
tmpFaces.Add(face);
vertex->GetMesh()->ReplaceVertexFromFaces(tmpFaces, vertex, newVertex);
}
}
}
}

View File

@@ -0,0 +1,163 @@
#include "../../include/tools/Extrude.h"
#include "../../include/tools/Calculate.h"
#include "../../include/tools/Select.h"
#include "../../include/tools/Transform.h"
#include "../../include/tools/Detach.h"
namespace mt
{
FaceList ExtrudeVertices(const VertexList& vertices, float sideEdgeLength, float extrudeHeight)
{
PERFORMANCER;
Map<Vertex*, vec3> vertexNormals;
for (const auto& vertex : vertices)
vertexNormals.Add(vertex, vertex->GetNormal());
for (const auto& vertex : vertices)
{
auto vertexEdges = vertex->GetEdges();
for (const auto& edge : vertexEdges)
{
auto otherEdgeVertex = edge->GetOtherVertex(vertex);
auto direction = otherEdgeVertex->Position - vertex->Position;
auto length = direction.GetLength();
if (length == 0)
continue;
direction.Normalize();
length = Math::Min(length, sideEdgeLength);
auto splitVertex = new Vertex(vertex->Position + direction * length);
vertex->GetMesh()->SplitEdge(edge, splitVertex);
}
}
List<Face*> newFaces;
for (const auto& vertex : vertices)
{
auto normal = vertexNormals[vertex];
vertex->Position += normal * extrudeHeight;
}
return newFaces;
}
FaceList Extrude(const FaceList& faces, float factor, MoveMode mode)
{
PERFORMANCER;
return Bevel(faces, factor, 1.0f, mode);
}
FaceList Extrude(const FaceList& faces, const vec3& extrudeDirection)
{
PERFORMANCER;
return Bevel(faces, extrudeDirection, 1.0f);
}
FaceList Bevel(const FaceList& faces, const vec3& extrudeDirection, float capScale)
{
PERFORMANCER;
return Bevel(faces, 0.0f, capScale, MoveMode::UseCollectionNormal, &extrudeDirection);
}
FaceList Bevel(const FaceList& faces, float factor, float capScale, MoveMode mode, const vec3* extrudeDirectionOverride)
{
PERFORMANCER;
if (mode == MoveMode::UseCollectionNormal && extrudeDirectionOverride == nullptr && mt::GetNormal(mt::GetVertices(faces)) == vec3::zero)
return{};
auto oldOutlineEdges = mt::GetHalfEdges(mt::GetOutlineEdges(faces), faces);
auto oldToNewVertices = mt::Detach(faces, false, &oldOutlineEdges);
auto newOutlineEdges = mt::GetHalfEdges(mt::GetOutlineEdges(faces), faces);
const auto& vertices = mt::GetVertices(faces);
if (extrudeDirectionOverride != nullptr)
mt::MoveBy(vertices, *extrudeDirectionOverride);
else
mt::MoveBy(vertices, factor, mode);
if (capScale != 1.0f)
mt::Scale(vertices, capScale, mt::CenterMode::FaceCenter);
FaceList sideFaces;
Map<Vertex*, Vertex*> parentToCloneVertices;
for (int i = 0; i < newOutlineEdges.Count(); ++i)
{
auto oldEdge = oldOutlineEdges[i];
auto newEdge = newOutlineEdges[i];
Face* relevantFace = nullptr;
for (const auto& face : newEdge->GetFaces())
if (faces.Contains(face))
{
relevantFace = face;
break;
}
auto oldEdgeVertices = oldEdge->GetVertices();
auto original0 = oldEdgeVertices[0];
auto original1 = oldEdgeVertices[1];
auto target0 = oldToNewVertices[oldEdgeVertices[0]];
auto target1 = oldToNewVertices[oldEdgeVertices[1]];
Face* sideFace;
if (factor < 0)
sideFace = new Face(target1, target0, original0, original1);
else
sideFace = new Face(original0, original1, target1, target0);
sideFace->CopyProperties(relevantFace);
sideFaces.Add(sideFace);
relevantFace->GetMesh()->AddFace(sideFace);
}
return sideFaces;
}
List<FaceList> Loft(const FaceList& faces, const Path& path, int segments)
{
PERFORMANCER;
List<FaceList> result;
for (int segment = 0; segment < segments; ++segment)
{
auto progress = (1.0f + segment) / segments;
auto sides = mt::Extrude(faces, 1);
if (segment == 0)
{
FaceList tmp;
for (int i = 0; i < sides.Count(); ++i)
result.Add(tmp);
}
for (int i = 0; i < sides.Count(); ++i)
result[i].Add(sides[i]);
auto position = path.GetPoint(progress);
auto rotation = path.GetRotation(progress);
auto scale = path.GetScale(progress);
auto direction = path.GetDirection(progress);
auto vertices = mt::GetVertices(faces);
mt::MoveTo(vertices, position);
mt::Scale(vertices, scale, mt::CenterMode::CollectionCenter);
mt::Rotate(vertices, mt::GetNormal(vertices), direction);
mt::Rotate(vertices, mt::GetNormal(vertices), rotation);
}
return result;
}
}

View File

@@ -0,0 +1,227 @@
#include "../../include/tools/FFD.h"
#include "../../include/tools/Select.h"
namespace mt
{
FFD::FFD(const FaceList& faces, int l, int m, int n, FFDControlPointMode ffdControlPointMode)
: faces{ faces }
, L{ l }
, M{ m }
, N{ n }
, ffdControlPointMode{ ffdControlPointMode }
{
PERFORMANCER;
originalVertices = mt::GetVertices(faces);
Parameterize();
}
FFD::~FFD()
{
PERFORMANCER;
for (int i = 0; i < L + 1; ++i)
{
for (int j = 0; j < M + 1; ++j)
{
delete [] ControlPoints[i][j];
}
delete [] ControlPoints[i];
}
delete [] ControlPoints;
}
void FFD::Apply()
{
PERFORMANCER;
int idx = 0;
for (const auto& vp : vertexParams)
{
auto p = getWorldVector3(vp);
originalVertices[idx++]->Position = p;
}
}
List<vec3*> FFD::SelectPoints(int startX, int endX, int startY, int endY, int startZ, int endZ)
{
List<vec3*> result;
for (int x = startX; x <= endX; ++x)
for (int y = startY; y <= endY; ++y)
for (int z = startZ; z <= endZ; ++z)
result.Add(&ControlPoints[x][y][z]);
return result;
}
float FFD::binomialCoeff(int n, int k)
{
PERFORMANCER;
float total = 1.0f;
for (int i = 1; i <= k; i++)
{
total *= (n - (k - i)) / (float)i;
}
return total;
}
float FFD::bernsteinPoly(int n, int v, float x)
{
PERFORMANCER;
return binomialCoeff(n, v) * Math::Pow(x, (float)v) * Math::Pow((float)(1.0f - x), (float)(n - v));
}
void FFD::calculateSTU(const vec3& max, const vec3& min)
{
PERFORMANCER;
S = { max.x - min.x, 0.0f, 0.0f };
T = { 0.0f, max.y - min.y, 0.0f };
U = { 0.0f, 0.0f, max.z - min.z };
}
void FFD::calculateTrivariateBernsteinPolynomial(const vec3& p0)
{
PERFORMANCER;
vec3 TcU = vec3::Cross(T, U);
vec3 ScU = vec3::Cross(S, U);
vec3 ScT = vec3::Cross(S, T);
float TcUdS = vec3::Dot(TcU, S);
float ScUdT = vec3::Dot(ScU, T);
float ScTdU = vec3::Dot(ScT, U);
for (int v = 0; v < originalVertices.Count(); v++)
{
vec3 diff = originalVertices[v]->Position - p0;
Vector3Param tmp;
tmp.s = vec3::Dot(TcU, diff / TcUdS);
tmp.t = vec3::Dot(ScU, diff / ScUdT);
tmp.u = vec3::Dot(ScT, diff / ScTdU);
tmp.p = p0 + (S * tmp.s) + (T * tmp.t) + (U * tmp.u);
tmp.p0 = p0;
{ // Reserve room for each bernstein polynomial pack.
tmp.bernPolyPack.Add(List<float>(L)); //outer bernstein poly
tmp.bernPolyPack.Add(List<float>(M)); //middle bernstein poly
tmp.bernPolyPack.Add(List<float>(N)); //inner bernstein poly
}
{ // Pre-calculate bernstein polynomial expansion. It only needs to be done once per parameterization
for (int i = 0; i <= L; i++)
{
for (int j = 0; j <= M; j++)
{
for (int k = 0; k <= N; k++)
{
tmp.bernPolyPack[2].Add(bernsteinPoly(N, k, tmp.u));
}
tmp.bernPolyPack[1].Add(bernsteinPoly(M, j, tmp.t));
}
tmp.bernPolyPack[0].Add(bernsteinPoly(L, i, tmp.s));
}
}
vertexParams.Add(tmp);
if (vec3::Distance(tmp.p, originalVertices[v]->Position) > 0.001f)
{
//Debug.Log("Warning, mismatched parameterization");
}
}
}
void FFD::Parameterize()
{
PERFORMANCER;
vec3 min = { Math::Infinity, Math::Infinity, Math::Infinity };
vec3 max = { -Math::Infinity, -Math::Infinity, -Math::Infinity };
for (const auto& v : mt::GetVertices(faces))
{
max = vec3::Max(v->Position, max);
min = vec3::Min(v->Position, min);
}
calculateSTU(max, min);
calculateTrivariateBernsteinPolynomial(min);
createControlPoints(min);
}
void FFD::createControlPoints(const vec3& origin)
{
PERFORMANCER;
ControlPoints = new vec3**[L + 1];
for (int i = 0; i < L + 1; ++i)
{
ControlPoints[i] = new vec3*[M + 1];
for (int j = 0; j < M + 1; ++j)
ControlPoints[i][j] = new vec3[N + 1];
}
for (int i = 0; i <= L; i++)
{
for (int j = 0; j <= M; j++)
{
for (int k = 0; k <= N; k++)
{
auto matches = (i == 0 || i == L) ? 1 : 0;
matches += (j == 0 || j == M) ? 1 : 0;
matches += (k == 0 || k == N) ? 1 : 0;
if (ffdControlPointMode == FFDControlPointMode::Surface && matches < 1)
continue;
else if (ffdControlPointMode == FFDControlPointMode::Edges && matches < 2)
continue;
ControlPoints[i][j][k] = createControlPoint(origin, i, j, k);
}
}
}
}
vec3 FFD::createControlPoint(const vec3& p0, int i, int j, int k)
{
PERFORMANCER;
auto position = p0 + (S * (i / (float)L)) + (T * (j / (float)M)) + (U * (k / (float)N));
return position;
}
vec3 FFD::getWorldVector3(const Vector3Param& r)
{
PERFORMANCER;
int l = L;
int m = M;
int n = N;
vec3 tS = vec3::zero;
for (int i = 0; i <= l; i++)
{
vec3 tM = vec3::zero;
for (int j = 0; j <= m; j++)
{
vec3 tK = vec3::zero;
for (int k = 0; k <= n; k++)
{
auto matches = (i == 0 || i == L) ? 1 : 0;
matches += (j == 0 || j == M) ? 1 : 0;
matches += (k == 0 || k == N) ? 1 : 0;
if (ffdControlPointMode == FFDControlPointMode::Surface && matches < 1)
continue;
else if (ffdControlPointMode == FFDControlPointMode::Edges && matches < 2)
continue;
tK += ControlPoints[i][j][k] * r.bernPolyPack[2][k];
}
tM += tK * r.bernPolyPack[1][j];
}
tS += tM * r.bernPolyPack[0][i];
}
return tS;
}
}

View File

@@ -0,0 +1,14 @@
#include "../../include/tools/Flip.h"
#include "../../include/tools/Calculate.h"
#include "../../include/tools/Select.h"
#include "../../include/tools/Transform.h"
namespace mt
{
void FlipSides(const FaceList& faces)
{
PERFORMANCER;
for (const auto& face : faces)
face->FlipNormal();
}
}

View File

@@ -0,0 +1,60 @@
#include "../../include/tools/Group.h"
#include "../../include/tools/Calculate.h"
#include "../../include/tools/Select.h"
#include "../../include/tools/Transform.h"
namespace mt
{
void CheckFace(float angleThreshold, Map<Face*, Face*>& checkedFaces, FaceList& nextFaces, FaceList& groupFaces)
{
PERFORMANCER;
auto currentFace = nextFaces[nextFaces.Count() - 1];
nextFaces.RemoveAt(nextFaces.Count() - 1);
groupFaces.Add(currentFace);
for (const auto& edge : currentFace->GetEdges())
{
auto edgeFaces = edge->GetFaces();
if (edgeFaces.Count() != 2)
continue;
auto otherFace = edgeFaces[0] == currentFace ? edgeFaces[1] : edgeFaces[0];
if (!checkedFaces.ContainsKey(otherFace))
{
auto dotResult = vec3::Dot(currentFace->GetNormal(), otherFace->GetNormal());
if (dotResult >= 1 - angleThreshold)
{
nextFaces.Add(otherFace);
checkedFaces.Add(otherFace, otherFace);
}
}
}
}
List<FaceList> GetGroups(const FaceList& faces, float threshold)
{
PERFORMANCER;
auto currentFaces = faces;
List<FaceList> groups;
while (currentFaces.Count() > 0)
{
FaceList pendingFaces;
Map<Face*, Face*> checkedFaces;
FaceList groupFaces;
auto currentFace = faces[0];
checkedFaces.Add(currentFace, currentFace);
pendingFaces.Add(currentFace);
while (pendingFaces.Count() != 0)
CheckFace(threshold, checkedFaces, pendingFaces, groupFaces);
groups.Add(groupFaces);
currentFaces = Linq::Except(currentFaces, groupFaces);
}
return groups;
}
}

View File

@@ -0,0 +1,115 @@
#include "../../include/tools/Prefab.h"
#include "../../include/tools/Weld.h"
#include "../../include/tools/Subdivide.h"
namespace mt
{
Face* CreateQuad(const vec3& size, const vec3& position, PlaneDirection direction)
{
PERFORMANCER;
auto face = new Face(
new Vertex(vec3::zero),
new Vertex(vec3::zero),
new Vertex(vec3::zero),
new Vertex(vec3::zero));
auto halfSize = size * 0.5f;
auto& vertices = face->GetVertices();
switch (direction)
{
case PlaneDirection::Forward:
vertices[0]->Position = position + vec3(halfSize.x, halfSize.y, 0);
vertices[1]->Position = position + vec3(-halfSize.x, halfSize.y, 0);
vertices[2]->Position = position + vec3(-halfSize.x, -halfSize.y, 0);
vertices[3]->Position = position + vec3(halfSize.x, -halfSize.y, 0);
break;
case PlaneDirection::Backward:
vertices[0]->Position = position + vec3(-halfSize.x, halfSize.y, 0);
vertices[1]->Position = position + vec3(halfSize.x, halfSize.y, 0);
vertices[2]->Position = position + vec3(halfSize.x, -halfSize.y, 0);
vertices[3]->Position = position + vec3(-halfSize.x, -halfSize.y, 0);
break;
case PlaneDirection::Left:
vertices[0]->Position = position + vec3(0, halfSize.y, halfSize.x);
vertices[1]->Position = position + vec3(0, halfSize.y, -halfSize.x);
vertices[2]->Position = position + vec3(0, -halfSize.y, -halfSize.x);
vertices[3]->Position = position + vec3(0, -halfSize.y, halfSize.x);
break;
case PlaneDirection::Right:
vertices[0]->Position = position + vec3(0, halfSize.y, -halfSize.x);
vertices[1]->Position = position + vec3(0, halfSize.y, halfSize.x);
vertices[2]->Position = position + vec3(0, -halfSize.y, halfSize.x);
vertices[3]->Position = position + vec3(0, -halfSize.y, -halfSize.x);
break;
case PlaneDirection::Up:
vertices[0]->Position = position + vec3(-halfSize.x, 0, halfSize.y);
vertices[1]->Position = position + vec3(halfSize.x, 0, halfSize.y);
vertices[2]->Position = position + vec3(halfSize.x, 0, -halfSize.y);
vertices[3]->Position = position + vec3(-halfSize.x, 0, -halfSize.y);
break;
case PlaneDirection::Down:
vertices[0]->Position = position + vec3(halfSize.x, 0, halfSize.y);
vertices[1]->Position = position + vec3(-halfSize.x, 0, halfSize.y);
vertices[2]->Position = position + vec3(-halfSize.x, 0, -halfSize.y);
vertices[3]->Position = position + vec3(halfSize.x, 0, -halfSize.y);
break;
}
return face;
}
Mesh* CreatePlane(const vec3& position, float edgeSize, PlaneDirection direction)
{
PERFORMANCER;
auto mesh = new Mesh();
vec3 size = { edgeSize, edgeSize, edgeSize };
mesh->AddFace(CreateQuad(size, position, direction));
return mesh;
}
Mesh* CreateCube(bool weldVertices, const vec3& position, float edgeSize)
{
PERFORMANCER;
auto mesh = new Mesh();
auto halfSize = edgeSize / 2;
vec3 size = { edgeSize, edgeSize, edgeSize };
mesh->AddFace(CreateQuad(size, position + vec3{ 0, halfSize, 0 }, PlaneDirection::Up));
mesh->AddFace(CreateQuad(size, position + vec3{ 0, -halfSize, 0 }, PlaneDirection::Down));
mesh->AddFace(CreateQuad(size, position + vec3{ halfSize, 0, 0 }, PlaneDirection::Right));
mesh->AddFace(CreateQuad(size, position + vec3{ -halfSize, 0, 0 }, PlaneDirection::Left));
mesh->AddFace(CreateQuad(size, position + vec3{ 0, 0, halfSize }, PlaneDirection::Forward));
mesh->AddFace(CreateQuad(size, position + vec3{ 0, 0, -halfSize }, PlaneDirection::Backward));
const auto& faces = mesh->GetFaces();
faces[0]->AddTag(Tags::DirectionUp);
faces[1]->AddTag(Tags::DirectionDown);
faces[2]->AddTag(Tags::DirectionRight);
faces[3]->AddTag(Tags::DirectionLeft);
faces[4]->AddTag(Tags::DirectionFront);
faces[5]->AddTag(Tags::DirectionBack);
if (weldVertices)
mt::Weld(mesh->GetVertices());
return mesh;
}
Mesh* CreateSphere(int tesselationLevel, const vec3& position, float baseSize)
{
auto mesh = CreateCube(true, position, baseSize);
for (int i = 0; i < tesselationLevel; ++i)
mt::Subdivide(mesh->GetFaces(), true);
return mesh;
}
}

View File

@@ -0,0 +1,42 @@
#include "../../include/tools/Project.h"
#include "../../include/tools/Calculate.h"
#include "../../include/tools/Select.h"
#include "../../include/tools/Transform.h"
namespace mt
{
void Flatten(const VertexList& vertices)
{
PERFORMANCER;
Flatten(vertices, mt::GetNormal(vertices));
}
void Flatten(const VertexList& vertices, const vec3& normal)
{
PERFORMANCER;
Project(vertices, { normal, mt::GetCenter(vertices) });
}
void Project(const VertexList& vertices, const Plane& plane)
{
PERFORMANCER;
for (const auto& vertex : vertices)
{
auto distance = plane.GetDistanceToPoint(vertex->Position);
vertex->Position -= plane.GetNormal() * distance;
}
}
void Clamp(const VertexList& vertices, const Plane& plane)
{
PERFORMANCER;
for (const auto& vertex : vertices)
{
auto distance = plane.GetDistanceToPoint(vertex->Position);
if (distance < 0)
vertex->Position -= plane.GetNormal() * distance;
}
}
}

View File

@@ -0,0 +1,155 @@
#include "../../include/tools/Select.h"
namespace mt
{
FaceList GetFacesByDirection(const FaceList& faces, const vec3 direction, float threshold, bool mirror)
{
PERFORMANCER;
FaceList selected;
for (const auto& face : faces)
{
auto dot = vec3::Dot(face->GetNormal(), direction);
if (mirror)
dot = Math::Abs(dot);
if (dot >= 1 - threshold)
selected.Add(face);
}
return selected;
}
FaceList GetFacesOnPositivePlane(const FaceList& faces, const Plane& plane)
{
PERFORMANCER;
FaceList selected;
for (const auto& face : faces)
{
for (const auto& vertex : face->GetVertices())
if (plane.GetSide(vertex->Position))
{
selected.Add(face);
break;
}
}
return selected;
}
VertexList GetVerticesOnPositivePlane(const VertexList& vertices, const Plane& plane)
{
PERFORMANCER;
VertexList selected;
for (const auto& vertex : vertices)
{
if (plane.GetSide(vertex->Position))
{
selected.Add(vertex);
}
}
return selected;
}
FaceList GetFacesWithTag(const FaceList& faces, int tag)
{
PERFORMANCER;
FaceList selected;
for (const auto& face : faces)
{
if (face->HasTag(tag))
selected.Add(face);
}
return selected;
}
FaceList GetFacesWithTags(const FaceList& faces, List<int> tags, bool canMatchAnyTag)
{
PERFORMANCER;
FaceList selected;
for (const auto& face : faces)
{
if (canMatchAnyTag ? face->HasTagsAny(tags) : face->HasTagsAll(tags))
selected.Add(face);
}
return selected;
}
FaceList GetFacesAt(const FaceList& faces, const vec3& position, float range)
{
PERFORMANCER;
FaceList selected;
for (const auto& face : faces)
{
for (const auto& vertex : face->GetVertices())
if (vec3::Distance(vertex->Position, position) <= range)
{
selected.Add(face);
break;
}
}
return selected;
}
VertexList GetVerticesAt(const VertexList& vertices, const vec3& position, float range)
{
PERFORMANCER;
VertexList selected;
for (const auto& vertex : vertices)
if (vec3::Distance(vertex->Position, position) <= range)
{
selected.Add(vertex);
}
return selected;
}
Face* GetFaceAt(const FaceList& faces, const vec3& position, float range)
{
PERFORMANCER;
Face* nearest = nullptr;
float nearestDistance = Math::Infinity;
for (const auto& face : faces)
{
for (const auto& vertex : face->GetVertices())
{
auto distance = vec3::Distance(vertex->Position, position);
if (distance <= range && distance < nearestDistance)
{
nearestDistance = distance;
nearest = face;
break;
}
}
}
return nearest;
}
Vertex* GetVertexAt(const VertexList& vertices, const vec3& position, float range)
{
PERFORMANCER;
Vertex* nearest = nullptr;
float nearestDistance = Math::Infinity;
for (const auto& vertex : vertices)
{
auto distance = vec3::Distance(vertex->Position, position);
if (distance <= range && distance < nearestDistance)
{
nearestDistance = distance;
nearest = vertex;
}
}
return nearest;
}
}

View File

@@ -0,0 +1,139 @@
#include "../../include/tools/Split.h"
#include "../../include/tools/Select.h"
namespace mt
{
Face* FindPartnerFace(Face* self, const VertexList& vertices, const FaceList& faces)
{
const EdgeList& edges = self->GetEdges();
for (auto edge : edges)
{
int cnt = 0;
for (auto vertex : edge->GetVertices())
{
if (vertices.Contains(vertex))
cnt++;
}
if (cnt == 1)
{
for (auto face : edge->GetFaces())
if (face != self && faces.Contains(face))
return face;
}
}
return nullptr;
}
FaceList Split(const FaceList& faces, const Plane& plane)
{
if (faces.Count() == 0)
return{};
auto mesh = faces[0]->GetMesh();
Map<Edge*, Vertex*> edgesToIntersectionPoints;
VertexList splitVertices;
auto edges = Linq::SelectUniqueMany<Edge*>(faces, [](Face* face){ return face->GetEdges(); });
for (auto edge : edges)
{
const auto& vertices = edge->GetVertices();
vec3 intersectionPoint;
auto d = plane.IntersectLinesegment(vertices[0]->Position, vertices[1]->Position, intersectionPoint);
if (d >= 0 && d <= 1)
{
auto splitVertex = new Vertex(vertices[0]);
splitVertex->Interpolate(vertices[1], d);
edgesToIntersectionPoints.Add(edge, splitVertex);
splitVertices.Add(splitVertex);
}
}
FaceList result = faces;
mesh->StartAutoUpdate(result);
for (auto pair : edgesToIntersectionPoints)
{
auto edge = pair->Key;
auto vertex = pair->Value;
mesh->SplitEdge(edge, vertex);
}
FaceList sides[2];
sides[0] = mt::GetFacesOnPositivePlane(result, plane);
sides[1] = Linq::Except(result, sides[0]);
for (int i = 0; i < 2; ++i)
{
while (sides[i].Count())
{
auto face = sides[i][sides[i].Count() - 1];
sides[i].RemoveAt(sides[i].Count() - 1);
auto partnerFace = FindPartnerFace(face, splitVertices, sides[i]);
if (partnerFace != nullptr)
{
sides[i].Remove(partnerFace);
mt::MergeTriangles(face, partnerFace);
}
}
}
mesh->StopAutoUpdate(result);
return result;
}
Face* MergeTriangles(Face* a, Face* b)
{
auto mesh = a->GetMesh();
auto otherEdges = b->GetEdges();
Edge* sharedEdge;
for (const auto& edge : a->GetEdges())
if (otherEdges.IndexOf(edge) != -1)
{
sharedEdge = edge;
break;
}
if (sharedEdge == nullptr)
return nullptr;
List<Vertex*> vertices;
auto start0 = a->GetVertices().IndexOf(sharedEdge->GetVertices()[0]);
auto direction = 1;
auto sharedEdgeVertices = sharedEdge->GetVertices();
if (sharedEdgeVertices.IndexOf(a->GetVertices()[(start0 + 1) % 3]) >= 0)
direction = -1;
for (int i = 0; i < 3; ++i)
{
auto realIndex = start0 % 3;
if (realIndex < 0)
realIndex = realIndex + 3;
vertices.Add(a->GetVertices()[realIndex]);
start0 += direction;
}
Vertex* lastVertex;
for (const auto& vertex : b->GetVertices())
if (sharedEdgeVertices.IndexOf(vertex) == -1)
{
lastVertex = vertex;
break;
}
vertices = Linq::Reverse(vertices);
vertices.Add(lastVertex);
auto c = new Face(vertices);
c->CopyProperties(a);
c->CopyProperties(b, true);
c->FlipNormal(a->GetNormal());
mesh->AddFace(c);
mesh->RemoveFace(a);
mesh->RemoveFace(b);
auto faces = Linq::ToList(c);
mesh->TriggerFaceReplaced(a, faces);
mesh->TriggerFaceReplaced(b, faces);
return c;
}
}

View File

@@ -0,0 +1,161 @@
#include "../../include/tools/Subdivide.h"
#include "../../include/tools/Calculate.h"
#include "../../include/tools/Select.h"
namespace mt
{
FaceList Subdivide(const FaceList& inFaces, bool smooth)
{
PERFORMANCER;
auto faces = inFaces;
if (faces.Count() == 0)
return{};
auto mesh = faces[0]->GetMesh();
auto vertices = mt::GetVertices(faces);
auto edges = Linq::SelectUniqueMany<Edge*>(faces, [](Face* face) { return face->GetEdges(); });
// A FacePoint is defined as the average of the points from a face, in our case this is the face center.
Map<Face*, Vertex*> facePoints;
for (const auto& face : faces)
facePoints.Add(face, new Vertex(mt::GetCenter(face->GetVertices())));
Map<Edge*, Vertex*> borderEdges;
Map<Edge*, Vertex*> edgePoints;
for (const auto& edge : edges)
{
auto edgeFaces = edge->GetFaces();
edgeFaces = Linq::Intersect(edgeFaces, faces);
vec3 point = edge->GetCenter();
auto isBorderEdge = 1 == edgeFaces.Count();
if (smooth)
{
// An EdgePoint is the average of the edgepoints and the facepoints of the edges faces, if the edge is a border edge the edgepoint is only the average of the edgepoints (the center).
auto cnt = 1;
if (!isBorderEdge)
{
for (const auto& face : edgeFaces)
{
point += mt::GetCenter(face->GetVertices());
cnt++;
}
}
point /= (float)cnt;
}
auto edgeVertex = new Vertex(point);
edgePoints.Add(edge, edgeVertex);
if (isBorderEdge && edge->GetFaces().Count() > 1)
borderEdges.Add(edge, edgeVertex);
}
if (smooth)
{
for (const auto& vertex : vertices)
{
auto vertexFaces = Linq::Intersect(vertex->GetFaces(), faces);
auto touchingFaces = Linq::Where(vertexFaces, [&](Face* face) { return faces.IndexOf(face) != -1; });
auto touchingFaceEdges = Linq::SelectUniqueMany<Edge*>(touchingFaces, [&](Face* face) { return face->GetEdges(); });
auto touchingEdges = Linq::Where(touchingFaceEdges, [&](Edge* edge) { return edge->GetVertices().IndexOf(vertex) != -1; });
touchingEdges = Linq::Distinct(touchingEdges);
auto isBorderVertex = touchingFaces.Count() != touchingEdges.Count();
if (isBorderVertex)
{
vec3 R;
float cnt = 1.0f;
for (const auto& touchingEdge : touchingEdges)
{
auto edgeFaces = Linq::Intersect(touchingEdge->GetFaces(), faces);
if (edgeFaces.Count() == 1)
{
R += edgePoints[touchingEdge]->Position;
cnt++;
}
}
vertex->Position = (R + vertex->Position) / cnt;
}
else
{
vec3 F;
for (const auto& touchingFace : touchingFaces)
F += facePoints[touchingFace]->Position;
F /= (float)touchingFaces.Count();
vec3 R;
for (const auto& touchingEdge : touchingEdges)
R += edgePoints[touchingEdge]->Position;
R /= (float)touchingEdges.Count();
auto n = (float)vertexFaces.Count();
auto m1 = (n - 3) / n;
auto m2 = 1 / n;
auto m3 = 2 / n;
auto newPosition = (vertex->Position * m1)
+ (F * m2)
+ (R * m3);
//newPosition = (F + R * 2 + vertex->Position * (n - 3)) / n;
vertex->Position = newPosition;
}
}
}
Map<Face*, List<Edge*>> faceEdges;
for (const auto& face : faces)
faceEdges.Add(face, face->GetEdges());
FaceList newFaces;
for (const auto& face : faces)
{
FaceList currentNewFaces;
auto faceVertices = face->GetVertices();
auto facePoint = facePoints[face];
auto edgePoint0 = edgePoints[faceEdges[face][0]];
auto edgePoint1 = edgePoints[faceEdges[face][1]];
auto edgePoint2 = edgePoints[faceEdges[face][2]];
if (face->IsTriangle())
{
currentNewFaces.Add(new Face(faceVertices[0], edgePoint0, facePoint, edgePoint2));
currentNewFaces.Add(new Face(faceVertices[1], edgePoint1, facePoint, edgePoint0));
currentNewFaces.Add(new Face(faceVertices[2], edgePoint2, facePoint, edgePoint1));
}
else
{
auto edgePoint3 = edgePoints[faceEdges[face][3]];
currentNewFaces.Add(new Face(faceVertices[0], edgePoint0, facePoint, edgePoint3));
currentNewFaces.Add(new Face(faceVertices[1], edgePoint1, facePoint, edgePoint0));
currentNewFaces.Add(new Face(faceVertices[2], edgePoint2, facePoint, edgePoint1));
currentNewFaces.Add(new Face(faceVertices[3], edgePoint3, facePoint, edgePoint2));
}
for (auto& newFace : currentNewFaces)
{
newFace->CopyProperties(face);
newFace->FlipNormal(face->GetNormal());
mesh->AddFace(newFace);
newFaces.Add(newFace);
}
mesh->TriggerFaceReplaced(face, currentNewFaces);
}
for (const auto& face : faces)
mesh->RemoveFace(face);
for (const auto& pair : borderEdges)
Linq::AddRange(newFaces, mesh->SplitEdge(pair->Key, pair->Value));
return newFaces;
}
}

View File

@@ -0,0 +1,18 @@
#include "../../include/tools/Tags.h"
namespace mt
{
void AddTag(const FaceList& faces, int tag)
{
PERFORMANCER;
for (const auto& face : faces)
face->AddTag(tag);
}
void RemoveTag(const FaceList& faces, int tag)
{
PERFORMANCER;
for (const auto& face : faces)
face->RemoveTag(tag);
}
}

View File

@@ -0,0 +1,175 @@
#include "../../include/tools/Transform.h"
#include "../../include/tools/Calculate.h"
#include "../../include/tools/Select.h"
#include "../../include/tools/Group.h"
namespace mt
{
void MoveTo(const VertexList& vertices, const vec3& position)
{
PERFORMANCER;
auto center = mt::GetCenter(vertices);
for (const auto& vertex : vertices)
vertex->Position += position - center;
}
void MoveBy(const VertexList& vertices, const vec3& offset)
{
PERFORMANCER;
auto center = mt::GetCenter(vertices);
for (const auto& vertex : vertices)
vertex->Position += offset;
}
void MoveBy(const VertexList& vertices, float factor, MoveMode mode)
{
PERFORMANCER;
switch (mode)
{
case MoveMode::UseVertexNormal:
{
Map<Vertex*, vec3> normalMap;
for (const auto& vertex : vertices)
normalMap.Add(vertex, vertex->GetNormal());
for (auto& pair : normalMap)
pair->Key->Position += pair->Value * factor;
break;
}
case MoveMode::UseCollectionNormal:
{
auto normal = mt::GetNormal(vertices);
for (const auto& vertex : vertices)
vertex->Position += normal * factor;
break;
}
}
}
void Scale(const VertexList& vertices, const vec3& center, float factor)
{
PERFORMANCER;
for (const auto& vertex : vertices)
vertex->Position = center + (vertex->Position - center) * factor;
}
void Scale(const VertexList& vertices, const vec3& factor, const vec3& center)
{
PERFORMANCER;
for (const auto& vertex : vertices)
vertex->Position = center + vec3::Mul(vertex->Position - center, factor);
}
void Scale(const VertexList& vertices, float factor, CenterMode mode)
{
PERFORMANCER;
Scale(vertices, { factor, factor, factor }, mode);
}
void Scale(const VertexList& vertices, const vec3& factor, CenterMode mode)
{
PERFORMANCER;
if (vertices.Count() == 0)
return;
switch (mode)
{
case CenterMode::CollectionCenter:
{
mt::Scale(vertices, factor, mt::GetCenter(vertices));
break;
}
case CenterMode::MeshCenter:
{
auto center = mt::GetCenter(vertices[0]->GetMesh()->GetVertices());
mt::Scale(vertices, factor, center);
break;
}
case CenterMode::FaceCenter:
{
Map<Vertex*, vec3> normalMap;
for (const auto& vertex : vertices)
{
vec3 center;
auto faces = vertex->GetFaces();
for (const auto& face : faces)
center += mt::GetCenter(face->GetVertices());
center /= (float)faces.Count();
normalMap.Add(vertex, center);
}
for (const auto& pair : normalMap)
pair->Key->Position = pair->Value + vec3::Mul(pair->Key->Position - pair->Value, factor);
break;
}
}
}
void Rotate(const VertexList& vertices, const vec3& startNormal, const vec3& direction)
{
PERFORMANCER;
auto center = mt::GetCenter(vertices);
Rotate(vertices, center, startNormal, direction);
}
void Rotate(const VertexList& vertices, const vec3& center, const vec3& startNormal, const vec3& direction)
{
PERFORMANCER;
auto rotation = Quaternion::FromTo(startNormal, direction);
Rotate(vertices, center, rotation);
}
void Rotate(const VertexList& vertices, const vec3& axis, float angle)
{
PERFORMANCER;
auto center = mt::GetCenter(vertices);
Rotate(vertices, center, axis, angle);
}
void Rotate(const VertexList& vertices, const vec3& center, const vec3& axis, float angle)
{
PERFORMANCER;
auto rotation = Quaternion::CreateFromAxisAngle(axis, angle);
Rotate(vertices, center, rotation);
}
void Rotate(const VertexList& vertices, const vec3& center, const Quaternion& rotation)
{
PERFORMANCER;
for (const auto& vertex : vertices)
vertex->Position = ((vertex->Position - center) * rotation) + center;
}
void Mirror(const FaceList& faces, const Plane& plane)
{
PERFORMANCER;
for (const auto& vertex : mt::GetVertices(faces))
{
auto distance = plane.GetDistanceToPoint(vertex->Position);
vertex->Position -= plane.GetNormal() * distance * 2;
if (vertex->HasManualNormal())
{
auto normal = vertex->GetNormal();
auto planeNormalMagnitude = plane.GetNormal().GetLength();
vertex->SetNormal(normal - (plane.GetNormal() * 2) * vec3::Dot(normal, plane.GetNormal()) / (planeNormalMagnitude * planeNormalMagnitude));
}
}
for (const auto& face : faces)
face->FlipNormal();
}
}

View File

@@ -0,0 +1,9 @@
#include "../../include/tools/Triangulate.h"
namespace mt
{
FaceList Triangulate(const FaceList& faces)
{
return Linq::SelectMany<Face*>((FaceList)faces, [](Face* face) { return face->Triangulate(); });
}
}

View File

@@ -0,0 +1,88 @@
#include "../../include/tools/Weld.h"
#include "../../include/tools/Calculate.h"
#include "../../include/tools/Select.h"
#include "../../include/tools/Transform.h"
namespace mt
{
void Weld(const VertexList& vertices, float range)
{
PERFORMANCER;
if (range <= Math::FloatEpsilon)
range = Math::FloatEpsilon;
else
range *= range;
auto pendingVertices = vertices;
VertexList toWeld(1);
while (pendingVertices.Count() > 0)
{
toWeld.Clear();
auto vertex = pendingVertices[pendingVertices.Count() - 1];
for (int i = pendingVertices.Count() - 1; i >= 0; --i)
{
auto weldCandidate = pendingVertices[i];
if (vec3::DistanceSq(weldCandidate->Position, vertex->Position) <= range)
{
toWeld.Add(weldCandidate);
pendingVertices.RemoveAt(i);
}
}
if (toWeld.Count() == 1)
continue;
auto newVertex = new Vertex(toWeld[0]);
newVertex->Position = mt::GetCenter(toWeld);
toWeld[0]->GetMesh()->ReplaceVertices(toWeld, newVertex);
for (const auto& face : newVertex->GetFaces())
if (!face->RegainIntegrity())
face->GetMesh()->RemoveFace(face);
}
}
void Collapse(const FaceList& faces, CenterMode mode)
{
PERFORMANCER;
if (faces.Count() == 0)
return;
auto vertices = mt::GetVertices(faces);
if (mode == CenterMode::CollectionCenter)
{
auto center = mt::GetCenter(vertices);
for (const auto& vertex : vertices)
vertex->Position = center;
}
else if (mode == CenterMode::MeshCenter)
{
auto center = mt::GetCenter(faces[0]->GetMesh()->GetVertices());
for (const auto& vertex : vertices)
vertex->Position = center;
}
else if (mode == CenterMode::FaceCenter)
{
Map<Vertex*, vec3> normalMap;
for (const auto& vertex : vertices)
{
vec3 center;
auto faces = vertex->GetFaces();
for (const auto& face : faces)
center += mt::GetCenter(face->GetVertices());
center /= (float)faces.Count();
normalMap.Add(vertex, center);
}
for (const auto& pair : normalMap)
pair->Key->Position = pair->Value;
}
mt::Weld(vertices);
}
}

View File

@@ -0,0 +1,115 @@
#if _DEBUG
#include "ObjExporter.h"
#include <iostream>
#include <fstream>
void ObjExporter::Export(Mesh* mesh, const std::string& filePath)
{
PERFORMANCER;
ObjExporter exporter(mesh);
exporter.Export(filePath);
}
ObjExporter::ObjExporter(Mesh* mesh)
: mesh{mesh}
{
PERFORMANCER;
}
void ObjExporter::Export(const std::string& filePath)
{
PERFORMANCER;
std::ostringstream builder;
std::ostringstream mtlBuilder;
builder << "mtllib " + replace(filePath, ".obj", ".mtl") << std::endl;
WritePositions(builder);
WriteNormals(builder);
WriteUVs(builder);
WriteFaceGroups(builder, mtlBuilder);
std::ofstream objFile(filePath);
objFile << builder.str();
objFile.close();
std::ofstream mtlFile(replace(filePath, ".obj", ".mtl"));
mtlFile << mtlBuilder.str();
mtlFile.close();
}
void ObjExporter::WriteFaceGroups(std::ostringstream& builder, std::ostringstream& mtlBuilder)
{
PERFORMANCER;
auto groups = mesh->GetSubMeshs();
for(const auto& group : groups)
{
auto materialId = group->Key;
builder << "usemtl " << materialId << std::endl;
builder << "g " << materialId << std::endl;
WriteFaces(builder, group->Value);
mtlBuilder << "newmtl " << materialId << std::endl;
mtlBuilder << "Ka 0.0 0.0 0.0" << std::endl;
mtlBuilder << "Kd 1.0 1.0 1.0" << std::endl;
mtlBuilder << "Ks 1.0 1.0 1.0" << std::endl;
mtlBuilder << "Ns 16.0" << std::endl;
mtlBuilder << "illum 0" << std::endl;
mtlBuilder << "map_Kd " << materialId << ".png" << std::endl;
}
}
void ObjExporter::WriteFaces(std::ostringstream& builder, const FaceList& faces)
{
PERFORMANCER;
for (auto face : faces)
{
auto indices = face->GetIndices(true);
builder << "f";
for (int i = 0; i < indices.Count(); ++i)
{
builder << " ";
auto index = indices[i] + 1;
builder << index << "/" << index << "/" << index;
}
builder << std::endl;
}
}
void ObjExporter::WritePositions(std::ostringstream& builder)
{
PERFORMANCER;
for (const auto& v : mesh->GetVertices())
{
builder << "v " << v->Position.x << " " << v->Position.y << " " << v->Position.z << std::endl;
}
}
void ObjExporter::WriteNormals(std::ostringstream& builder)
{
PERFORMANCER;
for (const auto& v : mesh->GetVertices())
{
auto normal = v->GetNormal();
builder << "vn " << normal.x << " " << normal.y << " " << normal.z << std::endl;
}
}
void ObjExporter::WriteUVs(std::ostringstream& builder)
{
PERFORMANCER;
for (const auto& v : mesh->GetVertices())
{
builder << "vt " << v->UV[0] << " " << v->UV[1] << std::endl;
}
}
#endif

View File

@@ -0,0 +1,44 @@
#pragma once
#if _DEBUG
#include <core/core.h>
#include <sstream>
#include <string>
class Mesh;
class ObjExporter
{
public:
static void Export(Mesh* mesh, const std::string& filePath);
ObjExporter(Mesh* mesh);
void Export(const std::string& filePath);
void WriteFaceGroups(std::ostringstream& builder, std::ostringstream& mtlBuilder);
void WriteFaces(std::ostringstream& builder, const FaceList& faces);
void WritePositions(std::ostringstream& builder);
void WriteNormals(std::ostringstream& builder);
void WriteUVs(std::ostringstream& builder);
private:
std::string replace(const std::string &input, const std::string &search, const std::string &replace)
{
auto s = input;
for (size_t pos = 0;; pos += replace.length())
{
// Locate the substring to replace
pos = s.find(search, pos);
if (pos == std::string::npos) break;
// Replace by erasing and inserting
s.erase(pos, search.length());
s.insert(pos, replace);
}
return s;
}
private:
Mesh* mesh;
};
#endif

247
meshTools/test/src/main.cpp Normal file
View File

@@ -0,0 +1,247 @@
#include <TCHAR.h>
#include <tools/tools.h>
#include "ObjExporter.h"
void PlaceCubesOnPoints(const List<vec3*> points, Mesh* target, float size = 0.025f)
{
auto sphere = mt::CreateSphere(1, vec3::zero, size);
for (auto point : points)
{
auto faces = mt::CopyToMesh(sphere->GetFaces(), target);
mt::MoveTo(mt::GetVertices(faces), *point);
}
delete sphere;
}
void MakePointer(vec3 pos, vec3 dir, Mesh* mesh)
{
auto pointer = mt::CreateCube(true, vec3::zero, 0.1f);
mt::Collapse(mt::GetFacesWithTag(pointer->GetFaces(), mt::Tags::DirectionUp));
mt::Scale(pointer->GetVertices(), vec3{0.5f, 2, 0.5f});
mt::MoveBy(pointer->GetVertices(), { 0, 0.1f, 0 });
mt::Rotate(pointer->GetVertices(), vec3::zero, vec3::up, dir);
mt::MoveTo(pointer->GetVertices(), pos);
mt::CopyToMesh(pointer->GetFaces(), mesh);
delete pointer;
}
void DeleteOffsideFaces(const FaceList& faces, bool negate = false)
{
mt::Delete(mt::GetFacesByDirection(faces, negate ? vec3::right : vec3::left));
}
void MarkFaces(const FaceList& faces)
{
int i = 0;
for (auto face : faces)
face->SetMaterialId(i++);
}
Mesh* Skull()
{
const int EYE = 0;
const int BROW = 1;
const int NOSE = 2;
PERFORMANCER;
auto mesh = mt::CreateSphere(2);
const auto& meshFaces = mesh->GetFaces();
mt::Scale(mesh->GetVertices(), vec3{ 1, 0.8f, 0.8f });
mt::Delete(mt::GetFacesOnPositivePlane(meshFaces, { vec3::left, vec3::zero }));
{
mt::FFD ffd(meshFaces, 1, 3, 3);
auto points = ffd.SelectPoints(0, 1, 0, 0, 3, 3);
for (auto point : points)
*point += vec3::forward * 0.1f;
points = ffd.SelectPoints(0, 0, 0, 0, 3, 3);
for (auto point : points)
*point += vec3::up * 0.15f;
points = ffd.SelectPoints(0, 1, 1, 1, 3, 3);
for (auto point : points)
*point += vec3::forward * 0.25f + vec3::up * 0.1f;
points = ffd.SelectPoints(0, 1, 2, 2, 3, 3);
for (auto point : points)
*point += vec3::forward * -0.1f;
points = ffd.SelectPoints(0, 1, 0, 0, 2, 2);
for (auto point : points)
*point += vec3::up * 0.15f + vec3::forward * 0.1f;
points = ffd.SelectPoints(0, 1, 0, 3, 3, 3);
for (auto point : points)
*point += vec3::up * 0.2f;
points = ffd.SelectPoints(1, 1, 0, 3, 3, 3);
for (auto point : points)
*point += vec3::left * 0.1f;
points = ffd.SelectPoints(1, 1, 0, 3, 0, 3);
for (auto point : points)
*point += vec3::left * 0.05f;
ffd.Apply();
}
// Chin
FaceList selection;
selection.Add(meshFaces[8]);
selection.Add(meshFaces[9]);
selection.Add(meshFaces[20]);
selection.Add(meshFaces[23]);
auto vertices = mt::GetVertices(selection);
vertices[0]->Position.x += 0.03f;
mt::Flatten(vertices, vec3::down);
mt::MoveBy(vertices, vec3{ 0, -0.08f, 0 });
mt::Weld(mesh->GetVertices(), 0.05f);
mt::Weld(mesh->GetVertices(), 0.05f);
mt::Rotate(mesh->GetVertices(), vec3::right, 0.2f);
DeleteOffsideFaces(mt::Extrude(selection, vec3{ 0, -0.05f, 0 }));
// Eye socket
selection.Clear();
selection.Add(meshFaces[42]);
selection.Add(meshFaces[18]);
mt::Split(selection, { vec3::up, vec3::zero });
selection = Linq::ToList(meshFaces[54]);
vertices = meshFaces[54]->GetVertices();
vertices[0]->Position.x -= 0.09f;
vertices[1]->Position.x -= 0.07f;
vertices[2]->Position.y += 0.025f;
vertices[3]->Position += vec3{ -0.02f, 0.05f, 0 };
auto brows = mt::Bevel(selection, 0, 0.9f);
mt::AddTag(selection, EYE);
mt::Scale(selection, 0.8f);
// Nose
selection = Linq::ToList(meshFaces[53]);
mt::Split(selection, { vec3::up, vec3{0, 0.08f, 0} });
vertices = meshFaces[16]->GetVertices();
vertices[0]->Position.z -= 0.03f;
selection = Linq::ToList(meshFaces[55]);
mt::Split(selection, { vec3::up, vec3{ 0, -0.08f, 0 } });
selection = Linq::ToList(meshFaces[53]);
mt::Split(selection, { vec3::up, vec3{ 0, -0.08f, 0 } });
selection.Clear();
selection.Add(meshFaces[61]);
selection.Add(meshFaces[65]);
mt::AddTag(selection, NOSE);
DeleteOffsideFaces(mt::Extrude(selection, vec3{ 0, 0.05f, -0.1f }), true);
mt::Scale(selection, vec3::one * .5f, vec3::zero);
{
mt::FFD ffd(meshFaces, 1, 2, 2);
// shrink upper head area
auto points = ffd.SelectPoints(0, 1, 2, 2, 0, 2);
for (auto point : points)
{
*point = vec3::Mul(*point, vec3(0.7f, 0.8f, 0.7f));
point->z -= 0.1f;
}
points = ffd.SelectPoints(0, 0, 2, 2, 0, 2);
for (auto point : points)
point->y += 0.03f;
// thinning the upper jaw
points = ffd.SelectPoints(0, 1, 0, 0, 2, 2);
for (auto point : points)
{
point->x *= 0.2f;
point->z *= 0.9f;
}
points = ffd.SelectPoints(1, 1, 0, 2, 0, 0);
for (auto point : points)
point->x *= 0.6f;
points = ffd.SelectPoints(1, 1, 0, 1, 0, 0);
for (auto point : points)
point->x *= 0.6f;
points = ffd.SelectPoints(0, 0, 0, 2, 2, 2);
for (auto point : points)
point->z -= 0.05f;
ffd.Apply();
}
selection.Clear();
selection.Add(meshFaces[26]);
selection.Add(meshFaces[9]);
selection.Add(meshFaces[31]);
vertices = mt::GetVertices(selection);
vertices = Linq::Where(vertices, [](Vertex* vertex) { return vertex->Position.z > 0; });
for (const auto& vertex : vertices)
vertex->Position.x *= 0.3f;
selection = mt::GetFacesWithTag(meshFaces, EYE);
mt::Bevel(selection, { 0, 0, -0.1f }, 0.7f);
selection.Clear();
selection.Add(brows[3]);
mt::Scale(selection, { 1, 2, 1 });
mt::Bevel(selection, 0.02f, 0.5f);
selection = Linq::ToList(meshFaces[58]);
vertices = mt::GetVertices(selection);
vertices[0]->Position += vec3{ 0, -0.01f, 0.08f };
vertices[1]->Position += vec3{ 0, -0.01f, 0.025f };
MarkFaces(meshFaces);
// Finalize
auto clone = mt::Copy(meshFaces);
mt::Mirror(clone, { vec3::left, vec3::zero });
auto edges = mt::GetOutlineEdges(meshFaces);
mt::Weld(edges);
return mesh;
}
#include <stdarg.h>
#ifdef _DEBUG
int _tmain()
#else
int mainCRTStartup()
#endif
{
auto mesh = Skull();
/*MakePointer(vec3::up, vec3::up, mesh);
MakePointer(vec3::right, vec3::right, mesh);
MakePointer(vec3::forward, vec3::forward, mesh);*/
#ifdef _DEBUG
ObjExporter exporter(mesh);
exporter.Export("../Bin/export.obj");
#endif
PERFORMANCER_DUMP
return 0;
}

102
meshTools/test/test.vcxproj Normal file
View File

@@ -0,0 +1,102 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{6E7DF6F1-481D-41A2-84CD-27B2626BC21D}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>test</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)Bin\$(Configuration)\</OutDir>
<IncludePath>$(SolutionDir)common/include;$(SolutionDir)math/include;$(SolutionDir)meshCore/include;$(SolutionDir)meshTools/include;$(IncludePath)</IncludePath>
<LibraryPath>$(SolutionDir)Bin\$(Configuration)\lib\;C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(LibraryPath)</LibraryPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)Bin\$(Configuration)\</OutDir>
<IncludePath>$(SolutionDir)meshTools/include;$(IncludePath)</IncludePath>
<LibraryPath>$(SolutionDir);$(SolutionDir)Bin\$(Configuration)\lib\;C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(LibraryPath)</LibraryPath>
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>meshTools.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MinSpace</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<WholeProgramOptimization>false</WholeProgramOptimization>
<BufferSecurityCheck>false</BufferSecurityCheck>
<FloatingPointModel>Fast</FloatingPointModel>
<AdditionalOptions>/QIfist %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>msvcrt_old.LIB;meshTools.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalOptions>/CRINKLER %(AdditionalOptions)</AdditionalOptions>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="src\ObjExporter.cpp" />
<ClCompile Include="src\main.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\ObjExporter.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\ObjExporter.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\ObjExporter.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ShowAllFiles>true</ShowAllFiles>
</PropertyGroup>
</Project>