LOD Scene Node

Announce new projects or updates of Irrlicht Engine related tools, games, and applications.
Also check the Wiki
Lonesome Ducky
Competition winner
Posts: 1123
Joined: Sun Jun 10, 2007 11:14 pm

LOD Scene Node

Post by Lonesome Ducky »

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:

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; };

};
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! :wink:
christianclavet
Posts: 1638
Joined: Mon Apr 30, 2007 3:24 am
Location: Montreal, CANADA
Contact:

Post by christianclavet »

Nice work Lonesome Ducky!

That's one feature I'll surely need.
Lonesome Ducky
Competition winner
Posts: 1123
Joined: Sun Jun 10, 2007 11:14 pm

Post by Lonesome Ducky »

With this version you may just wanna test it because it might mess up your mesh :lol: I gotta implement the edge cost function, which I'll probably have done in a day or two. Thanks for the positive comment :D
wITTus
Posts: 167
Joined: Tue Jun 24, 2008 7:41 pm
Location: Germany

Post by wITTus »

Congratulations Lonesome Ducky. That's already awesome! I hope you get it to work with some more intelligent vertex dropping. I've put it already into the Wiki. Feel free to add a few words to the article ;)
Generated Documentation for BlindSide's irrNetLite.
"When I heard birds chirping, I knew I didn't have much time left before my mind would go." - clinko
FuzzYspo0N
Posts: 914
Joined: Fri Aug 03, 2007 12:43 pm
Location: South Africa
Contact:

Post by FuzzYspo0N »

Yea good work, i see a few optimisations already, if i have some time during lunch ill try suggesting some things!

Keep at it.
kingdutch
Posts: 76
Joined: Tue Sep 02, 2008 7:01 am

Post by kingdutch »

Really nice, I'll definately keep an eye out on the topic and tell you if I see any improvements because this is just what I've been looking for.

Will you implement culling when the scenenode is 'distance' past its lowest cull level (and maybe just include a boolean to allow that to be enabled or disabled)?
if (msg.getRubbishFactor() > rubbishLimit) { ignorePost(); cout << how it should be done << "\n"; }
stefbuet
Competition winner
Posts: 495
Joined: Sun Dec 09, 2007 4:13 pm
Location: france

Post by stefbuet »

Thank!!!!!!!!!!!!!!!
sudi
Posts: 1686
Joined: Fri Aug 26, 2005 8:38 pm

Post by sudi »

i tried it...it doesn't work. The LOD level is changed but the mesh is the same on every level.

Here is my code:
Header
Source
We're programmers. Programmers are, in their hearts, architects, and the first thing they want to do when they get to a site is to bulldoze the place flat and build something grand. We're not excited by renovation:tinkering,improving,planting flower beds.
Lonesome Ducky
Competition winner
Posts: 1123
Joined: Sun Jun 10, 2007 11:14 pm

Post by Lonesome Ducky »

You need to have way more collapses. 10 is almost nothing. Try 300 collapses, then you'll get noticeable results.
Lonesome Ducky
Competition winner
Posts: 1123
Joined: Sun Jun 10, 2007 11:14 pm

Post by Lonesome Ducky »

Alright, after this had been brought up in another thread, I decided it might be time to finish it. I need only to add collapsing based on edge cost and re mapping of uv's. Is there still interest in this project? I'd hate to work on something that nobody wants :lol:
christianclavet
Posts: 1638
Joined: Mon Apr 30, 2007 3:24 am
Location: Montreal, CANADA
Contact:

Post by christianclavet »

Sure! Will be usefull a lot!
devsh
Competition winner
Posts: 2057
Joined: Tue Dec 09, 2008 6:00 pm
Location: UK
Contact:

Post by devsh »

I'd love it for PNS PhysX
shadowslair
Posts: 758
Joined: Mon Mar 31, 2008 3:32 pm
Location: Bulgaria

Post by shadowslair »

Lonesome Ducky wrote: Is there still interest in this project?
You bet, buddy! Keep up the good work! :wink:
"Although we walk on the ground and step in the mud... our dreams and endeavors reach the immense skies..."
Virion
Competition winner
Posts: 2148
Joined: Mon Dec 18, 2006 5:04 am

Post by Virion »

that would be cool
Lonesome Ducky
Competition winner
Posts: 1123
Joined: Sun Jun 10, 2007 11:14 pm

Post by Lonesome Ducky »

Well, I pretty much have to rewrite my code from scratch now, as the way I wrote it before doesn't play nicely with edge cost.
Post Reply