LOD Scene Node
Posted: Wed Jun 10, 2009 5:06 pm
You may have read my topic about level of detail, and this is the fruit of my labor. Right now it only does random edge collapse, so the quality isn't assured, but I plan on making it find the best edges to collapse. The pregeneration of the meshes is a little slow, so any help on speeding it up would be greatly appreciated. It can usually speed up about 100fps or so, depending on what meshes you choose and the distance, so on so on. Anyway, here is the code:
And a link to the project files so you can test it yourself:
http://lonesomeducky.googlepages.com/LODSceneNode.zip
If the bandwidth gets used up, just tell me and I'll upload it elsewhere. Good luck!
Code: Select all
lass CLODSceneNode : public scene::ISceneNode
{
class Vertex;
class Triangle;
class Triangle {
public:
Vertex* vertex[3];
Triangle(Vertex* v1, Vertex* v2, Vertex* v3) {
vertex[0] = v1;
vertex[1] = v2;
vertex[2] = v3;
for (int x = 0; x < 3; x++) {
vertex[x]->face.push_back(this);
for (int y = 0; y < 3; y++) {
bool add = true;
for(int i = 0; i < vertex[x]->neighbors.size(); i++) {
if (vertex[y]->id == vertex[x]->neighbors[i]->id || y == x) {
add = false;
}
}
if (add) {
vertex[x]->neighbors.push_back(vertex[y]);
}
}
}
}
bool HasVertex(Vertex* v) { return (v->position == vertex[0]->position || v->position == vertex[1]->position || v->position == vertex[2]->position ); };
void ReplaceVertex(Vertex* target, Vertex* v) {
for (int x = 0; x < 3; x++)
if (vertex[x]->position == target->position ) {
vertex[x] = v;
}
}
bool CheckAndRemove(int num, array<Triangle*> *list) {
if (vertex[0]->position == vertex[1]->position || vertex[1]->position == vertex[2]->position || vertex[2]->position == vertex[0]->position) {
list->erase(num);
return true;
}
}
};
class Vertex {
public:
vector3df position;
array<Vertex*> neighbors;
array<Triangle*> face;
int id;
};
IMeshBuffer* BuildMeshBufferFromTriangles(IMeshBuffer* oldmb, array<Triangle*> arr) {
SMeshBuffer* newm = new SMeshBuffer();
newm->Material = oldmb->getMaterial();
u16* indices = new u16[arr.size()*3];
for (int x = 0; x < arr.size(); x++) {
for (int y = 0; y < 3; y++) {
indices[x*3+y] = arr[x]->vertex[y]->id;
}
}
newm->append(oldmb->getVertices(),oldmb->getVertexCount(),indices,arr.size()*3);
return newm;
}
IMesh* DefaultMesh;
IMesh** LODMesh;
IMesh* CurrentMesh;
IMeshBuffer* test;
char CurrentLevel;
char LevelCount;
float LODBegin;
float LODLast;
bool LODOn;
array<Vertex*> Verts[8];
array<Triangle*> Tris[8];
public:
CLODSceneNode(IMesh* mesh, scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id, int numOfCollapseOnLast, char numOfLevels = 4, float LODBeginDist = 10, float LODLastDist = 30, bool combineDuplicateVertices = true)
: scene::ISceneNode(parent, mgr, id)
{
DefaultMesh = mesh;
CurrentMesh = mesh;
//CurrentMesh->getMeshBuffer(0)->getMaterial().Wireframe = true;
LODBegin = LODBeginDist;
LODLast = LODLastDist;
LevelCount = numOfLevels;
CurrentLevel = 0;
LODMesh = new IMesh*[numOfLevels];
LODOn = true;
if (combineDuplicateVertices)
DefaultMesh = SceneManager->getMeshManipulator()->createMeshWelded(DefaultMesh);
for (int x = 0; x < DefaultMesh->getMeshBufferCount(); x++) {
S3DVertex* verts = (S3DVertex*)DefaultMesh->getMeshBuffer(x)->getVertices();
for (int y = 0; y < DefaultMesh->getMeshBuffer(x)->getVertexCount(); y++) {
Vertex* vert = new Vertex();
vert->position = verts[y].Pos;
vert->id = y;
Verts[x].push_back(vert);
}
}
for (int x = 0; x < DefaultMesh->getMeshBufferCount(); x++) {
u16* indices = DefaultMesh->getMeshBuffer(x)->getIndices();
for (int y = 0; y < DefaultMesh->getMeshBuffer(x)->getIndexCount(); y += 3) {
Triangle *tri = new Triangle(Verts[x][indices[y]],Verts[x][indices[y+1]],Verts[x][indices[y+2]]);
Tris[x].push_back(tri);
}
}
for (int x = 0; x < LevelCount; x++)
LODMesh[x] = DefaultMesh;
int collapsePerLevel = numOfCollapseOnLast/(LevelCount-1);
int collapsePerBuffer = collapsePerLevel/DefaultMesh->getMeshBufferCount();
for (int x = 1; x < LevelCount; x++) {
SMesh* newLod = new SMesh();
for (int y = 0; y < DefaultMesh->getMeshBufferCount(); y++) {
for (int i = 0; i < collapsePerBuffer; i++) {
int vert = rand()%Verts[y].size();
int neighbor = rand()%Verts[y][vert]->neighbors.size();
for (int u = 0; u < Tris[y].size(); u++) {
if (Tris[y][u]->HasVertex(Verts[y][vert])) {
Tris[y][u]->ReplaceVertex(Verts[y][vert],Verts[y][vert]->neighbors[neighbor]);
}
}
}
SMeshBuffer* mn = new SMeshBuffer();
for (int x = 0; x < Tris[0].size(); x++) {
if (Tris[0][x]->CheckAndRemove(x,&Tris[0])) {
x--;
}
}
mn = (SMeshBuffer*)BuildMeshBufferFromTriangles(DefaultMesh->getMeshBuffer(y),Tris[y]);
newLod->addMeshBuffer(mn);
newLod->recalculateBoundingBox();
}
LODMesh[x] = newLod;
}
}
virtual void OnRegisterSceneNode()
{
if (IsVisible)
SceneManager->registerNodeForRendering(this);
ISceneNode::OnRegisterSceneNode();
}
virtual void render()
{
if (LODOn) {
vector3df cameraPos = SceneManager->getActiveCamera()->getPosition();
f32 dist = AbsoluteTransformation.getTranslation().getDistanceFrom(cameraPos);
CurrentLevel = 0;
float increment = (LODLast-LODBegin)/LevelCount;
for (int x = 0; x < LevelCount; x++) {
if (dist >= (LODBegin+increment*x)*(LODBegin+increment*x))
CurrentLevel = x;
}
CurrentMesh = LODMesh[CurrentLevel];
}
else
CurrentMesh = DefaultMesh;
IVideoDriver* driver = SceneManager->getVideoDriver();
driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
for (int x = 0; x < CurrentMesh->getMeshBufferCount(); x++) {
driver->setMaterial(CurrentMesh->getMeshBuffer(x)->getMaterial());
driver->drawMeshBuffer(CurrentMesh->getMeshBuffer(x));
}
}
virtual const core::aabbox3d<f32>& getBoundingBox() const
{
return CurrentMesh->getBoundingBox();
}
void setLODOn(bool on) { LODOn = on; };
bool getLODOn() { return LODOn; };
};
http://lonesomeducky.googlepages.com/LODSceneNode.zip
If the bandwidth gets used up, just tell me and I'll upload it elsewhere. Good luck!
