- // Set render target
- driver->setRenderTarget(rtt, true, true, video::SColor(0,0,0,0));
-
- // Get a scene manager
- scene::ISceneManager *smgr_main = device->getSceneManager();
- assert(smgr_main);
- scene::ISceneManager *smgr = smgr_main->createNewSceneManager();
- assert(smgr);
-
- scene::IMeshSceneNode* meshnode = smgr->addMeshSceneNode(mesh, NULL, -1, v3f(0,0,0), v3f(0,0,0), v3f(1,1,1), true);
- meshnode->setMaterialFlag(video::EMF_LIGHTING, true);
- meshnode->setMaterialFlag(video::EMF_ANTI_ALIASING, true);
- meshnode->setMaterialFlag(video::EMF_BILINEAR_FILTER, true);
-
- scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0,
- camera_position, camera_lookat);
- // second parameter of setProjectionMatrix (isOrthogonal) is ignored
- camera->setProjectionMatrix(camera_projection_matrix, false);
-
- smgr->setAmbientLight(ambient_light);
- smgr->addLightSceneNode(0, light_position, light_color, light_radius);
-
- // Render scene
- driver->beginScene(true, true, video::SColor(0,0,0,0));
- smgr->drawAll();
- driver->endScene();
-
- // NOTE: The scene nodes should not be dropped, otherwise
- // smgr->drop() segfaults
- /*cube->drop();
- camera->drop();
- light->drop();*/
- // Drop scene manager
- smgr->drop();
-
- // Unset render target
- driver->setRenderTarget(0, true, true, 0);
-
- return rtt;
+private:
+ s32 cache[cachesize];
+ vcache *vc;
+ tcache *tc;
+};
+
+/**
+Vertex cache optimization according to the Forsyth paper:
+http://home.comcast.net/~tom_forsyth/papers/fast_vert_cache_opt.html
+
+The function is thread-safe (read: you can optimize several meshes in different threads)
+
+\param mesh Source mesh for the operation. */
+scene::IMesh* createForsythOptimizedMesh(const scene::IMesh *mesh)
+{
+ if (!mesh)
+ return 0;
+
+ scene::SMesh *newmesh = new scene::SMesh();
+ newmesh->BoundingBox = mesh->getBoundingBox();
+
+ const u32 mbcount = mesh->getMeshBufferCount();
+
+ for (u32 b = 0; b < mbcount; ++b)
+ {
+ const scene::IMeshBuffer *mb = mesh->getMeshBuffer(b);
+
+ if (mb->getIndexType() != video::EIT_16BIT)
+ {
+ //os::Printer::log("Cannot optimize a mesh with 32bit indices", ELL_ERROR);
+ newmesh->drop();
+ return 0;
+ }
+
+ const u32 icount = mb->getIndexCount();
+ const u32 tcount = icount / 3;
+ const u32 vcount = mb->getVertexCount();
+ const u16 *ind = mb->getIndices();
+
+ vcache *vc = new vcache[vcount];
+ tcache *tc = new tcache[tcount];
+
+ f_lru lru(vc, tc);
+
+ // init
+ for (u16 i = 0; i < vcount; i++)
+ {
+ vc[i].score = 0;
+ vc[i].cachepos = -1;
+ vc[i].NumActiveTris = 0;
+ }
+
+ // First pass: count how many times a vert is used
+ for (u32 i = 0; i < icount; i += 3)
+ {
+ vc[ind[i]].NumActiveTris++;
+ vc[ind[i + 1]].NumActiveTris++;
+ vc[ind[i + 2]].NumActiveTris++;
+
+ const u32 tri_ind = i/3;
+ tc[tri_ind].ind[0] = ind[i];
+ tc[tri_ind].ind[1] = ind[i + 1];
+ tc[tri_ind].ind[2] = ind[i + 2];
+ }
+
+ // Second pass: list of each triangle
+ for (u32 i = 0; i < tcount; i++)
+ {
+ vc[tc[i].ind[0]].tris.push_back(i);
+ vc[tc[i].ind[1]].tris.push_back(i);
+ vc[tc[i].ind[2]].tris.push_back(i);
+
+ tc[i].drawn = false;
+ }
+
+ // Give initial scores
+ for (u16 i = 0; i < vcount; i++)
+ {
+ vc[i].score = FindVertexScore(&vc[i]);
+ }
+ for (u32 i = 0; i < tcount; i++)
+ {
+ tc[i].score =
+ vc[tc[i].ind[0]].score +
+ vc[tc[i].ind[1]].score +
+ vc[tc[i].ind[2]].score;
+ }
+
+ switch(mb->getVertexType())
+ {
+ case video::EVT_STANDARD:
+ {
+ video::S3DVertex *v = (video::S3DVertex *) mb->getVertices();
+
+ scene::SMeshBuffer *buf = new scene::SMeshBuffer();
+ buf->Material = mb->getMaterial();
+
+ buf->Vertices.reallocate(vcount);
+ buf->Indices.reallocate(icount);
+
+ core::map<const video::S3DVertex, const u16> sind; // search index for fast operation
+ typedef core::map<const video::S3DVertex, const u16>::Node snode;
+
+ // Main algorithm
+ u32 highest = 0;
+ u32 drawcalls = 0;
+ for (;;)
+ {
+ if (tc[highest].drawn)
+ {
+ bool found = false;
+ float hiscore = 0;
+ for (u32 t = 0; t < tcount; t++)
+ {
+ if (!tc[t].drawn)
+ {
+ if (tc[t].score > hiscore)
+ {
+ highest = t;
+ hiscore = tc[t].score;
+ found = true;
+ }
+ }
+ }
+ if (!found)
+ break;
+ }
+
+ // Output the best triangle
+ u16 newind = buf->Vertices.size();
+
+ snode *s = sind.find(v[tc[highest].ind[0]]);
+
+ if (!s)
+ {
+ buf->Vertices.push_back(v[tc[highest].ind[0]]);
+ buf->Indices.push_back(newind);
+ sind.insert(v[tc[highest].ind[0]], newind);
+ newind++;
+ }
+ else
+ {
+ buf->Indices.push_back(s->getValue());
+ }
+
+ s = sind.find(v[tc[highest].ind[1]]);
+
+ if (!s)
+ {
+ buf->Vertices.push_back(v[tc[highest].ind[1]]);
+ buf->Indices.push_back(newind);
+ sind.insert(v[tc[highest].ind[1]], newind);
+ newind++;
+ }
+ else
+ {
+ buf->Indices.push_back(s->getValue());
+ }
+
+ s = sind.find(v[tc[highest].ind[2]]);
+
+ if (!s)
+ {
+ buf->Vertices.push_back(v[tc[highest].ind[2]]);
+ buf->Indices.push_back(newind);
+ sind.insert(v[tc[highest].ind[2]], newind);
+ }
+ else
+ {
+ buf->Indices.push_back(s->getValue());
+ }
+
+ vc[tc[highest].ind[0]].NumActiveTris--;
+ vc[tc[highest].ind[1]].NumActiveTris--;
+ vc[tc[highest].ind[2]].NumActiveTris--;
+
+ tc[highest].drawn = true;
+
+ for (u16 j : tc[highest].ind) {
+ vcache *vert = &vc[j];
+ for (u16 t = 0; t < vert->tris.size(); t++)
+ {
+ if (highest == vert->tris[t])
+ {
+ vert->tris.erase(t);
+ break;
+ }
+ }
+ }
+
+ lru.add(tc[highest].ind[0]);
+ lru.add(tc[highest].ind[1]);
+ highest = lru.add(tc[highest].ind[2], true);
+ drawcalls++;
+ }
+
+ buf->setBoundingBox(mb->getBoundingBox());
+ newmesh->addMeshBuffer(buf);
+ buf->drop();
+ }
+ break;
+ case video::EVT_2TCOORDS:
+ {
+ video::S3DVertex2TCoords *v = (video::S3DVertex2TCoords *) mb->getVertices();
+
+ scene::SMeshBufferLightMap *buf = new scene::SMeshBufferLightMap();
+ buf->Material = mb->getMaterial();
+
+ buf->Vertices.reallocate(vcount);
+ buf->Indices.reallocate(icount);
+
+ core::map<const video::S3DVertex2TCoords, const u16> sind; // search index for fast operation
+ typedef core::map<const video::S3DVertex2TCoords, const u16>::Node snode;
+
+ // Main algorithm
+ u32 highest = 0;
+ u32 drawcalls = 0;
+ for (;;)
+ {
+ if (tc[highest].drawn)
+ {
+ bool found = false;
+ float hiscore = 0;
+ for (u32 t = 0; t < tcount; t++)
+ {
+ if (!tc[t].drawn)
+ {
+ if (tc[t].score > hiscore)
+ {
+ highest = t;
+ hiscore = tc[t].score;
+ found = true;
+ }
+ }
+ }
+ if (!found)
+ break;
+ }
+
+ // Output the best triangle
+ u16 newind = buf->Vertices.size();
+
+ snode *s = sind.find(v[tc[highest].ind[0]]);
+
+ if (!s)
+ {
+ buf->Vertices.push_back(v[tc[highest].ind[0]]);
+ buf->Indices.push_back(newind);
+ sind.insert(v[tc[highest].ind[0]], newind);
+ newind++;
+ }
+ else
+ {
+ buf->Indices.push_back(s->getValue());
+ }
+
+ s = sind.find(v[tc[highest].ind[1]]);
+
+ if (!s)
+ {
+ buf->Vertices.push_back(v[tc[highest].ind[1]]);
+ buf->Indices.push_back(newind);
+ sind.insert(v[tc[highest].ind[1]], newind);
+ newind++;
+ }
+ else
+ {
+ buf->Indices.push_back(s->getValue());
+ }
+
+ s = sind.find(v[tc[highest].ind[2]]);
+
+ if (!s)
+ {
+ buf->Vertices.push_back(v[tc[highest].ind[2]]);
+ buf->Indices.push_back(newind);
+ sind.insert(v[tc[highest].ind[2]], newind);
+ }
+ else
+ {
+ buf->Indices.push_back(s->getValue());
+ }
+
+ vc[tc[highest].ind[0]].NumActiveTris--;
+ vc[tc[highest].ind[1]].NumActiveTris--;
+ vc[tc[highest].ind[2]].NumActiveTris--;
+
+ tc[highest].drawn = true;
+
+ for (u16 j : tc[highest].ind) {
+ vcache *vert = &vc[j];
+ for (u16 t = 0; t < vert->tris.size(); t++)
+ {
+ if (highest == vert->tris[t])
+ {
+ vert->tris.erase(t);
+ break;
+ }
+ }
+ }
+
+ lru.add(tc[highest].ind[0]);
+ lru.add(tc[highest].ind[1]);
+ highest = lru.add(tc[highest].ind[2]);
+ drawcalls++;
+ }
+
+ buf->setBoundingBox(mb->getBoundingBox());
+ newmesh->addMeshBuffer(buf);
+ buf->drop();
+
+ }
+ break;
+ case video::EVT_TANGENTS:
+ {
+ video::S3DVertexTangents *v = (video::S3DVertexTangents *) mb->getVertices();
+
+ scene::SMeshBufferTangents *buf = new scene::SMeshBufferTangents();
+ buf->Material = mb->getMaterial();
+
+ buf->Vertices.reallocate(vcount);
+ buf->Indices.reallocate(icount);
+
+ core::map<const video::S3DVertexTangents, const u16> sind; // search index for fast operation
+ typedef core::map<const video::S3DVertexTangents, const u16>::Node snode;
+
+ // Main algorithm
+ u32 highest = 0;
+ u32 drawcalls = 0;
+ for (;;)
+ {
+ if (tc[highest].drawn)
+ {
+ bool found = false;
+ float hiscore = 0;
+ for (u32 t = 0; t < tcount; t++)
+ {
+ if (!tc[t].drawn)
+ {
+ if (tc[t].score > hiscore)
+ {
+ highest = t;
+ hiscore = tc[t].score;
+ found = true;
+ }
+ }
+ }
+ if (!found)
+ break;
+ }
+
+ // Output the best triangle
+ u16 newind = buf->Vertices.size();
+
+ snode *s = sind.find(v[tc[highest].ind[0]]);
+
+ if (!s)
+ {
+ buf->Vertices.push_back(v[tc[highest].ind[0]]);
+ buf->Indices.push_back(newind);
+ sind.insert(v[tc[highest].ind[0]], newind);
+ newind++;
+ }
+ else
+ {
+ buf->Indices.push_back(s->getValue());
+ }
+
+ s = sind.find(v[tc[highest].ind[1]]);
+
+ if (!s)
+ {
+ buf->Vertices.push_back(v[tc[highest].ind[1]]);
+ buf->Indices.push_back(newind);
+ sind.insert(v[tc[highest].ind[1]], newind);
+ newind++;
+ }
+ else
+ {
+ buf->Indices.push_back(s->getValue());
+ }
+
+ s = sind.find(v[tc[highest].ind[2]]);
+
+ if (!s)
+ {
+ buf->Vertices.push_back(v[tc[highest].ind[2]]);
+ buf->Indices.push_back(newind);
+ sind.insert(v[tc[highest].ind[2]], newind);
+ }
+ else
+ {
+ buf->Indices.push_back(s->getValue());
+ }
+
+ vc[tc[highest].ind[0]].NumActiveTris--;
+ vc[tc[highest].ind[1]].NumActiveTris--;
+ vc[tc[highest].ind[2]].NumActiveTris--;
+
+ tc[highest].drawn = true;
+
+ for (u16 j : tc[highest].ind) {
+ vcache *vert = &vc[j];
+ for (u16 t = 0; t < vert->tris.size(); t++)
+ {
+ if (highest == vert->tris[t])
+ {
+ vert->tris.erase(t);
+ break;
+ }
+ }
+ }
+
+ lru.add(tc[highest].ind[0]);
+ lru.add(tc[highest].ind[1]);
+ highest = lru.add(tc[highest].ind[2]);
+ drawcalls++;
+ }
+
+ buf->setBoundingBox(mb->getBoundingBox());
+ newmesh->addMeshBuffer(buf);
+ buf->drop();
+ }
+ break;
+ }
+
+ delete [] vc;
+ delete [] tc;
+
+ } // for each meshbuffer
+
+ return newmesh;