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
master
hybrid 2013-06-04 14:47:05 +00:00
parent a4b52c5c8a
commit 2ec010d0a5
8 changed files with 394 additions and 34 deletions

View File

@ -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.

View File

@ -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
// Clean up any degenerate tris
core::array<u16> &Indices = *outIdx;
Indices.set_used(indexCount);
for (u32 i=0; i<indexCount; ++i)
Indices.clear();
Indices.reallocate(indexCount);
for (u32 i = 0; i < indexCount; i+=3)
{
Indices[i] = redirects[ indices[i] ];
u16 a, b, c;
a = redirects[indices[i]];
b = redirects[indices[i+1]];
c = redirects[indices[i+2]];
bool drop = false;
if (a == b || b == c || a == c)
drop = true;
// Open for other checks
if (!drop)
{
Indices.push_back(a);
Indices.push_back(b);
Indices.push_back(c);
}
}
}
return clone;
@ -1017,6 +1035,318 @@ IMesh* CMeshManipulator::createMeshWithTangents(IMesh* mesh, bool recalculateNor
return clone;
}
namespace
{
struct height_edge
{
u32 far;
u32 polycount;
u32 polys[2];
core::vector3df normal[2];
};
enum
{
HEIGHT_TRIACCEL_MAX = 1024
};
}
//! Optimizes the mesh using an algorithm tuned for heightmaps.
void CMeshManipulator::heightmapOptimizeMesh(IMesh * const m, const f32 tolerance) const
{
const u32 max = m->getMeshBufferCount();
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<height_edge> 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

View File

@ -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

View File

@ -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);

View File

@ -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<s32>& 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<s32>& 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);
}

View File

@ -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

View File

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

View File

@ -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