This code is used to create a cartoon outline effect for any unanimated mesh (animated mesh support coming up soon). It works by moving the vertices out by their normals, and flipping the faces.
![Image](http://i117.photobucket.com/albums/o73/Azail/axe1.png)
Depending on the mesh, it may take a while to combine duplicate vertices (I'm not terribly good at optimization). Without it though, it ends up looking like this:
![Image](http://i117.photobucket.com/albums/o73/Azail/axe2.png)
Animated mesh support coming up as soon as I get the hang of the ISkinnedMesh interface. Anyway, here's the code:
Code: Select all
struct indexRedirect {
int oldIndex;
int newIndex;
};
struct vertCombine {
irr::core::vector3df totalPos;
irr::core::vector3df totalNormal;
int count;
};
// Adds a toon outline to a mesh. Works by copying the mesh, and expanding by the normals. It then flips the faces.
irr::scene::IMesh* createToonOutlineMesh(irr::scene::IMesh* mesh, float size, irr::video::SColor color) {
irr::scene::SMesh* toonMesh = new irr::scene::SMesh();
// Copy the mesh buffers
for (int i = 0; i < mesh->getMeshBufferCount(); i++) {
irr::scene::SMeshBuffer* sourceBuffer = (irr::scene::SMeshBuffer*)mesh->getMeshBuffer(i);
irr::scene::SMeshBuffer* destBuffer = new irr::scene::SMeshBuffer();
irr::video::S3DVertex* copyVertices = new irr::video::S3DVertex[sourceBuffer->getVertexCount()];
for (int j = 0; j < sourceBuffer->getVertexCount(); j++) {
copyVertices[j] = sourceBuffer->Vertices[j];
}
irr::u16* copyIndices = new irr::u16[sourceBuffer->getIndexCount()];
for (int j = 0; j < sourceBuffer->getIndexCount(); j++) {
copyIndices[j] = sourceBuffer->Indices[j];
}
// Find duplicate vertices, then add an indexRedirect so indices can be updated and the vertices can later be collapsed
irr::video::S3DVertex* sourceVertices = (irr::video::S3DVertex*)sourceBuffer->getVertices();
vertCombine* combiners = new vertCombine[sourceBuffer->getVertexCount()];
irr::core::array<indexRedirect> redirects;
bool *check = new bool[sourceBuffer->getVertexCount()];
for (int j = 0; j < sourceBuffer->getVertexCount(); j++) {
check[j] = true;
combiners[j].totalPos = sourceVertices[j].Pos;
combiners[j].totalNormal = sourceVertices[j].Normal;
combiners[j].count = 1;
}
for (int j = 0; j < sourceBuffer->getVertexCount(); j++) {
if (check[j]) {
// Check for vertices that are very close
for (int k = j; k < sourceBuffer->getVertexCount(); k++) {
if (check[k]) {
if (sourceVertices[j].Pos.getDistanceFromSQ(sourceVertices[k].Pos) <= irr::core::ROUNDING_ERROR_f32*irr::core::ROUNDING_ERROR_f32) {
check[k] = false;
indexRedirect redir;
redir.newIndex = j;
redir.oldIndex = k;
redirects.push_back(redir);
}
}
}
}
}
delete [] check;
// Combine vertices
for (int j = 0; j < redirects.size(); j++) {
combiners[redirects[j].newIndex].totalPos += sourceVertices[redirects[j].oldIndex].Pos;
combiners[redirects[j].newIndex].totalNormal += sourceVertices[redirects[j].oldIndex].Normal;
combiners[redirects[j].newIndex].count++;
}
for (int j = 0; j < redirects.size(); j++) {
copyVertices[redirects[j].newIndex].Pos = combiners[redirects[j].newIndex].totalPos / combiners[redirects[j].newIndex].count;
copyVertices[redirects[j].newIndex].Normal = combiners[redirects[j].newIndex].totalNormal / combiners[redirects[j].newIndex].count;
copyVertices[redirects[j].newIndex].Normal.normalize();
}
// After combining the vertices, fix indices so they no longer reference dead vertices
for (int j = 0; j < redirects.size(); j++) {
for (int k = 0; k < sourceBuffer->getIndexCount(); k++) {
if (copyIndices[k] == redirects[j].oldIndex) {
copyIndices[k] = redirects[j].newIndex;
}
}
}
delete [] combiners;
// Now expand the mesh, moving the vertices along their normals
for (int j = 0; j < sourceBuffer->getVertexCount(); j++) {
copyVertices[j].Color = color;
copyVertices[j].Pos += copyVertices[j].Normal*size;
}
destBuffer->append(copyVertices,sourceBuffer->getVertexCount(),copyIndices,sourceBuffer->getIndexCount());
destBuffer->recalculateBoundingBox();
// Now set the material
destBuffer->Material = destBuffer->Material;
destBuffer->Material.Lighting = false;
destBuffer->Material.BackfaceCulling = false;
destBuffer->Material.FrontfaceCulling = true;
destBuffer->Material.DiffuseColor = color;
destBuffer->Material.ColorMaterial = irr::video::ECM_DIFFUSE_AND_AMBIENT;
destBuffer->Material.TextureLayer[0].Texture = 0;
toonMesh->addMeshBuffer(destBuffer);
}
toonMesh->recalculateBoundingBox();
return (irr::scene::IMesh*)toonMesh;
}