Speed up stl format loading, especially with text format.

Loading whole file now in memory (unless it's already a memory file).
And avoiding lots of memory allocations otherwise by buffering token string in class object.
Was a bit unusable before for large files (several minute loading times now down to a second)-

git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@6349 dfc29bdd-3216-0410-991c-e03cc46cb475
This commit is contained in:
cutealien 2022-04-20 22:09:03 +00:00
parent 9504b3da21
commit 439667b369
3 changed files with 66 additions and 43 deletions

@ -1,5 +1,7 @@
-------------------------- --------------------------
Changes in 1.9 (not yet released) Changes in 1.9 (not yet released)
- stl meshloader now faster, especially with text format
- CMemoryReadFile::seek no longer allowed to go _before_ start.
- stl meshloader can now load 32 bit buffers. - stl meshloader can now load 32 bit buffers.
Thanks @Foaly for the patch (https://irrlicht.sourceforge.io/forum/viewtopic.php?f=9&t=51441) Thanks @Foaly for the patch (https://irrlicht.sourceforge.io/forum/viewtopic.php?f=9&t=51441)
- Add IMeshBufffer::clone function to create buffer copies. CMeshManipulator::createMeshCopy uses that now and works now with all types of meshbuffers. - Add IMeshBufffer::clone function to create buffer copies. CMeshManipulator::createMeshCopy uses that now and works now with all types of meshbuffers.

@ -9,6 +9,7 @@
#include "CSTLMeshFileLoader.h" #include "CSTLMeshFileLoader.h"
#include "SMesh.h" #include "SMesh.h"
#include "CDynamicMeshBuffer.h" #include "CDynamicMeshBuffer.h"
#include "CMemoryFile.h"
#include "SAnimatedMesh.h" #include "SAnimatedMesh.h"
#include "IReadFile.h" #include "IReadFile.h"
#include "fast_atof.h" #include "fast_atof.h"
@ -34,12 +35,26 @@ bool CSTLMeshFileLoader::isALoadableFileExtension(const io::path& filename) cons
//! \return Pointer to the created mesh. Returns 0 if loading failed. //! \return Pointer to the created mesh. Returns 0 if loading failed.
//! If you no longer need the mesh, you should call IAnimatedMesh::drop(). //! If you no longer need the mesh, you should call IAnimatedMesh::drop().
//! See IReferenceCounted::drop() for more information. //! See IReferenceCounted::drop() for more information.
IAnimatedMesh* CSTLMeshFileLoader::createMesh(io::IReadFile* file) IAnimatedMesh* CSTLMeshFileLoader::createMesh(io::IReadFile* fileIn)
{ {
const long filesize = file->getSize(); const long filesize = fileIn->getSize();
if (filesize < 6) // we need a header if (filesize < 6) // we need a header
return 0; return 0;
// We copy the whole file into a memory-read file if it isn't already one.
io::CMemoryReadFile * memoryFile = 0;
if ( fileIn->getType() != io::ERFT_MEMORY_READ_FILE )
{
u8* fileBuffer = new u8[filesize];
if ( fileIn->read(fileBuffer, filesize) != filesize )
{
delete[] fileBuffer;
return 0;
}
memoryFile = new io::CMemoryReadFile(fileBuffer, filesize, io::path(""), true); // takes over fileBuffer
}
io::IReadFile* file = memoryFile ? memoryFile : fileIn;
SMesh* mesh = new SMesh(); SMesh* mesh = new SMesh();
CDynamicMeshBuffer* meshBuffer = new CDynamicMeshBuffer(video::EVT_STANDARD, video::EIT_16BIT); CDynamicMeshBuffer* meshBuffer = new CDynamicMeshBuffer(video::EVT_STANDARD, video::EIT_16BIT);
IVertexBuffer& vertBuffer = meshBuffer->getVertexBuffer(); IVertexBuffer& vertBuffer = meshBuffer->getVertexBuffer();
@ -50,8 +65,7 @@ IAnimatedMesh* CSTLMeshFileLoader::createMesh(io::IReadFile* file)
core::vector3df normal; core::vector3df normal;
bool binary = false; bool binary = false;
core::stringc token; if (getNextToken(file) != "solid")
if (getNextToken(file, token) != "solid")
binary = true; binary = true;
// read/skip header // read/skip header
u32 binFaceCount = 0; u32 binFaceCount = 0;
@ -67,62 +81,64 @@ IAnimatedMesh* CSTLMeshFileLoader::createMesh(io::IReadFile* file)
goNextLine(file); goNextLine(file);
u16 attrib=0; u16 attrib=0;
token.reserve(32); Token.reserve(32);
bool failure = false;
while (file->getPos() < filesize) while (file->getPos() < filesize)
{ {
if (!binary) if (!binary)
{ {
if (getNextToken(file, token) != "facet") if (getNextToken(file) != "facet")
{ {
if (token=="endsolid") if (Token!="endsolid")
break; failure = true;
mesh->drop(); break;
return 0;
} }
if (getNextToken(file, token) != "normal") if (getNextToken(file) != "normal")
{ {
mesh->drop(); failure = true;
return 0; break;
} }
} }
getNextVector(file, normal, binary); getNextVector(file, normal, binary);
if (!binary) if (!binary)
{ {
if (getNextToken(file, token) != "outer") if (getNextToken(file) != "outer")
{ {
mesh->drop(); failure = true;
return 0; break;
} }
if (getNextToken(file, token) != "loop") if (getNextToken(file) != "loop")
{ {
mesh->drop(); failure = true;
return 0; break;
} }
} }
for (u32 i=0; i<3; ++i) for (u32 i=0; i<3; ++i)
{ {
if (!binary) if (!binary)
{ {
if (getNextToken(file, token) != "vertex") if (getNextToken(file) != "vertex")
{ {
mesh->drop(); failure = true;
return 0; break;
} }
} }
getNextVector(file, vertex[i], binary); getNextVector(file, vertex[i], binary);
} }
if ( failure )
break;
if (!binary) if (!binary)
{ {
if (getNextToken(file, token) != "endloop") if (getNextToken(file) != "endloop")
{ {
mesh->drop(); failure = true;
return 0; break;
} }
if (getNextToken(file, token) != "endfacet") if (getNextToken(file) != "endfacet")
{ {
mesh->drop(); failure = true;
return 0; break;
} }
} }
else else
@ -145,7 +161,7 @@ IAnimatedMesh* CSTLMeshFileLoader::createMesh(io::IReadFile* file)
// Create the Animated mesh if there's anything in the mesh // Create the Animated mesh if there's anything in the mesh
SAnimatedMesh* pAM = 0; SAnimatedMesh* pAM = 0;
if ( 0 != mesh->getMeshBufferCount() ) if ( !failure && mesh->getMeshBufferCount() > 0 )
{ {
IIndexBuffer& indexBuffer = meshBuffer->getIndexBuffer(); IIndexBuffer& indexBuffer = meshBuffer->getIndexBuffer();
u32 vertCount = vertBuffer.size(); u32 vertCount = vertBuffer.size();
@ -173,13 +189,16 @@ IAnimatedMesh* CSTLMeshFileLoader::createMesh(io::IReadFile* file)
} }
mesh->drop(); mesh->drop();
Token.clear();
if ( memoryFile )
memoryFile->drop();
return pAM; return pAM;
} }
//! Read 3d vector of floats //! Read 3d vector of floats
void CSTLMeshFileLoader::getNextVector(io::IReadFile* file, core::vector3df& vec, bool binary) const void CSTLMeshFileLoader::getNextVector(io::IReadFile* file, core::vector3df& vec, bool binary)
{ {
if (binary) if (binary)
{ {
@ -195,34 +214,33 @@ void CSTLMeshFileLoader::getNextVector(io::IReadFile* file, core::vector3df& vec
else else
{ {
goNextWord(file); goNextWord(file);
core::stringc tmp;
getNextToken(file, tmp); getNextToken(file);
core::fast_atof_move(tmp.c_str(), vec.X); core::fast_atof_move(Token.c_str(), vec.X);
getNextToken(file, tmp); getNextToken(file);
core::fast_atof_move(tmp.c_str(), vec.Y); core::fast_atof_move(Token.c_str(), vec.Y);
getNextToken(file, tmp); getNextToken(file);
core::fast_atof_move(tmp.c_str(), vec.Z); core::fast_atof_move(Token.c_str(), vec.Z);
} }
vec.X=-vec.X; vec.X=-vec.X;
} }
//! Read next word //! Read next word
const core::stringc& CSTLMeshFileLoader::getNextToken(io::IReadFile* file, core::stringc& token) const const core::stringc& CSTLMeshFileLoader::getNextToken(io::IReadFile* file)
{ {
goNextWord(file); goNextWord(file);
u8 c; u8 c;
token = ""; Token = "";
while(file->getPos() != file->getSize()) while(file->getPos() != file->getSize())
{ {
file->read(&c, 1); file->read(&c, 1);
// found it, so leave // found it, so leave
if (core::isspace(c)) if (core::isspace(c))
break; break;
token.append(c); Token.append(c);
} }
return token; return Token;
} }

@ -34,12 +34,15 @@ private:
// skips to the first non-space character available // skips to the first non-space character available
void goNextWord(io::IReadFile* file) const; void goNextWord(io::IReadFile* file) const;
// returns the next word // returns the next word
const core::stringc& getNextToken(io::IReadFile* file, core::stringc& token) const; const core::stringc& getNextToken(io::IReadFile* file);
// skip to next printable character after the first line break // skip to next printable character after the first line break
void goNextLine(io::IReadFile* file) const; void goNextLine(io::IReadFile* file) const;
//! Read 3d vector of floats //! Read 3d vector of floats
void getNextVector(io::IReadFile* file, core::vector3df& vec, bool binary) const; void getNextVector(io::IReadFile* file, core::vector3df& vec, bool binary);
//! Buffering last read token to avoid reallocating string all the time
core::stringc Token;
}; };
} // end namespace scene } // end namespace scene