Fix Meshbuffer interface of md3 files. At least for some situations these should return the proper buffers now, and hence allow for stencil shadows and other things. We might need an animation update check, though, to handle multiple meshes.

git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@3968 dfc29bdd-3216-0410-991c-e03cc46cb475
master
hybrid 2011-10-24 10:44:01 +00:00
parent 594ed8174b
commit 688ec5b873
2 changed files with 153 additions and 135 deletions

View File

@ -70,7 +70,7 @@ CAnimatedMeshMD3::CAnimatedMeshMD3()
#endif
Mesh = new SMD3Mesh();
MeshIPol = new SMesh();
setInterpolationShift(0, 0);
}
@ -80,6 +80,8 @@ CAnimatedMeshMD3::~CAnimatedMeshMD3()
{
if (Mesh)
Mesh->drop();
if (MeshIPol)
MeshIPol->drop();
}
@ -91,32 +93,66 @@ u32 CAnimatedMeshMD3::getFrameCount() const
//! Rendering Hint
void CAnimatedMeshMD3::setInterpolationShift ( u32 shift, u32 loopMode )
void CAnimatedMeshMD3::setInterpolationShift(u32 shift, u32 loopMode)
{
IPolShift = shift;
LoopMode = loopMode;
}
//! returns amount of mesh buffers.
u32 CAnimatedMeshMD3::getMeshBufferCount() const
{
return MeshIPol->getMeshBufferCount();
}
//! returns pointer to a mesh buffer
IMeshBuffer* CAnimatedMeshMD3::getMeshBuffer(u32 nr) const
{
return MeshIPol->getMeshBuffer(nr);
}
//! Returns pointer to a mesh buffer which fits a material
IMeshBuffer* CAnimatedMeshMD3::getMeshBuffer(const video::SMaterial &material) const
{
return MeshIPol->getMeshBuffer(material);
}
void CAnimatedMeshMD3::setMaterialFlag(video::E_MATERIAL_FLAG flag, bool newvalue)
{
MeshIPol->setMaterialFlag(flag, newvalue);
}
//! set the hardware mapping hint, for driver
void CAnimatedMeshMD3::setHardwareMappingHint(E_HARDWARE_MAPPING newMappingHint,
E_BUFFER_TYPE buffer)
{
MeshIPol.setHardwareMappingHint(newMappingHint, buffer);
MeshIPol->setHardwareMappingHint(newMappingHint, buffer);
}
//! flags the meshbuffer as changed, reloads hardware buffers
void CAnimatedMeshMD3::setDirty(E_BUFFER_TYPE buffer)
{
MeshIPol.setDirty(buffer);
MeshIPol->setDirty(buffer);
}
//! set user axis aligned bounding box
void CAnimatedMeshMD3::setBoundingBox(const core::aabbox3df& box)
{
MeshIPol->setBoundingBox(box);
}
//! Returns the animated tag list based on a detail level. 0 is the lowest, 255 the highest detail.
SMD3QuaternionTagList *CAnimatedMeshMD3::getTagList(s32 frame, s32 detailLevel, s32 startFrameLoop, s32 endFrameLoop)
{
if ( 0 == Mesh )
if (0 == Mesh)
return 0;
getMesh(frame, detailLevel, startFrameLoop, endFrameLoop);
@ -127,16 +163,16 @@ SMD3QuaternionTagList *CAnimatedMeshMD3::getTagList(s32 frame, s32 detailLevel,
//! Returns the animated mesh based on a detail level. 0 is the lowest, 255 the highest detail.
IMesh* CAnimatedMeshMD3::getMesh(s32 frame, s32 detailLevel, s32 startFrameLoop, s32 endFrameLoop)
{
if ( 0 == Mesh )
if (0 == Mesh)
return 0;
//! check if we have the mesh in our private cache
SCacheInfo candidate ( frame, startFrameLoop, endFrameLoop );
if ( candidate == Current )
return &MeshIPol;
SCacheInfo candidate(frame, startFrameLoop, endFrameLoop);
if (candidate == Current)
return MeshIPol;
startFrameLoop = core::s32_max ( 0, startFrameLoop >> IPolShift );
endFrameLoop = core::if_c_a_else_b ( endFrameLoop < 0, Mesh->MD3Header.numFrames - 1, endFrameLoop >> IPolShift );
startFrameLoop = core::s32_max(0, startFrameLoop >> IPolShift);
endFrameLoop = core::if_c_a_else_b(endFrameLoop < 0, Mesh->MD3Header.numFrames - 1, endFrameLoop >> IPolShift);
const u32 mask = 1 << IPolShift;
@ -144,46 +180,46 @@ IMesh* CAnimatedMeshMD3::getMesh(s32 frame, s32 detailLevel, s32 startFrameLoop,
s32 frameB;
f32 iPol;
if ( LoopMode )
if (LoopMode)
{
// correct frame to "pixel center"
frame -= mask >> 1;
// interpolation
iPol = f32(frame & ( mask - 1 )) * core::reciprocal ( f32(mask) );
iPol = f32(frame & (mask - 1)) * core::reciprocal(f32(mask));
// wrap anim
frame >>= IPolShift;
frameA = core::if_c_a_else_b ( frame < startFrameLoop, endFrameLoop, frame );
frameB = core::if_c_a_else_b ( frameA + 1 > endFrameLoop, startFrameLoop, frameA + 1 );
frameA = core::if_c_a_else_b(frame < startFrameLoop, endFrameLoop, frame);
frameB = core::if_c_a_else_b(frameA + 1 > endFrameLoop, startFrameLoop, frameA + 1);
}
else
{
// correct frame to "pixel center"
frame -= mask >> 1;
iPol = f32(frame & ( mask - 1 )) * core::reciprocal ( f32(mask) );
iPol = f32(frame & (mask - 1)) * core::reciprocal(f32(mask));
// clamp anim
frame >>= IPolShift;
frameA = core::s32_clamp ( frame, startFrameLoop, endFrameLoop );
frameB = core::s32_min ( frameA + 1, endFrameLoop );
frameA = core::s32_clamp(frame, startFrameLoop, endFrameLoop);
frameB = core::s32_min(frameA + 1, endFrameLoop);
}
// build current vertex
for (u32 i = 0; i!= Mesh->Buffer.size (); ++i)
for (u32 i = 0; i!= Mesh->Buffer.size(); ++i)
{
buildVertexArray(frameA, frameB, iPol,
Mesh->Buffer[i],
(SMeshBufferLightMap*) MeshIPol.getMeshBuffer(i));
(SMeshBufferLightMap*) MeshIPol->getMeshBuffer(i));
}
MeshIPol.recalculateBoundingBox();
MeshIPol->recalculateBoundingBox();
// build current tags
buildTagArray( frameA, frameB, iPol );
buildTagArray(frameA, frameB, iPol);
Current = candidate;
return &MeshIPol;
return MeshIPol;
}
@ -192,13 +228,13 @@ IMeshBuffer * CAnimatedMeshMD3::createMeshBuffer(const SMD3MeshBuffer* source,
io::IFileSystem* fs, video::IVideoDriver * driver)
{
SMeshBufferLightMap * dest = new SMeshBufferLightMap();
dest->Vertices.set_used( source->MeshHeader.numVertices );
dest->Indices.set_used( source->Indices.size () );
dest->Vertices.set_used(source->MeshHeader.numVertices);
dest->Indices.set_used(source->Indices.size());
u32 i;
// fill in static face info
for ( i = 0; i < source->Indices.size(); i += 3 )
for (i = 0; i < source->Indices.size(); i += 3)
{
dest->Indices[i + 0] = (u16) source->Indices[i + 0];
dest->Indices[i + 1] = (u16) source->Indices[i + 1];
@ -206,7 +242,7 @@ IMeshBuffer * CAnimatedMeshMD3::createMeshBuffer(const SMD3MeshBuffer* source,
}
// fill in static vertex info
for ( i = 0; i!= (u32)source->MeshHeader.numVertices; ++i )
for (i = 0; i!= (u32)source->MeshHeader.numVertices; ++i)
{
video::S3DVertex2TCoords &v = dest->Vertices[i];
v.Color = 0xFFFFFFFF;
@ -219,9 +255,9 @@ IMeshBuffer * CAnimatedMeshMD3::createMeshBuffer(const SMD3MeshBuffer* source,
// load static texture
u32 pos = 0;
quake3::tTexArray textureArray;
quake3::getTextures( textureArray, source->Shader, pos, fs, driver );
quake3::getTextures(textureArray, source->Shader, pos, fs, driver);
dest->Material.MaterialType = video::EMT_SOLID;
dest->Material.setTexture ( 0, textureArray[0] );
dest->Material.setTexture(0, textureArray[0]);
dest->Material.Lighting = false;
return dest;
@ -229,14 +265,13 @@ IMeshBuffer * CAnimatedMeshMD3::createMeshBuffer(const SMD3MeshBuffer* source,
//! build final mesh's vertices from frames frameA and frameB with linear interpolation.
void CAnimatedMeshMD3::buildVertexArray ( u32 frameA, u32 frameB, f32 interpolate,
const SMD3MeshBuffer * source,
SMeshBufferLightMap * dest
)
void CAnimatedMeshMD3::buildVertexArray(u32 frameA, u32 frameB, f32 interpolate,
const SMD3MeshBuffer* source,
SMeshBufferLightMap* dest)
{
const u32 frameOffsetA = frameA * source->MeshHeader.numVertices;
const u32 frameOffsetB = frameB * source->MeshHeader.numVertices;
const f32 scale = ( 1.f/ 64.f );
const f32 scale = (1.f/ 64.f);
for (s32 i = 0; i != source->MeshHeader.numVertices; ++i)
{
@ -246,30 +281,30 @@ void CAnimatedMeshMD3::buildVertexArray ( u32 frameA, u32 frameB, f32 interpolat
const SMD3Vertex &vB = source->Vertices [ frameOffsetB + i ];
// position
v.Pos.X = scale * ( vA.position[0] + interpolate * ( vB.position[0] - vA.position[0] ) );
v.Pos.Y = scale * ( vA.position[2] + interpolate * ( vB.position[2] - vA.position[2] ) );
v.Pos.Z = scale * ( vA.position[1] + interpolate * ( vB.position[1] - vA.position[1] ) );
v.Pos.X = scale * (vA.position[0] + interpolate * (vB.position[0] - vA.position[0]));
v.Pos.Y = scale * (vA.position[2] + interpolate * (vB.position[2] - vA.position[2]));
v.Pos.Z = scale * (vA.position[1] + interpolate * (vB.position[1] - vA.position[1]));
// normal
const core::vector3df nA( quake3::getMD3Normal ( vA.normal[0], vA.normal[1] ));
const core::vector3df nB( quake3::getMD3Normal ( vB.normal[0], vB.normal[1] ));
const core::vector3df nA(quake3::getMD3Normal(vA.normal[0], vA.normal[1]));
const core::vector3df nB(quake3::getMD3Normal(vB.normal[0], vB.normal[1]));
v.Normal.X = nA.X + interpolate * ( nB.X - nA.X );
v.Normal.Y = nA.Z + interpolate * ( nB.Z - nA.Z );
v.Normal.Z = nA.Y + interpolate * ( nB.Y - nA.Y );
v.Normal.X = nA.X + interpolate * (nB.X - nA.X);
v.Normal.Y = nA.Z + interpolate * (nB.Z - nA.Z);
v.Normal.Z = nA.Y + interpolate * (nB.Y - nA.Y);
}
dest->recalculateBoundingBox ();
dest->recalculateBoundingBox();
}
//! build final mesh's tag from frames frameA and frameB with linear interpolation.
void CAnimatedMeshMD3::buildTagArray ( u32 frameA, u32 frameB, f32 interpolate )
void CAnimatedMeshMD3::buildTagArray(u32 frameA, u32 frameB, f32 interpolate)
{
const u32 frameOffsetA = frameA * Mesh->MD3Header.numTags;
const u32 frameOffsetB = frameB * Mesh->MD3Header.numTags;
for ( s32 i = 0; i != Mesh->MD3Header.numTags; ++i )
for (s32 i = 0; i != Mesh->MD3Header.numTags; ++i)
{
SMD3QuaternionTag &d = TagListIPol [ i ];
@ -277,12 +312,12 @@ void CAnimatedMeshMD3::buildTagArray ( u32 frameA, u32 frameB, f32 interpolate )
const SMD3QuaternionTag &qB = Mesh->TagList[ frameOffsetB + i];
// rotation
d.rotation.slerp( qA.rotation, qB.rotation, interpolate );
d.rotation.slerp(qA.rotation, qB.rotation, interpolate);
// position
d.position.X = qA.position.X + interpolate * ( qB.position.X - qA.position.X );
d.position.Y = qA.position.Y + interpolate * ( qB.position.Y - qA.position.Y );
d.position.Z = qA.position.Z + interpolate * ( qB.position.Z - qA.position.Z );
d.position.X = qA.position.X + interpolate * (qB.position.X - qA.position.X);
d.position.Y = qA.position.Y + interpolate * (qB.position.Y - qA.position.Y);
d.position.Z = qA.position.Z + interpolate * (qB.position.Z - qA.position.Z);
}
}
@ -290,17 +325,17 @@ void CAnimatedMeshMD3::buildTagArray ( u32 frameA, u32 frameB, f32 interpolate )
/*!
loads a model
*/
bool CAnimatedMeshMD3::loadModelFile( u32 modelIndex, io::IReadFile* file,
io::IFileSystem* fs, video::IVideoDriver * driver)
bool CAnimatedMeshMD3::loadModelFile(u32 modelIndex, io::IReadFile* file,
io::IFileSystem* fs, video::IVideoDriver* driver)
{
if (!file)
return false;
//! Check MD3Header
{
file->read( &Mesh->MD3Header, sizeof(SMD3Header) );
file->read(&Mesh->MD3Header, sizeof(SMD3Header));
if ( strncmp("IDP3", Mesh->MD3Header.headerID, 4) )
if (strncmp("IDP3", Mesh->MD3Header.headerID, 4))
{
os::Printer::log("MD3 Loader: invalid header");
return false;
@ -312,13 +347,13 @@ bool CAnimatedMeshMD3::loadModelFile( u32 modelIndex, io::IReadFile* file,
u32 i;
//! Frame Data ( ignore )
//! Frame Data (ignore)
#if 0
SMD3Frame frameImport;
file->seek ( Mesh->MD3Header.frameStart );
for (i = 0; i != Mesh->MD3Header.numFrames; ++i )
file->seek(Mesh->MD3Header.frameStart);
for (i = 0; i != Mesh->MD3Header.numFrames; ++i)
{
file->read(&frameImport, sizeof(frameImport) );
file->read(&frameImport, sizeof(frameImport));
}
#endif
@ -327,11 +362,11 @@ bool CAnimatedMeshMD3::loadModelFile( u32 modelIndex, io::IReadFile* file,
SMD3Tag import;
file->seek( Mesh->MD3Header.tagStart );
Mesh->TagList.set_used ( totalTags );
for (i = 0; i != totalTags; ++i )
file->seek(Mesh->MD3Header.tagStart);
Mesh->TagList.set_used(totalTags);
for (i = 0; i != totalTags; ++i)
{
file->read(&import, sizeof(import) );
file->read(&import, sizeof(import));
SMD3QuaternionTag &exp = Mesh->TagList[i];
@ -344,84 +379,84 @@ bool CAnimatedMeshMD3::loadModelFile( u32 modelIndex, io::IReadFile* file,
exp.position.Z = import.position[1];
//! construct quaternion from a RH 3x3 Matrix
exp.rotation.set (import.rotationMatrix[7],
exp.rotation.set(import.rotationMatrix[7],
0.f,
-import.rotationMatrix[6],
1 + import.rotationMatrix[8]);
exp.rotation.normalize ();
exp.rotation.normalize();
}
//! Meshes
u32 offset = Mesh->MD3Header.tagEnd;
for (i = 0; i != (u32)Mesh->MD3Header.numMeshes; ++i )
for (i = 0; i != (u32)Mesh->MD3Header.numMeshes; ++i)
{
//! construct a new mesh buffer
SMD3MeshBuffer * buf = new SMD3MeshBuffer ();
SMD3MeshBuffer * buf = new SMD3MeshBuffer();
// !read mesh header info
SMD3MeshHeader &meshHeader = buf->MeshHeader;
//! read mesh info
file->seek( offset );
file->read( &meshHeader, sizeof(SMD3MeshHeader) );
file->seek(offset);
file->read(&meshHeader, sizeof(SMD3MeshHeader));
//! prepare memory
buf->Vertices.set_used ( meshHeader.numVertices * Mesh->MD3Header.numFrames );
buf->Indices.set_used ( meshHeader.numTriangles * 3 );
buf->Tex.set_used ( meshHeader.numVertices );
buf->Vertices.set_used(meshHeader.numVertices * Mesh->MD3Header.numFrames);
buf->Indices.set_used(meshHeader.numTriangles * 3);
buf->Tex.set_used(meshHeader.numVertices);
//! read skins (shaders). should be 1 per meshbuffer
SMD3Shader skin;
file->seek( offset + buf->MeshHeader.offset_shaders );
for ( s32 g = 0; g != buf->MeshHeader.numShader; ++g )
file->seek(offset + buf->MeshHeader.offset_shaders);
for (s32 g = 0; g != buf->MeshHeader.numShader; ++g)
{
file->read( &skin, sizeof(skin) );
file->read(&skin, sizeof(skin));
io::path name;
cutFilenameExtension ( name, skin.name );
name.replace ( '\\', '/' );
cutFilenameExtension(name, skin.name);
name.replace('\\', '/');
buf->Shader = name;
}
//! read texture coordinates
file->seek( offset + buf->MeshHeader.offset_st);
file->read( buf->Tex.pointer(), buf->MeshHeader.numVertices * sizeof(SMD3TexCoord) );
file->seek(offset + buf->MeshHeader.offset_st);
file->read(buf->Tex.pointer(), buf->MeshHeader.numVertices * sizeof(SMD3TexCoord));
//! read vertices
file->seek(offset + meshHeader.vertexStart);
file->read( buf->Vertices.pointer(), Mesh->MD3Header.numFrames * meshHeader.numVertices * sizeof(SMD3Vertex) );
file->read(buf->Vertices.pointer(), Mesh->MD3Header.numFrames * meshHeader.numVertices * sizeof(SMD3Vertex));
//! read indices
file->seek( offset + meshHeader.offset_triangles );
file->read( buf->Indices.pointer(), meshHeader.numTriangles * sizeof(SMD3Face) );
file->seek(offset + meshHeader.offset_triangles);
file->read(buf->Indices.pointer(), meshHeader.numTriangles * sizeof(SMD3Face));
//! store meshBuffer
Mesh->Buffer.push_back ( buf );
Mesh->Buffer.push_back(buf);
offset += meshHeader.offset_end;
}
// Init Mesh Interpolation
for ( i = 0; i != Mesh->Buffer.size (); ++i )
for (i = 0; i != Mesh->Buffer.size(); ++i)
{
IMeshBuffer * buffer = createMeshBuffer ( Mesh->Buffer[i], fs, driver );
MeshIPol.addMeshBuffer ( buffer );
buffer->drop ();
IMeshBuffer * buffer = createMeshBuffer(Mesh->Buffer[i], fs, driver);
MeshIPol->addMeshBuffer(buffer);
buffer->drop();
}
MeshIPol.recalculateBoundingBox ();
MeshIPol->recalculateBoundingBox();
// Init Tag Interpolation
for (i = 0; i != (u32)Mesh->MD3Header.numTags; ++i )
for (i = 0; i != (u32)Mesh->MD3Header.numTags; ++i)
{
TagListIPol.push_back ( Mesh->TagList[i] );
TagListIPol.push_back(Mesh->TagList[i]);
}
return true;
}
SMD3Mesh * CAnimatedMeshMD3::getOriginalMesh ()
SMD3Mesh * CAnimatedMeshMD3::getOriginalMesh()
{
return Mesh;
}
@ -430,7 +465,7 @@ SMD3Mesh * CAnimatedMeshMD3::getOriginalMesh ()
//! Returns an axis aligned bounding box
const core::aabbox3d<f32>& CAnimatedMeshMD3::getBoundingBox() const
{
return MeshIPol.BoundingBox;
return MeshIPol->BoundingBox;
}

View File

@ -24,52 +24,40 @@ namespace scene
public:
//! constructor
CAnimatedMeshMD3( );
CAnimatedMeshMD3();
//! destructor
virtual ~CAnimatedMeshMD3();
//! loads a quake3 md3 file
virtual bool loadModelFile( u32 modelIndex, io::IReadFile* file,
io::IFileSystem* fs, video::IVideoDriver * driver
);
virtual bool loadModelFile(u32 modelIndex, io::IReadFile* file,
io::IFileSystem* fs, video::IVideoDriver* driver);
// IAnimatedMeshMD3
virtual void setInterpolationShift ( u32 shift, u32 loopMode );
virtual SMD3Mesh * getOriginalMesh ();
virtual SMD3QuaternionTagList *getTagList(s32 frame, s32 detailLevel, s32 startFrameLoop, s32 endFrameLoop);
virtual void setInterpolationShift(u32 shift, u32 loopMode);
virtual SMD3Mesh* getOriginalMesh();
virtual SMD3QuaternionTagList* getTagList(s32 frame, s32 detailLevel, s32 startFrameLoop, s32 endFrameLoop);
//IAnimatedMesh
virtual u32 getFrameCount() const;
virtual IMesh* getMesh(s32 frame, s32 detailLevel, s32 startFrameLoop, s32 endFrameLoop);
virtual IMesh* getMesh(s32 frame, s32 detailLevel,
s32 startFrameLoop, s32 endFrameLoop);
virtual const core::aabbox3d<f32>& getBoundingBox() const;
virtual E_ANIMATED_MESH_TYPE getMeshType() const;
//! returns amount of mesh buffers.
virtual u32 getMeshBufferCount() const
{
return 0;
}
virtual u32 getMeshBufferCount() const;
//! returns pointer to a mesh buffer
virtual IMeshBuffer* getMeshBuffer(u32 nr) const
{
return 0;
}
virtual IMeshBuffer* getMeshBuffer(u32 nr) const;
//! Returns pointer to a mesh buffer which fits a material
/** \param material: material to search for
\return Returns the pointer to the mesh buffer or
NULL if there is no such mesh buffer. */
virtual IMeshBuffer* getMeshBuffer( const video::SMaterial &material) const
{
return 0;
}
virtual IMeshBuffer* getMeshBuffer(const video::SMaterial &material) const;
virtual void setMaterialFlag(video::E_MATERIAL_FLAG flag, bool newvalue)
{
return;
}
virtual void setMaterialFlag(video::E_MATERIAL_FLAG flag, bool newvalue);
//! set user axis aligned bounding box
virtual void setBoundingBox(const core::aabbox3df& box);
//! set the hardware mapping hint, for driver
virtual void setHardwareMappingHint(E_HARDWARE_MAPPING newMappingHint, E_BUFFER_TYPE buffer=EBT_VERTEX_AND_INDEX);
@ -77,16 +65,9 @@ namespace scene
//! flags the meshbuffer as changed, reloads hardware buffers
virtual void setDirty(E_BUFFER_TYPE buffer=EBT_VERTEX_AND_INDEX);
//! set user axis aligned bounding box
virtual void setBoundingBox(const core::aabbox3df& box)
{
return;
}
private:
//! animates one frame
inline void Animate (u32 frame);
inline void Animate(u32 frame);
video::SMaterial Material;
@ -100,9 +81,10 @@ namespace scene
//! Cache Info
struct SCacheInfo
{
SCacheInfo ( s32 frame = -1, s32 start = -1, s32 end = -1 )
: Frame ( frame ), startFrameLoop ( start ),
endFrameLoop ( end ) {}
SCacheInfo(s32 frame=-1, s32 start=-1, s32 end=-1 ) :
Frame(frame), startFrameLoop(start),
endFrameLoop(end)
{}
bool operator == ( const SCacheInfo &other ) const
{
@ -115,17 +97,17 @@ namespace scene
SCacheInfo Current;
//! return a Mesh per frame
SMesh MeshIPol;
SMesh* MeshIPol;
SMD3QuaternionTagList TagListIPol;
IMeshBuffer * createMeshBuffer ( const SMD3MeshBuffer *source, io::IFileSystem* fs, video::IVideoDriver * driver );
IMeshBuffer* createMeshBuffer(const SMD3MeshBuffer* source,
io::IFileSystem* fs, video::IVideoDriver* driver);
void buildVertexArray ( u32 frameA, u32 frameB, f32 interpolate,
const SMD3MeshBuffer * source,
SMeshBufferLightMap * dest
);
void buildVertexArray(u32 frameA, u32 frameB, f32 interpolate,
const SMD3MeshBuffer* source,
SMeshBufferLightMap* dest);
void buildTagArray ( u32 frameA, u32 frameB, f32 interpolate );
void buildTagArray(u32 frameA, u32 frameB, f32 interpolate);
};
@ -133,3 +115,4 @@ namespace scene
} // end namespace irr
#endif