It scans all normals and blends them together so that surface normals are not perpendicular to each triangle, resulting in smoothing.
It also maintains correct lighting on sharp edges, but its not as fast as the smoother in Assimp, and I don't know why... yet

As it scans through the normal vectors, it eliminates found normals, resulting in an every decreasing search range, but somehow Assimp's smoother works twice as fast... I will figure it when I have time.
Code: Select all
CMeshManipulator::SmoothMesh.h
//! Smooths the normals of the mesh if < 30º divergence (adjustable**)
virtual void SmoothMesh(scene::IMesh* mesh, int uBuffer = -1, f32 fAngleLimit = 0);
CMeshManipulator::SmoothMesh.cpp
void CMeshManipulator::SmoothMesh(scene::IMesh* mesh, int iBuffer, f32 fAngleLimit) // If iBuffer is -1, smooths all buffers
{
u32 uMeshCount, l;
uMeshCount = mesh->getMeshBufferCount();
if (iBuffer < 0)
l = 0;
else
l = iBuffer;
if (fAngleLimit == 0.f)
fAngleLimit = 80.f / 180.f;
for (; l < uMeshCount; ++l) // Processing normals from each sub-mesh leaves normals at sub-mesh boundaries unsmoothed, but usually its not noticeable
{
IMeshBuffer *buf = mesh->getMeshBuffer(l);
if (!buf)
return;
video::S3DVertex *pV = (video::S3DVertex*)buf->getVertices(); // Casting a void* to a pointer to array
u32 uVertices = buf->getVertexCount();
u8 *pDone = (u8*)malloc(uVertices);
memset(pDone, 0, uVertices);
core::vector3df pos, n1, n2, n3, n4, n5, n6, n7, n8, Normal;
f32 nDot;
u32 ixFound[8];
u32 ixF, uix0, uMatched = 0, uOutsideAngle = 0;
// 1. Each vertex pos is joined to 4 other triangles in a simple surface, so take 1st pos, seek out the other matching positions
// 2. Compare the angles between their normals, and if below say 30º, set the normals to the angle between them all.
for (u32 ix = 0; ix < uVertices; ix++)
{
if (!pDone[ix]) // If vertex not already smoothed
{
uix0 = ix; // Index of ref vertex for pos compare
pos = pV[ix].Pos; // Load next ref vertex pos, and find all those sharing same position
ixFound[0] = ix;
ixFound[1] = 0;
ixFound[2] = 0;
ixFound[3] = 0;
ixFound[4] = 0;
ixFound[5] = 0;
ixFound[6] = 0;
ixFound[7] = 0;
ixF = 1;
for (u32 iv = ix + 1; iv < uVertices; iv++) // Read all vertices to find matching positions, ignoring those processed
{
if (pDone[iv]) // Ignore if already smoothed
continue;
//if (pos == pV[iv + 1].Pos)
if (core::equals(pos.X, pV[iv].Pos.X, core::ROUNDING_ERROR_f32) &&
core::equals(pos.Y, pV[iv].Pos.Y, core::ROUNDING_ERROR_f32) &&
core::equals(pos.Z, pV[iv].Pos.Z, core::ROUNDING_ERROR_f32))
{
// Check angle between normals is < 30º, as we don't want to smooth major edges
n1 = pV[uix0].Normal;
n2 = pV[iv].Normal;
nDot = n1.dotProduct(n2);
if (nDot > fAngleLimit) // 1.f is exactly the same dir
{
ixFound[ixF++] = iv;
uMatched++;
pDone[iv] = 1; // Mark done
#define SMOOTH_CHECK_DEPTH 7 // Contoured surfaces can easily have 7 triangles meeting at one point
if (ixF > SMOOTH_CHECK_DEPTH)
break;
}
//else
// uOutsideAngle++; // Testing only
}
}
if (ixF == 2) // Vertice matches found, so lets average their normals
{
n1 = pV[ixFound[0]].Normal;
n2 = pV[ixFound[1]].Normal;
Normal = (n1 + n2).normalize();
pV[ixFound[0]].Normal = Normal;
pV[ixFound[1]].Normal = Normal;
}
else
if (ixF == 3)
{
n1 = pV[ixFound[0]].Normal;
n2 = pV[ixFound[1]].Normal;
n3 = pV[ixFound[2]].Normal;
Normal = (n1 + n2 + n3).normalize();
pV[ixFound[0]].Normal = Normal;
pV[ixFound[1]].Normal = Normal;
pV[ixFound[2]].Normal = Normal;
}
else
if (ixF == 4)
{
n1 = pV[ixFound[0]].Normal;
n2 = pV[ixFound[1]].Normal;
n3 = pV[ixFound[2]].Normal;
n4 = pV[ixFound[3]].Normal;
Normal = (n1 + n2 + n3 + n4).normalize();
pV[ixFound[0]].Normal = Normal;
pV[ixFound[1]].Normal = Normal;
pV[ixFound[2]].Normal = Normal;
pV[ixFound[3]].Normal = Normal;
}
else
if (ixF == 5)
{
n1 = pV[ixFound[0]].Normal;
n2 = pV[ixFound[1]].Normal;
n3 = pV[ixFound[2]].Normal;
n4 = pV[ixFound[3]].Normal;
n5 = pV[ixFound[4]].Normal;
Normal = (n1 + n2 + n3 + n4 + n5).normalize();
pV[ixFound[0]].Normal = Normal;
pV[ixFound[1]].Normal = Normal;
pV[ixFound[2]].Normal = Normal;
pV[ixFound[3]].Normal = Normal;
pV[ixFound[4]].Normal = Normal;
}
else
if (ixF == 6)
{
n1 = pV[ixFound[0]].Normal;
n2 = pV[ixFound[1]].Normal;
n3 = pV[ixFound[2]].Normal;
n4 = pV[ixFound[3]].Normal;
n5 = pV[ixFound[4]].Normal;
n6 = pV[ixFound[5]].Normal;
Normal = (n1 + n2 + n3 + n4 + n5 + n6).normalize();
pV[ixFound[0]].Normal = Normal;
pV[ixFound[1]].Normal = Normal;
pV[ixFound[2]].Normal = Normal;
pV[ixFound[3]].Normal = Normal;
pV[ixFound[4]].Normal = Normal;
pV[ixFound[5]].Normal = Normal;
}
else
if (ixF == 7)
{
n1 = pV[ixFound[0]].Normal;
n2 = pV[ixFound[1]].Normal;
n3 = pV[ixFound[2]].Normal;
n4 = pV[ixFound[3]].Normal;
n5 = pV[ixFound[4]].Normal;
n6 = pV[ixFound[5]].Normal;
n7 = pV[ixFound[6]].Normal;
Normal = (n1 + n2 + n3 + n4 + n5 + n6 + n7).normalize();
pV[ixFound[0]].Normal = Normal;
pV[ixFound[1]].Normal = Normal;
pV[ixFound[2]].Normal = Normal;
pV[ixFound[3]].Normal = Normal;
pV[ixFound[4]].Normal = Normal;
pV[ixFound[5]].Normal = Normal;
pV[ixFound[6]].Normal = Normal;
}
else
if (ixF == 8)
{
n1 = pV[ixFound[0]].Normal;
n2 = pV[ixFound[1]].Normal;
n3 = pV[ixFound[2]].Normal;
n4 = pV[ixFound[3]].Normal;
n5 = pV[ixFound[4]].Normal;
n6 = pV[ixFound[5]].Normal;
n7 = pV[ixFound[6]].Normal;
n8 = pV[ixFound[7]].Normal;
Normal = (n1 + n2 + n3 + n4 + n5 + n6 + n7 + n8).normalize();
pV[ixFound[0]].Normal = Normal;
pV[ixFound[1]].Normal = Normal;
pV[ixFound[2]].Normal = Normal;
pV[ixFound[3]].Normal = Normal;
pV[ixFound[4]].Normal = Normal;
pV[ixFound[5]].Normal = Normal;
pV[ixFound[6]].Normal = Normal;
pV[ixFound[7]].Normal = Normal;
}
}
}
free(pDone);
if (iBuffer >= 0) // If sub-mesh specified, break, only one to do
break;
}
}