From 2ec010d0a5d872c22f2ce83007acf3bc6f56f9ff Mon Sep 17 00:00:00 2001 From: hybrid Date: Tue, 4 Jun 2013 14:47:05 +0000 Subject: [PATCH] Merged revisions 4511-4534 from trunk. OpenGL bug fix, wchar filesystem fix, clone method fix, tris fix, heightmap optimization, x mesh loader fix git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/branches/ogl-es@4535 dfc29bdd-3216-0410-991c-e03cc46cb475 --- include/IMeshManipulator.h | 26 ++ source/Irrlicht/CMeshManipulator.cpp | 342 ++++++++++++++++++- source/Irrlicht/CMeshManipulator.h | 6 + source/Irrlicht/COBJMeshWriter.cpp | 2 +- source/Irrlicht/COpenGLDriver.cpp | 38 +-- source/Irrlicht/COpenGLDriver.h | 3 +- source/Irrlicht/CSceneNodeAnimatorDelete.cpp | 7 +- source/Irrlicht/CXMeshFileLoader.cpp | 4 + 8 files changed, 394 insertions(+), 34 deletions(-) diff --git a/include/IMeshManipulator.h b/include/IMeshManipulator.h index c7b4a305..b48b336f 100644 --- a/include/IMeshManipulator.h +++ b/include/IMeshManipulator.h @@ -295,6 +295,32 @@ namespace scene \return A new mesh optimized for the vertex cache. */ virtual IMesh* createForsythOptimizedMesh(const IMesh *mesh) const = 0; + //! Optimize the mesh with an algorithm tuned for heightmaps. + /** + This differs from usual simplification methods in two ways: + - it's intended to be lossless + - it has special care for the borders, which are useful with heightmap tiles + + This function is thread-safe. Remember to weld afterwards - this + function only moves vertices, it does not weld. + + \param mesh Mesh to operate on. + */ + virtual void heightmapOptimizeMesh(IMesh * const mesh, const f32 tolerance = core::ROUNDING_ERROR_f32) const = 0; + + //! Optimize the meshbuffer with an algorithm tuned for heightmaps. + /** + This differs from usual simplification methods in two ways: + - it's intended to be lossless + - it has special care for the borders, which are useful with heightmap tiles + + This function is thread-safe. Remember to weld afterwards - this + function only moves vertices, it does not weld. + + \param mb Meshbuffer to operate on. + */ + virtual void heightmapOptimizeMesh(IMeshBuffer * const mb, const f32 tolerance = core::ROUNDING_ERROR_f32) const = 0; + //! Apply a manipulator on the Meshbuffer /** \param func A functor defining the mesh manipulation. \param buffer The Meshbuffer to apply the manipulator to. diff --git a/source/Irrlicht/CMeshManipulator.cpp b/source/Irrlicht/CMeshManipulator.cpp index 102c19e2..6d56b8d5 100644 --- a/source/Irrlicht/CMeshManipulator.cpp +++ b/source/Irrlicht/CMeshManipulator.cpp @@ -8,6 +8,7 @@ #include "SAnimatedMesh.h" #include "os.h" #include "irrMap.h" +#include "triangle3d.h" namespace irr { @@ -916,13 +917,30 @@ IMesh* CMeshManipulator::createMeshWelded(IMesh *mesh, f32 tolerance) const break; } - // write the buffer's index list - core::array &Indices = *outIdx; + // Clean up any degenerate tris + core::array &Indices = *outIdx; + Indices.clear(); + Indices.reallocate(indexCount); + for (u32 i = 0; i < indexCount; i+=3) + { + u16 a, b, c; + a = redirects[indices[i]]; + b = redirects[indices[i+1]]; + c = redirects[indices[i+2]]; - Indices.set_used(indexCount); - for (u32 i=0; igetMeshBufferCount(); + + for (u32 i = 0; i < max; i++) + { + IMeshBuffer * const mb = m->getMeshBuffer(i); + + heightmapOptimizeMesh(mb, tolerance); + } +} + +//! Optimizes the mesh using an algorithm tuned for heightmaps. +void CMeshManipulator::heightmapOptimizeMesh(IMeshBuffer * const mb, const f32 tolerance) const +{ + using namespace core; + using namespace video; + + array edges; + + const u32 idxs = mb->getIndexCount(); + const u32 verts = mb->getVertexCount(); + + u16 *ind = mb->getIndices(); + S3DVertex *vert = (S3DVertex *) mb->getVertices(); + + // First an acceleration structure: given this vert, which triangles touch it? + // Using this drops two exponents off the algorightm complexity, O(n^4) > O(n^2) + // Other optimizations brought it down to O(n). + u32 **accel = (u32 **) malloc(verts * sizeof(u32 *)); + for (u32 i = 0; i < verts; i++) + { + accel[i] = (u32 *) calloc(HEIGHT_TRIACCEL_MAX, sizeof(u32)); + for (u32 j = 0; j < HEIGHT_TRIACCEL_MAX; j++) + { + accel[i][j] = USHRT_MAX; + } + } + + u16 *cur = (u16 *) calloc(verts, sizeof(u16)); + for (u32 j = 0; j < idxs; j+=3) + { + u32 v = ind[j]; + + if (cur[v] >= HEIGHT_TRIACCEL_MAX) + { + os::Printer::log("Too complex mesh to optimize, aborting."); + goto donehere; + } + + accel[v][cur[v]] = j; + cur[v]++; + + // Unrolled tri loop, parts 2 and 3 + v = ind[j+1]; + + if (cur[v] >= HEIGHT_TRIACCEL_MAX) + { + os::Printer::log("Too complex mesh to optimize, aborting."); + goto donehere; + } + + accel[v][cur[v]] = j; + cur[v]++; + + v = ind[j+2]; + + if (cur[v] >= HEIGHT_TRIACCEL_MAX) + { + os::Printer::log("Too complex mesh to optimize, aborting."); + goto donehere; + } + + accel[v][cur[v]] = j; + cur[v]++; + } + free(cur); + + // Built, go + for (u32 i = 0; i < verts; i++) + { + const vector3df &mypos = vert[i].Pos; + + // find all edges of this vert + edges.clear(); + + bool gotonext = false; + u32 j; + u16 cur; + for (cur = 0; accel[i][cur] != USHRT_MAX && cur < HEIGHT_TRIACCEL_MAX; cur++) + { + j = accel[i][cur]; + + u32 far1 = -1, far2 = -1; + if (ind[j] == i) + { + far1 = ind[j+1]; + far2 = ind[j+2]; + } + else if (ind[j+1] == i) + { + far1 = ind[j]; + far2 = ind[j+2]; + } + else if (ind[j+2] == i) + { + far1 = ind[j]; + far2 = ind[j+1]; + } + + // Skip degenerate tris + if (vert[i].Pos == vert[far1].Pos || + vert[far1].Pos == vert[far2].Pos) + { +// puts("skipping degenerate tri"); + continue; + } + + // Edges found, check if we already added them + const u32 ecount = edges.size(); + bool far1new = true, far2new = true; + + for (u32 e = 0; e < ecount; e++) + { + if (edges[e].far == far1 || + edges[e].far == far2) + { + + // Skip if over 2 polys + if (edges[e].polycount > 2) + { + gotonext = true; + goto almostnext; + } + edges[e].polys[edges[e].polycount] = j; + edges[e].normal[edges[e].polycount] = + vert[i].Normal; + edges[e].polycount++; + + if (edges[e].far == far1) + far1new = false; + else + far2new = false; + } + } + + if (far1new) + { + // New edge + height_edge ed; + + ed.far = far1; + ed.polycount = 1; + ed.polys[0] = j; + ed.normal[0] = vert[i].Normal; + + edges.push_back(ed); + } + if (far2new) + { + // New edge + height_edge ed; + + ed.far = far2; + ed.polycount = 1; + ed.polys[0] = j; + ed.normal[0] = vert[i].Normal; + + edges.push_back(ed); + } + } + + almostnext: + if (gotonext) + continue; + + // Edges found. Possible to simplify? + + const u32 ecount = edges.size(); +// printf("Vert %u has %u edges\n", i, ecount); + for (u32 e = 0; e < ecount; e++) + { + for (u32 f = 0; f < ecount; f++) + { + if (f == e) continue; + + vector3df one = mypos - vert[edges[e].far].Pos; + vector3df two = vert[edges[f].far].Pos - mypos; + + one.normalize(); + two.normalize(); + + // Straight line ? + if (!one.equals(two, tolerance) || one.getLengthSQ() < 0.5f) + continue; + + // All other edges must have two polys + for (u32 g = 0; g < ecount; g++) + { + if (g == e || g == f) + continue; + + if (edges[g].polycount != 2) + { +// printf("%u: polycount not 2 (%u)\n", +// g, edges[g].polycount); + goto testnext; + } + + // Normals must match + if (!edges[g].normal[0].equals(edges[g].normal[1], + tolerance)) + { +// puts("Normals don't match"); + goto testnext; + } + + // Normals must not flip + for (u32 z = 0; z < edges[g].polycount; z++) + { + bool flat = false; + vector3df pos[3]; + pos[0] = + vert[ind[edges[g].polys[z]]].Pos; + pos[1] = + vert[ind[edges[g].polys[z] + 1]].Pos; + pos[2] = + vert[ind[edges[g].polys[z] + 2]].Pos; + + for (u32 y = 0; y < 3; y++) + { + if (edges[g].polys[z] + y == i) + { + pos[y] = vert[edges[e].far].Pos; + } + else if (edges[g].polys[z] + y + == edges[e].far) + { + flat = true; + break; + } + } + if (!flat) + { + triangle3df temp(pos[0], + pos[1], pos[2]); + vector3df N = temp.getNormal(); + N.normalize(); +// if (N.getLengthSQ() < 0.5f) +// puts("empty"); + + if (N != edges[g].normal[z]) + { +// puts("wouldflip"); + goto testnext; + } + } + } + + // Must not be on model edge + if (edges[g].polycount == 1) + { + goto testnext; + } + + } + + // Must not be on model edge + if (edges[e].polycount == 1) + { + goto testnext; + } + + // OK, moving to welding position + vert[i] = vert[edges[e].far]; +// printf("Contracted vert %u to %u\n", +// i, edges[e].far); + } + } + + + testnext:; + } + +donehere: + for (u32 i = 0; i < verts; i++) + { + free(accel[i]); + } + free(accel); +} + //! Creates a copy of the mesh, which will only consist of S3DVertex2TCoords vertices. // not yet 32bit diff --git a/source/Irrlicht/CMeshManipulator.h b/source/Irrlicht/CMeshManipulator.h index af51f62f..ce36b3d9 100644 --- a/source/Irrlicht/CMeshManipulator.h +++ b/source/Irrlicht/CMeshManipulator.h @@ -86,6 +86,12 @@ public: //! create a mesh optimized for the vertex cache virtual IMesh* createForsythOptimizedMesh(const scene::IMesh *mesh) const; + + //! Optimizes the mesh using an algorithm tuned for heightmaps + virtual void heightmapOptimizeMesh(IMesh * const m, const f32 tolerance = core::ROUNDING_ERROR_f32) const; + + //! Optimizes the mesh using an algorithm tuned for heightmaps + virtual void heightmapOptimizeMesh(IMeshBuffer * const m, const f32 tolerance = core::ROUNDING_ERROR_f32) const; }; } // end namespace scene diff --git a/source/Irrlicht/COBJMeshWriter.cpp b/source/Irrlicht/COBJMeshWriter.cpp index 1661448b..049a33b9 100644 --- a/source/Irrlicht/COBJMeshWriter.cpp +++ b/source/Irrlicht/COBJMeshWriter.cpp @@ -64,7 +64,7 @@ bool COBJMeshWriter::writeMesh(io::IWriteFile* file, scene::IMesh* mesh, s32 fla // write OBJ MESH header - core::stringc name; + io::path name; core::cutFilenameExtension(name,file->getFileName()) += ".mtl"; file->write("# exported by Irrlicht\n",23); file->write("mtllib ",7); diff --git a/source/Irrlicht/COpenGLDriver.cpp b/source/Irrlicht/COpenGLDriver.cpp index 1e695e80..0147bf95 100644 --- a/source/Irrlicht/COpenGLDriver.cpp +++ b/source/Irrlicht/COpenGLDriver.cpp @@ -34,6 +34,9 @@ namespace irr namespace video { +// Statics variables +const u16 COpenGLDriver::Quad2DIndices[4] = { 0, 1, 2, 3 }; + // ----------------------------------------------------------------------- // WINDOWS CONSTRUCTOR // ----------------------------------------------------------------------- @@ -756,22 +759,11 @@ bool COpenGLDriver::genericDriverInit() extGlProvokingVertex(GL_FIRST_VERTEX_CONVENTION_EXT); #endif - // Create built-in 2D quad and line for 2D rendering. - - Quad2DIndices[0] = 0; - Quad2DIndices[1] = 2; - Quad2DIndices[2] = 3; - Quad2DIndices[3] = 0; - Quad2DIndices[4] = 1; - Quad2DIndices[5] = 2; - - Line2DIndices[0] = 0; - Line2DIndices[1] = 1; - + // Create built-in 2D quad for 2D rendering (both quads and lines). Quad2DVertices[0] = S3DVertex(core::vector3df(-1.0f, 1.0f, 0.0f), core::vector3df(0.0f, 0.0f, 0.0f), SColor(255,255,255,255), core::vector2df(0.0f, 1.0f)); - Quad2DVertices[0] = S3DVertex(core::vector3df(1.0f, 1.0f, 0.0f), core::vector3df(0.0f, 0.0f, 0.0f), SColor(255,255,255,255), core::vector2df(1.0f, 1.0f)); - Quad2DVertices[0] = S3DVertex(core::vector3df(1.0f, -1.0f, 0.0f), core::vector3df(0.0f, 0.0f, 0.0f), SColor(255,255,255,255), core::vector2df(1.0f, 0.0f)); - Quad2DVertices[0] = S3DVertex(core::vector3df(-1.0f, -1.0f, 0.0f), core::vector3df(0.0f, 0.0f, 0.0f), SColor(255,255,255,255), core::vector2df(0.0f, 0.0f)); + Quad2DVertices[1] = S3DVertex(core::vector3df(1.0f, 1.0f, 0.0f), core::vector3df(0.0f, 0.0f, 0.0f), SColor(255,255,255,255), core::vector2df(1.0f, 1.0f)); + Quad2DVertices[2] = S3DVertex(core::vector3df(1.0f, -1.0f, 0.0f), core::vector3df(0.0f, 0.0f, 0.0f), SColor(255,255,255,255), core::vector2df(1.0f, 0.0f)); + Quad2DVertices[3] = S3DVertex(core::vector3df(-1.0f, -1.0f, 0.0f), core::vector3df(0.0f, 0.0f, 0.0f), SColor(255,255,255,255), core::vector2df(0.0f, 0.0f)); // create material renderers createMaterialRenderers(); @@ -2069,7 +2061,7 @@ void COpenGLDriver::draw2DImageBatch(const video::ITexture* texture, Quad2DVertices[2].TCoords = core::vector2df(tcoords.LowerRightCorner.X, tcoords.LowerRightCorner.Y); Quad2DVertices[3].TCoords = core::vector2df(tcoords.UpperLeftCorner.X, tcoords.LowerRightCorner.Y); - glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, Quad2DIndices); + glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_SHORT, Quad2DIndices); } } @@ -2223,7 +2215,7 @@ void COpenGLDriver::draw2DImage(const video::ITexture* texture, glColorPointer(colorSize, GL_UNSIGNED_BYTE, 0, &ColorBuffer[0]); } - glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, Quad2DIndices); + glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_SHORT, Quad2DIndices); } @@ -2308,7 +2300,7 @@ void COpenGLDriver::draw2DImage(const video::ITexture* texture, const core::rect glColorPointer(colorSize, GL_UNSIGNED_BYTE, 0, &ColorBuffer[0]); } - glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, Quad2DIndices); + glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_SHORT, Quad2DIndices); if (clipRect) glDisable(GL_SCISSOR_TEST); @@ -2401,7 +2393,7 @@ void COpenGLDriver::draw2DImage(const video::ITexture* texture, Quad2DVertices[2].TCoords = core::vector2df(tcoords.LowerRightCorner.X, tcoords.LowerRightCorner.Y); Quad2DVertices[3].TCoords = core::vector2df(tcoords.UpperLeftCorner.X, tcoords.LowerRightCorner.Y); - glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, Quad2DIndices); + glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_SHORT, Quad2DIndices); targetPos.X += sourceRects[currentIndex].getWidth(); } @@ -2482,7 +2474,7 @@ void COpenGLDriver::draw2DRectangle(const core::rect& position, glColorPointer(colorSize, GL_UNSIGNED_BYTE, 0, &ColorBuffer[0]); } - glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, Quad2DIndices); + glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_SHORT, Quad2DIndices); } @@ -2523,7 +2515,7 @@ void COpenGLDriver::draw2DLine(const core::position2d& start, glColorPointer(colorSize, GL_UNSIGNED_BYTE, 0, &ColorBuffer[0]); } - glDrawElements(GL_LINES, 2, GL_UNSIGNED_SHORT, Line2DIndices); + glDrawElements(GL_LINES, 2, GL_UNSIGNED_SHORT, Quad2DIndices); } } @@ -3984,7 +3976,7 @@ void COpenGLDriver::drawStencilShadow(bool clearStencilBuffer, video::SColor lef glColorPointer(colorSize, GL_UNSIGNED_BYTE, 0, &ColorBuffer[0]); } - glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, Quad2DIndices); + glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_SHORT, Quad2DIndices); clearBuffers(false, false, clearStencilBuffer, 0x0); @@ -4069,7 +4061,7 @@ void COpenGLDriver::draw3DLine(const core::vector3df& start, glColorPointer(colorSize, GL_UNSIGNED_BYTE, 0, &ColorBuffer[0]); } - glDrawElements(GL_LINES, 2, GL_UNSIGNED_SHORT, Line2DIndices); + glDrawElements(GL_LINES, 2, GL_UNSIGNED_SHORT, Quad2DIndices); } diff --git a/source/Irrlicht/COpenGLDriver.h b/source/Irrlicht/COpenGLDriver.h index 9a2ff72b..1d96ae2f 100644 --- a/source/Irrlicht/COpenGLDriver.h +++ b/source/Irrlicht/COpenGLDriver.h @@ -602,8 +602,7 @@ namespace video //! Built-in 2D quad for 2D rendering. S3DVertex Quad2DVertices[4]; - u16 Quad2DIndices[6]; - u16 Line2DIndices[2]; + static const u16 Quad2DIndices[4]; #ifdef _IRR_WINDOWS_API_ HDC HDc; // Private GDI Device Context diff --git a/source/Irrlicht/CSceneNodeAnimatorDelete.cpp b/source/Irrlicht/CSceneNodeAnimatorDelete.cpp index 3a519de8..0befba9c 100644 --- a/source/Irrlicht/CSceneNodeAnimatorDelete.cpp +++ b/source/Irrlicht/CSceneNodeAnimatorDelete.cpp @@ -39,8 +39,11 @@ void CSceneNodeAnimatorDelete::animateNode(ISceneNode* node, u32 timeMs) ISceneNodeAnimator* CSceneNodeAnimatorDelete::createClone(ISceneNode* node, ISceneManager* newManager) { - CSceneNodeAnimatorDelete * newAnimator = - new CSceneNodeAnimatorDelete(SceneManager, FinishTime); + if (!newManager) + newManager = SceneManager; + + CSceneNodeAnimatorDelete* newAnimator = + new CSceneNodeAnimatorDelete(newManager, FinishTime); return newAnimator; } diff --git a/source/Irrlicht/CXMeshFileLoader.cpp b/source/Irrlicht/CXMeshFileLoader.cpp index 6ea9a48a..40ff4b77 100644 --- a/source/Irrlicht/CXMeshFileLoader.cpp +++ b/source/Irrlicht/CXMeshFileLoader.cpp @@ -2269,6 +2269,8 @@ void CXMeshFileLoader::readUntilEndOfLine() u16 CXMeshFileLoader::readBinWord() { + if (P>=End) + return 0; #ifdef __BIG_ENDIAN__ const u16 tmp = os::Byteswap::byteswap(*(u16 *)P); #else @@ -2281,6 +2283,8 @@ u16 CXMeshFileLoader::readBinWord() u32 CXMeshFileLoader::readBinDWord() { + if (P>=End) + return 0; #ifdef __BIG_ENDIAN__ const u32 tmp = os::Byteswap::byteswap(*(u32 *)P); #else