Initial Commit. Copied everything from Voxelman.

This commit is contained in:
Relintai 2020-01-31 16:12:16 +01:00
commit 63d55b1549
12 changed files with 1594 additions and 0 deletions

8
.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
.import
*.d
*.o
*.meta
*.obj
*.pyc
*.bc
*.os

20
LICENSE Normal file
View File

@ -0,0 +1,20 @@
Copyright (c) 2020 Péter Magyar
Copyright (c) 2017-2020 Mattias Edlund
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

3
README.md Normal file
View File

@ -0,0 +1,3 @@
# Fast Quadratic Mesh Simplifier
A work-in-progress port of https://github.com/Whinarn/UnityMeshSimplifier to a GODOT engine c++ module.

32
SCsub Normal file
View File

@ -0,0 +1,32 @@
import os
Import('env')
module_env = env.Clone()
#Should work with just arrays
#if os.path.isdir('../mesh_data_resource'):
# module_env.Append(CPPDEFINES=['MESH_DATA_RESOURCE_PRESENT'])
module_env.Append(CPPFLAGS=["-std=c++11"])
sources = [
"register_types.cpp",
"mesh_simplifier.cpp",
"fast_quadratic_mesh_simplifier.cpp",
]
if ARGUMENTS.get('custom_modules_shared', 'no') == 'yes':
# Shared lib compilation
module_env.Append(CCFLAGS=['-fPIC'])
module_env['LIBS'] = []
shared_lib = module_env.SharedLibrary(target='#bin/fqms', source=sources)
shared_lib_shim = shared_lib[0].name.rsplit('.', 1)[0]
env.Append(LIBS=[shared_lib_shim])
env.Append(LIBPATH=['#bin'])
else:
# Static compilation
module_env.add_source_files(env.modules_sources, sources)

77
config.py Normal file
View File

@ -0,0 +1,77 @@
# Copyright (c) 2020 Péter Magyar
# Copyright(c) 2017-2020 Mattias Edlund
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
def can_build(env, platform):
return True
def configure(env):
pass
def get_doc_classes():
return [
"WorldArea",
"GroundClutterFoliage",
"GroundClutter",
"VoxelmanQueue",
"VoxelmanUnboundedQueue",
"VoxelLight",
"VoxelmanLevelGenerator",
"VoxelSurfaceMerger",
"VoxelSurfaceSimple",
"VoxelSurface",
"VoxelmanLibraryMerger",
"VoxelmanLibrarySimple",
"VoxelmanLibrary",
"VoxelCubePoints",
"VoxelMesherCubic",
"TransvoxelCellData",
"VoxelMeshData",
"VoxelMesherTransvoxel",
"VoxelMesher",
"PropDataEntity",
"PropDataEntry",
"PropDataLight",
"PropDataMesh",
"PropDataProp",
"PropDataScene",
"PropData",
"EnvironmentData",
"VoxelChunkPropData",
"VoxelChunk",
"VoxelStructure",
"VoxelWorld",
]
def get_doc_path():
return "doc_classes"

View File

@ -0,0 +1,748 @@
#include "fast_quadratic_mesh_simplifier.h"
/*
Copyright (c) 2020 Péter Magyar
Copyright(c) 2017-2020 Mattias Edlund
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
void FastQuadraticMeshSimplifier::initialize(Array arrays) {
//_mesher = mesher;
/*
_vertices = mesher->get_vertices();
_normals = mesher->get_normals();
_colors = mesher->get_colors();
_uvs = mesher->get_uvs();
_uv2s = mesher->get_uv2s();
_indices = mesher->get_indices();
*/
if ((_indices.size() % 3) != 0)
ERR_FAIL_MSG("The index array length must be a multiple of 3 in order to represent triangles.");
int triangle_count = _indices.size() / 3;
_mu_triangles.resize(triangle_count);
for (int i = 0; i < triangle_count; ++i) {
int offset = i * 3;
int v0 = _indices[offset];
int v1 = _indices[offset + 1];
int v2 = _indices[offset + 2];
_mu_triangles[i] = MUTriangle(v0, v1, v2, 0);
}
_mu_vertices.resize(_vertices.size());
for (int i = 0; i < _vertices.size(); ++i) {
_mu_vertices[i] = MUVertex(_vertices[i]);
}
}
void FastQuadraticMeshSimplifier::refresh_vertices() {
_vertices.resize(_mu_vertices.size());
for (int i = 0; i < _mu_vertices.size(); ++i) {
MUVertex vert = _mu_vertices[i];
_vertices[i] = Vector3(vert.p);
}
}
void FastQuadraticMeshSimplifier::SimplifyMesh(float quality) {
quality = CLAMP(quality, 0, 1);
int deletedTris = 0;
PoolVector<bool> deleted0;
deleted0.resize(20);
PoolVector<bool> deleted1;
deleted1.resize(20);
int startTrisCount = _mu_triangles.size();
int targetTrisCount = static_cast<int>(_mu_triangles.size() * quality + 0.5);
for (int iteration = 0; iteration < maxIterationCount; iteration++) {
if ((startTrisCount - deletedTris) <= targetTrisCount)
break;
// Update mesh once in a while
if ((iteration % 5) == 0) {
UpdateMesh(iteration);
}
// Clear dirty flag
for (int i = 0; i < _mu_triangles.size(); ++i) {
_mu_triangles[i].set_dirty(false);
}
// All triangles with edges below the threshold will be removed
//
// The following numbers works well for most models.
// If it does not, try to adjust the 3 parameters
double threshold = 0.000000001 * Math::pow(iteration + 3, agressiveness);
//print_verbose("iteration {0} - triangles {1} threshold {2}", iteration, (startTrisCount - deletedTris), threshold);
// Remove vertices & mark deleted triangles
deletedTris = RemoveVertexPass(startTrisCount, targetTrisCount, threshold, deleted0, deleted1, deletedTris);
}
CompactMesh();
//print_verbose("Finished simplification with triangle count {0}", _mu_triangles.size());
}
//Mesh Simplification
//Ported from https://github.com/Whinarn/UnityFastQuadraticMeshSimplifier
//Original license: MIT License Copyright (c) 2017 Mattias Edlund
void FastQuadraticMeshSimplifier::SimplifyMeshLossless() {
int deletedTris = 0;
PoolVector<bool> deleted0;
PoolVector<bool> deleted1;
int startTrisCount = _mu_triangles.size();
for (int iteration = 0; iteration < 9999; iteration++) {
// Update mesh constantly
UpdateMesh(iteration);
// Clear dirty flag
for (int i = 0; i < _mu_triangles.size(); ++i) {
_mu_triangles[i].set_dirty(false);
}
// All triangles with edges below the threshold will be removed
//
// The following numbers works well for most models.
// If it does not, try to adjust the 3 parameters
double threshold = 1.0E-3;
//Debug.LogFormat("Lossless iteration {0} - triangles {1}", iteration, triangleCount);
// Remove vertices & mark deleted triangles
deletedTris = RemoveVertexPass(startTrisCount, 0, threshold, deleted0, deleted1, deletedTris);
if (deletedTris <= 0)
break;
deletedTris = 0;
}
CompactMesh();
//Debug.LogFormat("Finished simplification with triangle count {0}", this.triangles.Length);
}
void FastQuadraticMeshSimplifier::UpdateMesh(int iteration) {
if (iteration > 0) // compact triangles
{
int dst = 0;
for (int i = 0; i < _mu_triangles.size(); ++i) {
if (!_mu_triangles[i].deleted) {
if (dst != i) {
_mu_triangles[dst] = _mu_triangles[i];
}
dst++;
}
}
_mu_triangles.resize(dst);
}
UpdateReferences();
// Identify boundary : vertices[].border=0,1
if (iteration == 0) {
PoolVector<int> vcount;
vcount.resize(8);
PoolVector<int> vids;
vids.resize(8);
int vsize = 0;
for (int i = 0; i < _mu_vertices.size(); i++) {
_mu_vertices[i].set_border_edge(false);
_mu_vertices[i].set_uv_seam_edge(false);
_mu_vertices[i].set_uv_foldover_edge(false);
}
int ofs;
int id;
int borderVertexCount = 0;
double borderMinX = std::numeric_limits<double>::max();
double borderMaxX = std::numeric_limits<double>::min();
for (int i = 0; i < _mu_vertices.size(); i++) {
int tstart = _mu_vertices[i].tstart;
int tcount = _mu_vertices[i].tcount;
vcount.resize(0);
vids.resize(0);
vsize = 0;
for (int j = 0; j < tcount; j++) {
int tid = _mu_refs[tstart + j].tid;
for (int k = 0; k < 3; k++) {
ofs = 0;
id = _mu_triangles[tid].get(k);
while (ofs < vsize) {
if (vids[ofs] == id)
break;
++ofs;
}
if (ofs == vsize) {
vcount.push_back(1);
vids.push_back(id);
++vsize;
} else {
vcount.set(ofs, vcount[ofs] + 1);
}
}
}
for (int j = 0; j < vsize; j++) {
if (vcount[j] == 1) {
id = vids[j];
_mu_vertices[id].set_border_edge(true);
++borderVertexCount;
if (enableSmartLink) {
if (_mu_vertices[id].p.x < borderMinX) {
borderMinX = _mu_vertices[id].p.x;
}
if (_mu_vertices[id].p.x > borderMaxX) {
borderMaxX = _mu_vertices[id].p.x;
}
}
}
}
}
if (enableSmartLink) {
// First find all border vertices
Vector<BorderVertex> borderVertices;
borderVertices.resize(borderVertexCount);
int borderIndexCount = 0;
double borderAreaWidth = borderMaxX - borderMinX;
for (int i = 0; i < _mu_vertices.size(); i++) {
if (_mu_vertices[i].borderEdge) {
int vertexHash = (int)(((((_mu_vertices[i].p.x - borderMinX) / borderAreaWidth) * 2.0) - 1.0) * std::numeric_limits<int>::max());
borderVertices.set(borderIndexCount, BorderVertex(i, vertexHash));
++borderIndexCount;
}
}
// Sort the border vertices by hash
borderVertices.sort_custom<BorderVertexComparer>();
// Calculate the maximum hash distance based on the maximum vertex link distance
double vertexLinkDistance = Math::sqrt(vertexLinkDistanceSqr);
int hashMaxDistance = MAX((int)((vertexLinkDistance / borderAreaWidth) * std::numeric_limits<int>::max()), 1);
// Then find identical border vertices and bind them together as one
for (int i = 0; i < borderIndexCount; i++) {
int myIndex = borderVertices[i].index;
if (myIndex == -1)
continue;
Vector3 myPoint = _mu_vertices[myIndex].p;
for (int j = i + 1; j < borderIndexCount; j++) {
int otherIndex = borderVertices[j].index;
if (otherIndex == -1)
continue;
else if ((borderVertices[j].hash - borderVertices[i].hash) > hashMaxDistance) // There is no point to continue beyond this point
break;
Vector3 otherPoint = _mu_vertices[otherIndex].p;
double sqrX = ((myPoint.x - otherPoint.x) * (myPoint.x - otherPoint.x));
double sqrY = ((myPoint.y - otherPoint.y) * (myPoint.y - otherPoint.y));
double sqrZ = ((myPoint.z - otherPoint.z) * (myPoint.z - otherPoint.z));
double sqrMagnitude = sqrX + sqrY + sqrZ;
if (sqrMagnitude <= vertexLinkDistanceSqr) {
borderVertices.get(j).set_index(-1); // NOTE: This makes sure that the "other" vertex is not processed again
_mu_vertices[myIndex].set_border_edge(false);
_mu_vertices[otherIndex].set_border_edge(false);
if (AreUVsTheSame(0, myIndex, otherIndex)) {
_mu_vertices[myIndex].set_uv_foldover_edge(true);
_mu_vertices[otherIndex].set_uv_foldover_edge(true);
} else {
_mu_vertices[myIndex].set_uv_seam_edge(true);
_mu_vertices[otherIndex].set_uv_seam_edge(true);
}
int otherTriangleCount = _mu_vertices[otherIndex].tcount;
int otherTriangleStart = _mu_vertices[otherIndex].tstart;
for (int k = 0; k < otherTriangleCount; k++) {
MURef r = _mu_refs[otherTriangleStart + k];
_mu_triangles[r.tid].set(myIndex, r.tvertex);
}
}
}
}
// Update the references again
UpdateReferences();
}
// Init Quadrics by Plane & Edge Errors
//
// required at the beginning ( iteration == 0 )
// recomputing during the simplification is not required,
// but mostly improves the result for closed meshes
for (int i = 0; i < _mu_vertices.size(); ++i) {
_mu_vertices[i].q.reset();
}
int v0, v1, v2;
Vector3 n, p0, p1, p2, p10, p20, dummy;
SymmetricMatrix sm;
for (int i = 0; i < _mu_triangles.size(); ++i) {
v0 = _mu_triangles[i].v0;
v1 = _mu_triangles[i].v1;
v2 = _mu_triangles[i].v2;
p0 = _mu_vertices[v0].p;
p1 = _mu_vertices[v1].p;
p2 = _mu_vertices[v2].p;
p10 = p1 - p0;
p20 = p2 - p0;
n = p10.cross(p20);
n.normalize();
_mu_triangles[i].n = n;
sm.from_plane(n.x, n.y, n.z, -n.dot(p0));
_mu_vertices[v0].q += sm;
_mu_vertices[v1].q += sm;
_mu_vertices[v2].q += sm;
}
for (int i = 0; i < _mu_triangles.size(); ++i) {
// Calc Edge Error
MUTriangle triangle = _mu_triangles[i];
_mu_triangles[i].set_err0(CalculateError(_mu_vertices[triangle.v0], _mu_vertices[triangle.v1], &dummy));
_mu_triangles[i].set_err1(CalculateError(_mu_vertices[triangle.v1], _mu_vertices[triangle.v2], &dummy));
_mu_triangles[i].set_err2(CalculateError(_mu_vertices[triangle.v2], _mu_vertices[triangle.v0], &dummy));
_mu_triangles[i].set_err3(FastQuadraticMeshSimplifier::Min3(_mu_triangles[i].err0, _mu_triangles[i].err1, _mu_triangles[i].err2));
}
}
}
void FastQuadraticMeshSimplifier::UpdateReferences() {
// Init Reference ID list
for (int i = 0; i < _mu_vertices.size(); i++) {
_mu_vertices[i].set_tstart(0);
_mu_vertices[i].set_tcount(0);
}
for (int i = 0; i < _mu_triangles.size(); i++) {
_mu_vertices[_mu_triangles[i].v0].set_tcount(_mu_vertices[_mu_triangles[i].v0].tcount + 1);
_mu_vertices[_mu_triangles[i].v1].set_tcount(_mu_vertices[_mu_triangles[i].v1].tcount + 1);
_mu_vertices[_mu_triangles[i].v2].set_tcount(_mu_vertices[_mu_triangles[i].v2].tcount + 1);
}
int tstart = 0;
for (int i = 0; i < _mu_vertices.size(); i++) {
_mu_vertices[i].set_tstart(tstart);
tstart += _mu_vertices[i].tcount;
_mu_vertices[i].set_tcount(0);
}
// Write References
_mu_refs.resize(tstart);
for (int i = 0; i < _mu_triangles.size(); i++) {
int v0 = _mu_triangles[i].v0;
int v1 = _mu_triangles[i].v1;
int v2 = _mu_triangles[i].v2;
int start0 = _mu_vertices[v0].tstart;
int count0 = _mu_vertices[v0].tcount;
int start1 = _mu_vertices[v1].tstart;
int count1 = _mu_vertices[v1].tcount;
int start2 = _mu_vertices[v2].tstart;
int count2 = _mu_vertices[v2].tcount;
_mu_refs[start0 + count0].Set(i, 0);
_mu_refs[start1 + count1].Set(i, 1);
_mu_refs[start2 + count2].Set(i, 2);
_mu_vertices[v0].set_tcount(_mu_vertices[v0].tcount + 1);
_mu_vertices[v1].set_tcount(_mu_vertices[v1].tcount + 1);
_mu_vertices[v2].set_tcount(_mu_vertices[v2].tcount + 1);
}
}
/// <summary>
/// Finally compact mesh before exiting.
/// </summary>
void FastQuadraticMeshSimplifier::CompactMesh() {
int dst = 0;
for (int i = 0; i < _mu_vertices.size(); i++) {
_mu_vertices[i].set_tcount(0);
}
for (int i = 0; i < _mu_triangles.size(); i++) {
MUTriangle triangle = _mu_triangles[i];
if (!triangle.deleted) {
if (triangle.va0 != triangle.v0) {
int iDest = triangle.va0;
int iSrc = triangle.v0;
_mu_vertices[iDest].p = _mu_vertices[iSrc].p;
triangle.v0 = triangle.va0;
}
if (triangle.va1 != triangle.v1) {
int iDest = triangle.va1;
int iSrc = triangle.v1;
_mu_vertices[iDest].p = _mu_vertices[iSrc].p;
triangle.v1 = triangle.va1;
}
if (triangle.va2 != triangle.v2) {
int iDest = triangle.va2;
int iSrc = triangle.v2;
_mu_vertices[iDest].p = _mu_vertices[iSrc].p;
triangle.v2 = triangle.va2;
}
int newTriangleIndex = ++dst;
_mu_triangles[newTriangleIndex] = triangle;
_mu_vertices[triangle.v0].set_tcount(1);
_mu_vertices[triangle.v1].set_tcount(1);
_mu_vertices[triangle.v2].set_tcount(1);
}
}
_mu_triangles.resize(dst);
dst = 0;
for (int i = 0; i < _mu_vertices.size(); i++) {
MUVertex vert = _mu_vertices[i];
if (vert.tcount > 0) {
vert.tstart = dst;
_mu_vertices[i] = vert;
if (dst != i) {
_mu_vertices[dst].p = vert.p;
if (_normals.size() > 0) _normals[dst] = _normals[i];
if (_colors.size() > 0) _colors.set(dst, _colors[i]);
if (_uvs.size() > 0) _uvs.set(dst, _uvs[i]);
if (_uv2s.size() > 0) _uv2s.set(dst, _uv2s[i]);
if (_indices.size() > 0) _indices.set(dst, _indices[i]);
}
++dst;
}
}
for (int i = 0; i < _mu_triangles.size(); i++) {
MUTriangle triangle = _mu_triangles[i];
triangle.v0 = _mu_vertices[triangle.v0].tstart;
triangle.v1 = _mu_vertices[triangle.v1].tstart;
triangle.v2 = _mu_vertices[triangle.v2].tstart;
_mu_triangles[i] = triangle;
}
//vertexCount = dst;
_vertices.resize(dst);
if (_normals.size() > 0) _normals.resize(dst);
if (_colors.size() > 0) _colors.resize(dst);
if (_uvs.size() > 0) _uvs.resize(dst);
if (_uv2s.size() > 0) _uv2s.resize(dst);
if (_indices.size() > 0) _indices.resize(dst);
}
bool FastQuadraticMeshSimplifier::AreUVsTheSame(int channel, int indexA, int indexB) {
if (_uv2s.size() > 0) {
//Vector2 vertUV = _uv2s[channel];
Vector2 uvA = _uv2s[indexA];
Vector2 uvB = _uv2s[indexB];
return uvA == uvB;
}
return false;
}
/// Remove vertices and mark deleted triangles
int FastQuadraticMeshSimplifier::RemoveVertexPass(int startTrisCount, int targetTrisCount, double threshold, PoolVector<bool> &deleted0, PoolVector<bool> &deleted1, int deletedTris) {
Vector3 p;
Vector3 barycentricCoord;
for (int tid = 0; tid < _mu_triangles.size(); tid++) {
if (_mu_triangles[tid].dirty || _mu_triangles[tid].deleted || _mu_triangles[tid].err3 > threshold)
continue;
Vector3 errors = _mu_triangles[tid].GetErrors();
Vector3 attrib_indices = _mu_triangles[tid].GetAttributeIndices();
for (int edgeIndex = 0; edgeIndex < 3; edgeIndex++) {
if (errors[edgeIndex] > threshold)
continue;
int nextEdgeIndex = ((edgeIndex + 1) % 3);
int i0 = _mu_triangles[tid].get(edgeIndex);
int i1 = _mu_triangles[tid].get(nextEdgeIndex);
// Border check
if (_mu_vertices[i0].borderEdge != _mu_vertices[i1].borderEdge)
continue;
// Seam check
else if (_mu_vertices[i0].uvSeamEdge != _mu_vertices[i1].uvSeamEdge)
continue;
// Foldover check
else if (_mu_vertices[i0].uvFoldoverEdge != _mu_vertices[i1].uvFoldoverEdge)
continue;
// If borders should be preserved
else if (preserveBorderEdges && _mu_vertices[i0].borderEdge)
continue;
// If seams should be preserved
else if (preserveUVSeamEdges && _mu_vertices[i0].uvSeamEdge)
continue;
// If foldovers should be preserved
else if (preserveUVFoldoverEdges && _mu_vertices[i0].uvFoldoverEdge)
continue;
// Compute vertex to collapse to
CalculateError(_mu_vertices[i0], _mu_vertices[i1], &p);
deleted0.resize(_mu_vertices[i0].tcount); // normals temporarily
deleted1.resize(_mu_vertices[i1].tcount); // normals temporarily
// Don't remove if flipped
if (Flipped(p, i0, i1, _mu_vertices[i0], deleted0))
continue;
if (Flipped(p, i1, i0, _mu_vertices[i1], deleted1))
continue;
// Calculate the barycentric coordinates within the triangle
int nextNextEdgeIndex = ((edgeIndex + 2) % 3);
int i2 = _mu_triangles[tid].get(nextNextEdgeIndex);
barycentricCoord = CalculateBarycentricCoords(p, _mu_vertices[i0].p, _mu_vertices[i1].p, _mu_vertices[i2].p);
// Not flipped, so remove edge
_mu_vertices[i0].p = p;
_mu_vertices[i0].q += _mu_vertices[i1].q;
// Interpolate the vertex attributes
int ia0 = attrib_indices[edgeIndex];
int ia1 = attrib_indices[nextEdgeIndex];
int ia2 = attrib_indices[nextNextEdgeIndex];
InterpolateVertexAttributes(ia0, ia0, ia1, ia2, barycentricCoord);
if (_mu_vertices[i0].uvSeamEdge) {
ia0 = -1;
}
int tstart = _mu_refs.size();
deletedTris = UpdateTriangles(i0, ia0, _mu_vertices[i0], deleted0, deletedTris);
deletedTris = UpdateTriangles(i0, ia0, _mu_vertices[i1], deleted1, deletedTris);
int tcount = _mu_refs.size() - tstart;
if (tcount <= _mu_vertices[i0].tcount) {
// save ram
if (tcount > 0) {
int dests = _mu_vertices[i0].tstart;
for (int v = 0; v < tcount; ++v) {
_mu_refs[v + tstart] = _mu_refs[v + dests];
}
}
} else {
// append
_mu_vertices[i0].set_tstart(tstart);
}
_mu_vertices[i0].set_tcount(tcount);
break;
}
// Check if we are already done
if ((startTrisCount - deletedTris) <= targetTrisCount)
break;
}
return deletedTris;
}
double FastQuadraticMeshSimplifier::VertexError(SymmetricMatrix q, double x, double y, double z) {
return q.m0 * x * x + 2 * q.m1 * x * y + 2 * q.m2 * x * z + 2 * q.m3 * x + q.m4 * y * y + 2 * q.m5 * y * z + 2 * q.m6 * y + q.m7 * z * z + 2 * q.m8 * z + q.m9;
}
double FastQuadraticMeshSimplifier::CalculateError(MUVertex vert0, MUVertex vert1, Vector3 *result) {
// compute interpolated vertex
SymmetricMatrix q = (vert0.q + vert1.q);
bool borderEdge = (vert0.borderEdge & vert1.borderEdge);
double error = 0.0;
double det = q.Determinant1();
if (det != 0.0 && !borderEdge) {
// q_delta is invertible
result = new Vector3(
-1.0 / det * q.Determinant2(), // vx = A41/det(q_delta)
1.0 / det * q.Determinant3(), // vy = A42/det(q_delta)
-1.0 / det * q.Determinant4()); // vz = A43/det(q_delta)
error = VertexError(q, result->x, result->y, result->z);
} else {
// det = 0 -> try to find best result
Vector3 p1 = vert0.p;
Vector3 p2 = vert1.p;
Vector3 p3 = (p1 + p2) * 0.5f;
double error1 = VertexError(q, p1.x, p1.y, p1.z);
double error2 = VertexError(q, p2.x, p2.y, p2.z);
double error3 = VertexError(q, p3.x, p3.y, p3.z);
error = FastQuadraticMeshSimplifier::Min3(error1, error2, error3);
if (error == error3) {
result->x = p3.x;
result->y = p3.y;
result->z = p3.z;
} else if (error == error2) {
result->x = p2.x;
result->y = p2.y;
result->z = p2.z;
} else if (error == error1) {
result->x = p1.x;
result->y = p1.y;
result->z = p1.z;
} else {
result->x = p3.x;
result->y = p3.y;
result->z = p3.z;
}
}
return error;
}
int FastQuadraticMeshSimplifier::UpdateTriangles(int i0, int ia0, const MUVertex &v, PoolVector<bool> &deleted, int p_deletedTriangles) {
Vector3 p;
int deletedTriangles = p_deletedTriangles;
int tcount = v.tcount;
for (int k = 0; k < tcount; k++) {
MURef r = _mu_refs[v.tstart + k];
int tid = r.tid;
MUTriangle t = _mu_triangles[tid];
if (t.deleted)
continue;
if (deleted[k]) {
_mu_triangles[tid].set_deleted(true);
++deletedTriangles;
continue;
}
t.set(r.tvertex, i0);
if (ia0 != -1) {
t.SetAttributeIndex(r.tvertex, ia0);
}
t.dirty = true;
t.err0 = CalculateError(_mu_vertices[t.v0], _mu_vertices[t.v1], &p);
t.err1 = CalculateError(_mu_vertices[t.v1], _mu_vertices[t.v2], &p);
t.err2 = CalculateError(_mu_vertices[t.v2], _mu_vertices[t.v0], &p);
t.err3 = FastQuadraticMeshSimplifier::Min3(t.err0, t.err1, t.err2);
_mu_triangles[tid] = t;
_mu_refs.push_back(r);
}
return deletedTriangles;
}
bool FastQuadraticMeshSimplifier::Flipped(const Vector3 &p, int i0, int i1, const MUVertex &v0, PoolVector<bool> &deleted) {
int tcount = v0.tcount;
for (int k = 0; k < tcount; k++) {
MURef r = _mu_refs[v0.tstart + k];
if (_mu_triangles[r.tid].deleted)
continue;
int s = r.tvertex;
int id1 = _mu_triangles[r.tid].get((s + 1) % 3);
int id2 = _mu_triangles[r.tid].get((s + 2) % 3);
if (id1 == i1 || id2 == i1) {
deleted.set(k, true);
continue;
}
Vector3 d1 = _mu_vertices[id1].p - p;
d1.normalize();
Vector3 d2 = _mu_vertices[id2].p - p;
d2.normalize();
double dot = d1.dot(d2);
if (Math::abs(dot) > 0.999)
return true;
Vector3 n = d1.cross(d2);
n.normalize();
deleted.set(k, false);
dot = n.dot(_mu_triangles[r.tid].n);
if (dot < 0.2)
return true;
}
return false;
}
Vector3 FastQuadraticMeshSimplifier::CalculateBarycentricCoords(Vector3 const &point, Vector3 const &a, Vector3 const &b, Vector3 const &c) {
Vector3 v0 = (Vector3)(b - a), v1 = (Vector3)(c - a), v2 = (Vector3)(point - a);
float d00 = v0.dot(v0);
float d01 = v0.dot(v1);
float d11 = v1.dot(v1);
float d20 = v2.dot(v0);
float d21 = v2.dot(v1);
float denom = d00 * d11 - d01 * d01;
float v = (d11 * d20 - d01 * d21) / denom;
float w = (d00 * d21 - d01 * d20) / denom;
float u = 1.0 - v - w;
return Vector3(u, v, w);
}
void FastQuadraticMeshSimplifier::InterpolateVertexAttributes(int dst, int i0, int i1, int i2, Vector3 &barycentricCoord) {
if (_normals.size() > 0) {
_normals[dst] = (_normals[i0] * barycentricCoord.x) + (_normals[i1] * barycentricCoord.y) + (_normals[i2] * barycentricCoord.z).normalized();
}
if (_uvs.size() > 0) {
_uvs[dst] = (_uvs[i0] * barycentricCoord.x) + (_uvs[i1] * barycentricCoord.y) + (_uvs[i2] * barycentricCoord.z);
}
if (_uv2s.size() > 0) {
_uv2s[dst] = (_uv2s[i0] * barycentricCoord.x) + (_uv2s[i1] * barycentricCoord.y) + (_uv2s[i2] * barycentricCoord.z);
}
if (_colors.size() > 0) {
_colors[dst] = (_colors[i0] * barycentricCoord.x) + (_colors[i1] * barycentricCoord.y) + (_colors[i2] * barycentricCoord.z);
}
}
FastQuadraticMeshSimplifier::FastQuadraticMeshSimplifier() {
maxIterationCount = 100;
agressiveness = 7.0;
enableSmartLink = true;
preserveBorderEdges = false;
preserveUVSeamEdges = false;
preserveUVFoldoverEdges = false;
}

View File

@ -0,0 +1,90 @@
#ifndef FAST_QUADRATIC_MESH_SIMPLIFIER_H
#define FAST_QUADRATIC_MESH_SIMPLIFIER_H
/*
Copyright (c) 2020 Péter Magyar
Copyright(c) 2017-2020 Mattias Edlund
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "mesh_simplifier.h"
#include <limits>
#include "mesh_utils.h"
#include "core/array.h"
#include "core/pool_vector.h"
#include "core/resource.h"
class VoxelMesher;
class FastQuadraticMeshSimplifier : public MeshSimplifier {
GDCLASS(FastQuadraticMeshSimplifier, MeshSimplifier);
public:
void initialize(Array arrays);
void refresh_vertices();
void SimplifyMesh(float quality);
void SimplifyMeshLossless();
void UpdateMesh(int iteration);
void UpdateReferences();
int RemoveVertexPass(int startTrisCount, int targetTrisCount, double threshold, PoolVector<bool> &deleted0, PoolVector<bool> &deleted1, int deletedTris);
void CompactMesh();
bool AreUVsTheSame(int channel, int indexA, int indexB);
double VertexError(SymmetricMatrix q, double x, double y, double z);
double CalculateError(MUVertex vert0, MUVertex vert1, Vector3 *result);
int UpdateTriangles(int i0, int ia0, const MUVertex &v, PoolVector<bool> &deleted, int deletedTriangles);
bool Flipped(const Vector3 &p, int i0, int i1, const MUVertex &v0, PoolVector<bool> &deleted);
static Vector3 CalculateBarycentricCoords(Vector3 const &point, Vector3 const &a, Vector3 const &b, Vector3 const &c);
void InterpolateVertexAttributes(int dst, int i0, int i1, int i2, Vector3 &barycentricCoord);
static double Min3(double val1, double val2, double val3) {
return (val1 < val2 ? (val1 < val3 ? val1 : val3) : (val2 < val3 ? val2 : val3));
}
FastQuadraticMeshSimplifier();
private:
PoolVector<Vector3> _vertices;
PoolVector<Vector3> _normals;
PoolVector<Color> _colors;
PoolVector<Vector2> _uvs;
PoolVector<Vector2> _uv2s;
PoolVector<int> _indices;
PoolVector<MUTriangle> _mu_triangles;
PoolVector<MUVertex> _mu_vertices;
PoolVector<MURef> _mu_refs;
//Ref<VoxelMesher> _mesher;
double vertexLinkDistanceSqr = std::numeric_limits<double>::epsilon();
int maxIterationCount;
double agressiveness;
bool enableSmartLink;
bool preserveBorderEdges;
bool preserveUVSeamEdges;
bool preserveUVFoldoverEdges;
};
#endif

29
mesh_simplifier.cpp Normal file
View File

@ -0,0 +1,29 @@
#include "mesh_simplifier.h"
/*
Copyright (c) 2020 Péter Magyar
Copyright(c) 2017-2020 Mattias Edlund
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
MeshSimplifier::MeshSimplifier() {
}

44
mesh_simplifier.h Normal file
View File

@ -0,0 +1,44 @@
#ifndef MESH_SIMPLIFIER_H
#define MESH_SIMPLIFIER_H
/*
Copyright (c) 2020 Péter Magyar
Copyright(c) 2017-2020 Mattias Edlund
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "core/reference.h"
#include "core/pool_vector.h"
#include "core/resource.h"
class MeshSimplifier : public Reference {
GDCLASS(MeshSimplifier, Reference);
public:
MeshSimplifier();
protected:
static void _bind_methods() {}
};
#endif

474
mesh_utils.h Normal file
View File

@ -0,0 +1,474 @@
#ifndef VOXELMAN_MESH_UTILS_H
#define VOXELMAN_MESH_UTILS_H
/*
Copyright (c) 2020 Péter Magyar
Copyright(c) 2017-2020 Mattias Edlund
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "core/math/vector3.h"
/// A symmetric matrix.
struct SymmetricMatrix {
/// The m11 component.
double m0;
/// The m12 component.
double m1;
/// The m13 component.
double m2;
/// The m14 component.
double m3;
/// The m22 component.
double m4;
/// The m23 component.
double m5;
/// The m24 component.
double m6;
/// The m33 component.
double m7;
/// The m34 component.
double m8;
/// The m44 component.
double m9;
_FORCE_INLINE_ const double get(int p_index) const {
CRASH_BAD_INDEX(p_index, 10);
switch (p_index) {
case 0:
return m0;
case 1:
return m1;
case 2:
return m2;
case 3:
return m3;
case 4:
return m4;
case 5:
return m5;
case 6:
return m6;
case 7:
return m7;
case 8:
return m8;
case 9:
return m9;
}
return 0;
}
SymmetricMatrix() {
m0 = 0;
m1 = 0;
m2 = 0;
m3 = 0;
m4 = 0;
m5 = 0;
m6 = 0;
m7 = 0;
m8 = 0;
m9 = 0;
}
/// Creates a symmetric matrix with a value in each component.
SymmetricMatrix(double c) {
m0 = c;
m1 = c;
m2 = c;
m3 = c;
m4 = c;
m5 = c;
m6 = c;
m7 = c;
m8 = c;
m9 = c;
}
/// Creates a symmetric matrix.
SymmetricMatrix(double p_m0, double p_m1, double p_m2, double p_m3,
double p_m4, double p_m5, double p_m6, double p_m7, double p_m8, double p_m9) {
m0 = p_m0;
m1 = p_m1;
m2 = p_m2;
m3 = p_m3;
m4 = p_m4;
m5 = p_m5;
m6 = p_m6;
m7 = p_m7;
m8 = p_m8;
m9 = p_m9;
}
/// Creates a symmetric matrix from a plane.
SymmetricMatrix(double a, double b, double c, double d) {
m0 = a * a;
m1 = a * b;
m2 = a * c;
m3 = a * d;
m4 = b * b;
m5 = b * c;
m6 = b * d;
m7 = c * c;
m8 = c * d;
m9 = d * d;
}
SymmetricMatrix operator+(const SymmetricMatrix &p_m) const {
return SymmetricMatrix(m0 + p_m.m0, m1 + p_m.m1, m2 + p_m.m2, m3 + p_m.m3,
m4 + p_m.m4, m5 + p_m.m5, m6 + p_m.m6,
m7 + p_m.m7, m8 + p_m.m8,
m9 + p_m.m9);
}
void operator+=(const SymmetricMatrix &p_m) {
m0 += p_m.m0;
m1 += p_m.m1;
m2 += p_m.m2;
m3 += p_m.m3;
m4 += p_m.m4;
m5 += p_m.m5;
m6 += p_m.m6;
m7 += p_m.m7;
m8 += p_m.m8;
m9 += p_m.m9;
}
/// Determinant(0, 1, 2, 1, 4, 5, 2, 5, 7)
double Determinant1() {
double det =
m0 * m4 * m7 +
m2 * m1 * m5 +
m1 * m5 * m2 -
m2 * m4 * m2 -
m0 * m5 * m5 -
m1 * m1 * m7;
return det;
}
/// Determinant(1, 2, 3, 4, 5, 6, 5, 7, 8)
double Determinant2() {
double det =
m1 * m5 * m8 +
m3 * m4 * m7 +
m2 * m6 * m5 -
m3 * m5 * m5 -
m1 * m6 * m7 -
m2 * m4 * m8;
return det;
}
double Determinant3() {
double det =
m0 * m5 * m8 +
m3 * m1 * m7 +
m2 * m6 * m2 -
m3 * m5 * m2 -
m0 * m6 * m7 -
m2 * m1 * m8;
return det;
}
/// Determinant(0, 1, 3, 1, 4, 6, 2, 5, 8)
double Determinant4() {
double det =
m0 * m4 * m8 +
m3 * m1 * m5 +
m1 * m6 * m2 -
m3 * m4 * m2 -
m0 * m6 * m5 -
m1 * m1 * m8;
return det;
}
double Determinant(int a11, int a12, int a13,
int a21, int a22, int a23,
int a31, int a32, int a33) {
double det =
get(a11) * get(a22) * get(a33) +
get(a13) * get(a21) * get(a32) +
get(a12) * get(a23) * get(a31) -
get(a13) * get(a22) * get(a31) -
get(a11) * get(a23) * get(a32) -
get(a12) * get(a21) * get(a33);
return det;
}
void from_plane(double a, double b, double c, double d) {
m0 = a * a;
m1 = a * b;
m2 = a * c;
m3 = a * d;
m4 = b * b;
m5 = b * c;
m6 = b * d;
m7 = c * c;
m8 = c * d;
m9 = d * d;
}
void reset() {
m0 = 0;
m1 = 0;
m2 = 0;
m3 = 0;
m4 = 0;
m5 = 0;
m6 = 0;
m7 = 0;
m8 = 0;
m9 = 0;
}
};
struct MUTriangle {
int v0;
int v1;
int v2;
int subMeshIndex;
int va0;
int va1;
int va2;
double err0;
double err1;
double err2;
double err3;
bool deleted;
bool dirty;
Vector3 n;
_FORCE_INLINE_ int get(int p_index) {
return (p_index == 0 ? v0 : (p_index == 1 ? v1 : v2));
}
_FORCE_INLINE_ void set(int p_index, int value) {
CRASH_BAD_INDEX(p_index, 3);
switch (p_index) {
case 0:
v0 = value;
break;
case 1:
v1 = value;
break;
case 2:
v2 = value;
break;
}
}
_FORCE_INLINE_ void set_dirty(bool p_value) {
dirty = p_value;
}
_FORCE_INLINE_ void set_err0(double p_value) {
err0 = p_value;
}
_FORCE_INLINE_ void set_err1(double p_value) {
err1 = p_value;
}
_FORCE_INLINE_ void set_err2(double p_value) {
err2 = p_value;
}
_FORCE_INLINE_ void set_err3(double p_value) {
err3 = p_value;
}
_FORCE_INLINE_ void set_deleted(double p_value) {
deleted = p_value;
}
MUTriangle() {
v0 = 0;
v1 = 0;
v2 = 0;
subMeshIndex = 0;
va0 = 0;
va1 = 0;
va2 = 0;
err0 = err1 = err2 = err3 = 0;
deleted = dirty = false;
}
MUTriangle(int p_v0, int p_v1, int p_v2, int p_subMeshIndex) {
v0 = p_v0;
v1 = p_v1;
v2 = p_v2;
subMeshIndex = p_subMeshIndex;
va0 = p_v0;
va1 = p_v1;
va2 = p_v2;
err0 = err1 = err2 = err3 = 0;
deleted = dirty = false;
}
Vector3 GetAttributeIndices() {
Vector3 attributeIndices;
attributeIndices[0] = va0;
attributeIndices[1] = va1;
attributeIndices[2] = va2;
return attributeIndices;
}
void SetAttributeIndex(int index, int value) {
CRASH_BAD_INDEX(index, 3);
switch (index) {
case 0:
va0 = value;
break;
case 1:
va1 = value;
break;
case 2:
va2 = value;
break;
}
}
Vector3 GetErrors() {
Vector3 err;
err[0] = err0;
err[1] = err1;
err[2] = err2;
return err;
}
};
struct MUVertex {
Vector3 p;
int tstart;
int tcount;
SymmetricMatrix q;
bool borderEdge;
bool uvSeamEdge;
bool uvFoldoverEdge;
_FORCE_INLINE_ void set_tstart(int p_value) {
tstart = p_value;
}
_FORCE_INLINE_ void set_tcount(int p_value) {
tcount = p_value;
}
_FORCE_INLINE_ void set_border_edge(bool p_value) {
borderEdge = p_value;
}
_FORCE_INLINE_ void set_uv_seam_edge(bool p_value) {
uvSeamEdge = p_value;
}
_FORCE_INLINE_ void set_uv_foldover_edge(bool p_value) {
uvFoldoverEdge = p_value;
}
MUVertex() {
tstart = 0;
tcount = 0;
borderEdge = true;
uvSeamEdge = false;
uvFoldoverEdge = false;
}
MUVertex(float x, float y, float z) {
p = Vector3(x, y, z);
tstart = 0;
tcount = 0;
borderEdge = true;
uvSeamEdge = false;
uvFoldoverEdge = false;
}
MUVertex(Vector3 point) {
p = point;
tstart = 0;
tcount = 0;
borderEdge = true;
uvSeamEdge = false;
uvFoldoverEdge = false;
}
};
struct MURef {
int tid;
int tvertex;
MURef() {
tid = 0;
tvertex = 0;
}
void Set(int p_tid, int p_tvertex) {
tid = p_tid;
tvertex = p_tvertex;
}
};
struct BorderVertex {
int index;
int hash;
_FORCE_INLINE_ void set_index(int p_value) {
index = p_value;
}
BorderVertex() {
index = 0;
hash = 0;
}
BorderVertex(int p_index, int p_hash) {
index = p_index;
hash = p_hash;
}
};
struct BorderVertexComparer {
_FORCE_INLINE_ bool operator()(const BorderVertex &a, const BorderVertex &b) const { return a.hash < b.hash; }
};
#endif

37
register_types.cpp Normal file
View File

@ -0,0 +1,37 @@
#include "register_types.h"
#include "fast_quadratic_mesh_simplifier.h"
#include "mesh_simplifier.h"
/*
Copyright (c) 2020 Péter Magyar
Copyright(c) 2017-2020 Mattias Edlund
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
void register_fast_quadratic_mesh_simplifier_types() {
ClassDB::register_class<MeshSimplifier>();
ClassDB::register_class<FastQuadraticMeshSimplifier>();
}
void unregister_fast_quadratic_mesh_simplifier_types() {
}

32
register_types.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef FQMS_REGISTER_TYPES_H
#define FQMS_REGISTER_TYPES_H
/*
Copyright (c) 2020 Péter Magyar
Copyright(c) 2017-2020 Mattias Edlund
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
void register_fast_quadratic_mesh_simplifier_types();
void unregister_fast_quadratic_mesh_simplifier_types();
#endif