2019-12-12 17:32:41 +01:00
// Copyright (C) 2007-2012 Christian Stehno
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h
# include "IrrCompileConfig.h"
# ifdef _IRR_COMPILE_WITH_STL_LOADER_
# include "CSTLMeshFileLoader.h"
# include "SMesh.h"
2022-04-19 23:32:11 +02:00
# include "CDynamicMeshBuffer.h"
2022-04-21 00:09:03 +02:00
# include "CMemoryFile.h"
2019-12-12 17:32:41 +01:00
# include "SAnimatedMesh.h"
# include "IReadFile.h"
# include "fast_atof.h"
# include "coreutil.h"
# include "os.h"
namespace irr
{
namespace scene
{
//! returns true if the file maybe is able to be loaded by this class
//! based on the file extension (e.g. ".bsp")
bool CSTLMeshFileLoader : : isALoadableFileExtension ( const io : : path & filename ) const
{
return core : : hasFileExtension ( filename , " stl " ) ;
}
//! creates/loads an animated mesh from the file.
//! \return Pointer to the created mesh. Returns 0 if loading failed.
//! If you no longer need the mesh, you should call IAnimatedMesh::drop().
//! See IReferenceCounted::drop() for more information.
2022-04-21 00:09:03 +02:00
IAnimatedMesh * CSTLMeshFileLoader : : createMesh ( io : : IReadFile * fileIn )
2019-12-12 17:32:41 +01:00
{
2022-04-21 00:09:03 +02:00
const long filesize = fileIn - > getSize ( ) ;
2019-12-12 17:32:41 +01:00
if ( filesize < 6 ) // we need a header
return 0 ;
2022-04-21 00:09:03 +02:00
// 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 ;
2019-12-12 17:32:41 +01:00
SMesh * mesh = new SMesh ( ) ;
2022-04-19 23:32:11 +02:00
CDynamicMeshBuffer * meshBuffer = new CDynamicMeshBuffer ( video : : EVT_STANDARD , video : : EIT_16BIT ) ;
IVertexBuffer & vertBuffer = meshBuffer - > getVertexBuffer ( ) ;
2019-12-12 17:32:41 +01:00
mesh - > addMeshBuffer ( meshBuffer ) ;
meshBuffer - > drop ( ) ;
core : : vector3df vertex [ 3 ] ;
core : : vector3df normal ;
bool binary = false ;
2022-04-21 00:09:03 +02:00
if ( getNextToken ( file ) ! = " solid " )
2019-12-12 17:32:41 +01:00
binary = true ;
// read/skip header
u32 binFaceCount = 0 ;
if ( binary )
{
file - > seek ( 80 ) ;
file - > read ( & binFaceCount , 4 ) ;
# ifdef __BIG_ENDIAN__
binFaceCount = os : : Byteswap : : byteswap ( binFaceCount ) ;
# endif
}
else
goNextLine ( file ) ;
u16 attrib = 0 ;
2022-04-21 00:09:03 +02:00
Token . reserve ( 32 ) ;
bool failure = false ;
2019-12-12 17:32:41 +01:00
while ( file - > getPos ( ) < filesize )
{
if ( ! binary )
{
2022-04-21 00:09:03 +02:00
if ( getNextToken ( file ) ! = " facet " )
2019-12-12 17:32:41 +01:00
{
2022-04-21 00:09:03 +02:00
if ( Token ! = " endsolid " )
failure = true ;
break ;
2019-12-12 17:32:41 +01:00
}
2022-04-21 00:09:03 +02:00
if ( getNextToken ( file ) ! = " normal " )
2019-12-12 17:32:41 +01:00
{
2022-04-21 00:09:03 +02:00
failure = true ;
break ;
2019-12-12 17:32:41 +01:00
}
}
getNextVector ( file , normal , binary ) ;
if ( ! binary )
{
2022-04-21 00:09:03 +02:00
if ( getNextToken ( file ) ! = " outer " )
2019-12-12 17:32:41 +01:00
{
2022-04-21 00:09:03 +02:00
failure = true ;
break ;
2019-12-12 17:32:41 +01:00
}
2022-04-21 00:09:03 +02:00
if ( getNextToken ( file ) ! = " loop " )
2019-12-12 17:32:41 +01:00
{
2022-04-21 00:09:03 +02:00
failure = true ;
break ;
2019-12-12 17:32:41 +01:00
}
}
for ( u32 i = 0 ; i < 3 ; + + i )
{
if ( ! binary )
{
2022-04-21 00:09:03 +02:00
if ( getNextToken ( file ) ! = " vertex " )
2019-12-12 17:32:41 +01:00
{
2022-04-21 00:09:03 +02:00
failure = true ;
break ;
2019-12-12 17:32:41 +01:00
}
}
getNextVector ( file , vertex [ i ] , binary ) ;
}
2022-04-21 00:09:03 +02:00
if ( failure )
break ;
2019-12-12 17:32:41 +01:00
if ( ! binary )
{
2022-04-21 00:09:03 +02:00
if ( getNextToken ( file ) ! = " endloop " )
2019-12-12 17:32:41 +01:00
{
2022-04-21 00:09:03 +02:00
failure = true ;
break ;
2019-12-12 17:32:41 +01:00
}
2022-04-21 00:09:03 +02:00
if ( getNextToken ( file ) ! = " endfacet " )
2019-12-12 17:32:41 +01:00
{
2022-04-21 00:09:03 +02:00
failure = true ;
break ;
2019-12-12 17:32:41 +01:00
}
}
else
{
file - > read ( & attrib , 2 ) ;
# ifdef __BIG_ENDIAN__
attrib = os : : Byteswap : : byteswap ( attrib ) ;
# endif
}
video : : SColor color ( 0xffffffff ) ;
if ( attrib & 0x8000 )
color = video : : A1R5G5B5toA8R8G8B8 ( attrib ) ;
if ( normal = = core : : vector3df ( ) )
normal = core : : plane3df ( vertex [ 2 ] , vertex [ 1 ] , vertex [ 0 ] ) . Normal ;
2022-04-19 23:32:11 +02:00
vertBuffer . push_back ( video : : S3DVertex ( vertex [ 2 ] , normal , color , core : : vector2df ( ) ) ) ;
vertBuffer . push_back ( video : : S3DVertex ( vertex [ 1 ] , normal , color , core : : vector2df ( ) ) ) ;
vertBuffer . push_back ( video : : S3DVertex ( vertex [ 0 ] , normal , color , core : : vector2df ( ) ) ) ;
2019-12-12 17:32:41 +01:00
} // end while (file->getPos() < filesize)
// Create the Animated mesh if there's anything in the mesh
SAnimatedMesh * pAM = 0 ;
2022-04-21 00:09:03 +02:00
if ( ! failure & & mesh - > getMeshBufferCount ( ) > 0 )
2019-12-12 17:32:41 +01:00
{
2022-04-19 23:32:11 +02:00
IIndexBuffer & indexBuffer = meshBuffer - > getIndexBuffer ( ) ;
u32 vertCount = vertBuffer . size ( ) ;
if ( vertCount > 65535 ) // Note 65535 instead of 65536 as it divides by 3
{
if ( getIndexTypeHint ( ) ! = EITH_16BIT )
indexBuffer . setType ( video : : EIT_32BIT ) ;
else
{
// Could split buffer, but probably no one really needs this anymore now with 32-bit support and necessary buffer manipulation functions are not there yet
vertCount = 65535 ;
}
}
indexBuffer . reallocate ( vertCount ) ;
for ( u32 i = 0 ; i < vertCount ; + + i ) //every vertex is unique, so we can just generate the indices
indexBuffer . push_back ( i ) ;
meshBuffer - > recalculateBoundingBox ( ) ;
2019-12-12 17:32:41 +01:00
mesh - > recalculateBoundingBox ( ) ;
pAM = new SAnimatedMesh ( ) ;
2022-04-19 23:32:11 +02:00
pAM - > Type = EAMT_STATIC ;
2019-12-12 17:32:41 +01:00
pAM - > addMesh ( mesh ) ;
pAM - > recalculateBoundingBox ( ) ;
}
mesh - > drop ( ) ;
2022-04-21 00:09:03 +02:00
Token . clear ( ) ;
if ( memoryFile )
memoryFile - > drop ( ) ;
2019-12-12 17:32:41 +01:00
return pAM ;
}
//! Read 3d vector of floats
2022-04-21 00:09:03 +02:00
void CSTLMeshFileLoader : : getNextVector ( io : : IReadFile * file , core : : vector3df & vec , bool binary )
2019-12-12 17:32:41 +01:00
{
if ( binary )
{
file - > read ( & vec . X , 4 ) ;
file - > read ( & vec . Y , 4 ) ;
file - > read ( & vec . Z , 4 ) ;
# ifdef __BIG_ENDIAN__
vec . X = os : : Byteswap : : byteswap ( vec . X ) ;
vec . Y = os : : Byteswap : : byteswap ( vec . Y ) ;
vec . Z = os : : Byteswap : : byteswap ( vec . Z ) ;
# endif
}
else
{
goNextWord ( file ) ;
2022-04-21 00:09:03 +02:00
getNextToken ( file ) ;
core : : fast_atof_move ( Token . c_str ( ) , vec . X ) ;
getNextToken ( file ) ;
core : : fast_atof_move ( Token . c_str ( ) , vec . Y ) ;
getNextToken ( file ) ;
core : : fast_atof_move ( Token . c_str ( ) , vec . Z ) ;
2019-12-12 17:32:41 +01:00
}
vec . X = - vec . X ;
}
//! Read next word
2022-04-21 00:09:03 +02:00
const core : : stringc & CSTLMeshFileLoader : : getNextToken ( io : : IReadFile * file )
2019-12-12 17:32:41 +01:00
{
goNextWord ( file ) ;
u8 c ;
2022-04-21 00:09:03 +02:00
Token = " " ;
2019-12-12 17:32:41 +01:00
while ( file - > getPos ( ) ! = file - > getSize ( ) )
{
file - > read ( & c , 1 ) ;
// found it, so leave
if ( core : : isspace ( c ) )
break ;
2022-04-21 00:09:03 +02:00
Token . append ( c ) ;
2019-12-12 17:32:41 +01:00
}
2022-04-21 00:09:03 +02:00
return Token ;
2019-12-12 17:32:41 +01:00
}
//! skip to next word
void CSTLMeshFileLoader : : goNextWord ( io : : IReadFile * file ) const
{
u8 c ;
while ( file - > getPos ( ) ! = file - > getSize ( ) )
{
file - > read ( & c , 1 ) ;
// found it, so leave
if ( ! core : : isspace ( c ) )
{
file - > seek ( - 1 , true ) ;
break ;
}
}
}
//! Read until line break is reached and stop at the next non-space character
void CSTLMeshFileLoader : : goNextLine ( io : : IReadFile * file ) const
{
u8 c ;
// look for newline characters
while ( file - > getPos ( ) ! = file - > getSize ( ) )
{
file - > read ( & c , 1 ) ;
// found it, so leave
if ( c = = ' \n ' | | c = = ' \r ' )
break ;
}
}
} // end namespace scene
} // end namespace irr
# endif // _IRR_COMPILE_WITH_STL_LOADER_