834 lines
21 KiB
C++
834 lines
21 KiB
C++
// Copyright (C) 2002-2007 Nikolaus Gebhardt
|
|
// This file is part of the "Irrlicht Engine".
|
|
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
|
|
|
#include "CAnimatedMeshMS3D.h"
|
|
#include "os.h"
|
|
#include "IVideoDriver.h"
|
|
#include "quaternion.h"
|
|
|
|
namespace irr
|
|
{
|
|
namespace scene
|
|
{
|
|
|
|
|
|
// byte-align structures
|
|
#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__)
|
|
# pragma pack( push, packing )
|
|
# pragma pack( 1 )
|
|
# define PACK_STRUCT
|
|
#elif defined( __GNUC__ )
|
|
# define PACK_STRUCT __attribute__((packed))
|
|
#else
|
|
# error compiler not supported
|
|
#endif
|
|
|
|
// File header
|
|
struct MS3DHeader
|
|
{
|
|
c8 ID[10];
|
|
s32 Version;
|
|
} PACK_STRUCT;
|
|
|
|
// Vertex information
|
|
struct MS3DVertex
|
|
{
|
|
u8 Flags;
|
|
f32 Vertex[3];
|
|
s8 BoneID;
|
|
u8 RefCount;
|
|
} PACK_STRUCT;
|
|
|
|
// Triangle information
|
|
struct MS3DTriangle
|
|
{
|
|
u16 Flags;
|
|
u16 VertexIndices[3];
|
|
f32 VertexNormals[3][3];
|
|
f32 S[3], T[3];
|
|
u8 SmoothingGroup;
|
|
u8 GroupIndex;
|
|
} PACK_STRUCT;
|
|
|
|
// Material information
|
|
struct MS3DMaterial
|
|
{
|
|
s8 Name[32];
|
|
f32 Ambient[4];
|
|
f32 Diffuse[4];
|
|
f32 Specular[4];
|
|
f32 Emissive[4];
|
|
f32 Shininess; // 0.0f - 128.0f
|
|
f32 Transparency; // 0.0f - 1.0f
|
|
u8 Mode; // 0, 1, 2 is unused now
|
|
s8 Texture[128];
|
|
s8 Alphamap[128];
|
|
} PACK_STRUCT;
|
|
|
|
// Joint information
|
|
struct MS3DJoint
|
|
{
|
|
u8 Flags;
|
|
s8 Name[32];
|
|
s8 ParentName[32];
|
|
f32 Rotation[3];
|
|
f32 Translation[3];
|
|
u16 NumRotationKeyframes;
|
|
u16 NumTranslationKeyframes;
|
|
} PACK_STRUCT;
|
|
|
|
// Keyframe data
|
|
struct MS3DKeyframe
|
|
{
|
|
f32 Time;
|
|
f32 Parameter[3];
|
|
} PACK_STRUCT;
|
|
|
|
// Default alignment
|
|
#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__)
|
|
# pragma pack( pop, packing )
|
|
#endif
|
|
|
|
#undef PACK_STRUCT
|
|
|
|
|
|
//! constructor
|
|
CAnimatedMeshMS3D::CAnimatedMeshMS3D(video::IVideoDriver* driver)
|
|
: Driver(driver)
|
|
{
|
|
if (Driver)
|
|
Driver->grab();
|
|
|
|
lastCalculatedFrame = -1;
|
|
}
|
|
|
|
|
|
|
|
//! destructor
|
|
CAnimatedMeshMS3D::~CAnimatedMeshMS3D()
|
|
{
|
|
if (Driver)
|
|
Driver->drop();
|
|
}
|
|
|
|
|
|
|
|
//! loads an md2 file
|
|
bool CAnimatedMeshMS3D::loadFile(io::IReadFile* file)
|
|
{
|
|
s32 i=0,j;
|
|
|
|
if (!file)
|
|
return false;
|
|
|
|
HasAnimation = false;
|
|
|
|
// find file size
|
|
|
|
size_t fileSize = file->getSize();
|
|
|
|
// read whole file
|
|
|
|
u8* buffer = new u8[fileSize];
|
|
size_t read = (s32)file->read(buffer, fileSize);
|
|
if (read != fileSize)
|
|
{
|
|
delete [] buffer;
|
|
os::Printer::log("Could not read full file. Loading failed", file->getFileName(), ELL_ERROR);
|
|
return false;
|
|
}
|
|
|
|
// read header
|
|
|
|
const u8 *pPtr = (u8*)((void*)buffer);
|
|
MS3DHeader *pHeader = (MS3DHeader*)pPtr;
|
|
pPtr += sizeof(MS3DHeader);
|
|
|
|
if ( strncmp( pHeader->ID, "MS3D000000", 10 ) != 0 )
|
|
{
|
|
delete [] buffer;
|
|
os::Printer::log("Not a valid Milkshape3D Model File. Loading failed", file->getFileName(), ELL_ERROR);
|
|
return false;
|
|
}
|
|
|
|
#ifdef __BIG_ENDIAN__
|
|
pHeader->Version = os::Byteswap::byteswap(pHeader->Version);
|
|
#endif
|
|
if ( pHeader->Version < 3 || pHeader->Version > 4 )
|
|
{
|
|
delete [] buffer;
|
|
os::Printer::log("Only Milkshape3D version 1.3 and 1.4 is supported. Loading failed", file->getFileName(), ELL_ERROR);
|
|
return false;
|
|
}
|
|
|
|
// get pointers to data
|
|
|
|
// vertices
|
|
u16 numVertices = *(u16*)pPtr;
|
|
#ifdef __BIG_ENDIAN__
|
|
numVertices = os::Byteswap::byteswap(numVertices);
|
|
#endif
|
|
pPtr += sizeof(u16);
|
|
MS3DVertex *vertices = (MS3DVertex*)pPtr;
|
|
pPtr += sizeof(MS3DVertex) * numVertices;
|
|
#ifdef __BIG_ENDIAN__
|
|
for (i=0; i<numVertices; ++i)
|
|
for (j=0; j<3; ++j)
|
|
vertices[i].Vertex[j] = os::Byteswap::byteswap(vertices[i].Vertex[j]);
|
|
#endif
|
|
|
|
// triangles
|
|
u16 numTriangles = *(u16*)pPtr;
|
|
#ifdef __BIG_ENDIAN__
|
|
numTriangles = os::Byteswap::byteswap(numTriangles);
|
|
#endif
|
|
pPtr += sizeof(u16);
|
|
MS3DTriangle *triangles = (MS3DTriangle*)pPtr;
|
|
pPtr += sizeof(MS3DTriangle) * numTriangles;
|
|
#ifdef __BIG_ENDIAN__
|
|
for (i=0; i<numTriangles; ++i)
|
|
{
|
|
triangles[i].Flags = os::Byteswap::byteswap(triangles[i].Flags);
|
|
for (j=0; j<3; ++j)
|
|
{
|
|
triangles[i].VertexIndices[j] = os::Byteswap::byteswap(triangles[i].VertexIndices[j]);
|
|
for (u16 k=0; k<3; ++k)
|
|
triangles[i].VertexNormals[j][k] = os::Byteswap::byteswap(triangles[i].VertexNormals[j][k]);
|
|
triangles[i].S[j] = os::Byteswap::byteswap(triangles[i].S[j]);
|
|
triangles[i].T[j] = os::Byteswap::byteswap(triangles[i].T[j]);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// groups
|
|
u16 numGroups = *(u16*)pPtr;
|
|
#ifdef __BIG_ENDIAN__
|
|
numGroups = os::Byteswap::byteswap(numGroups);
|
|
#endif
|
|
pPtr += sizeof(u16);
|
|
|
|
//skip groups
|
|
for (i=0; i<numGroups; ++i)
|
|
{
|
|
Groups.push_back(SGroup());
|
|
SGroup& grp = Groups.getLast();
|
|
|
|
// The byte flag is before the name, so add 1
|
|
grp.Name = ((const c8*) pPtr) + 1;
|
|
|
|
pPtr += 33; // name and 1 byte flags
|
|
u16 triangleCount = *(u16*)pPtr;
|
|
#ifdef __BIG_ENDIAN__
|
|
triangleCount = os::Byteswap::byteswap(triangleCount);
|
|
#endif
|
|
pPtr += sizeof(u16);
|
|
|
|
//pPtr += sizeof(u16) * triangleCount; // triangle indices
|
|
for (j=0; j<triangleCount; ++j)
|
|
{
|
|
#ifdef __BIG_ENDIAN__
|
|
grp.VertexIds.push_back(os::Byteswap::byteswap(*(u16*)pPtr));
|
|
#else
|
|
grp.VertexIds.push_back(*(u16*)pPtr);
|
|
#endif
|
|
pPtr += sizeof (u16);
|
|
}
|
|
|
|
grp.MaterialIdx = *(u8*)pPtr;
|
|
|
|
pPtr += sizeof(c8); // material index
|
|
}
|
|
|
|
// skip materials
|
|
u16 numMaterials = *(u16*)pPtr;
|
|
#ifdef __BIG_ENDIAN__
|
|
numMaterials = os::Byteswap::byteswap(numMaterials);
|
|
#endif
|
|
pPtr += sizeof(u16);
|
|
|
|
// MS3DMaterial *materials = (MS3DMaterial*)pPtr;
|
|
// pPtr += sizeof(MS3DMaterial) * numMaterials;
|
|
|
|
if(numMaterials <= 0)
|
|
{
|
|
// if there are no materials, add at least one buffer
|
|
|
|
Buffers.push_back (SMS3DMeshBuffer ());
|
|
SMS3DMeshBuffer& tmpBuffer = Buffers.getLast();
|
|
tmpBuffer.BoundingBox = &BoundingBox;
|
|
tmpBuffer.Vertices = &AnimatedVertices;
|
|
}
|
|
|
|
for (i=0; i<numMaterials; ++i)
|
|
{
|
|
MS3DMaterial *material = (MS3DMaterial*)pPtr;
|
|
#ifdef __BIG_ENDIAN__
|
|
for (j=0; j<4; ++j)
|
|
material->Ambient[j] = os::Byteswap::byteswap(material->Ambient[j]);
|
|
for (j=0; j<4; ++j)
|
|
material->Diffuse[j] = os::Byteswap::byteswap(material->Diffuse[j]);
|
|
for (j=0; j<4; ++j)
|
|
material->Specular[j] = os::Byteswap::byteswap(material->Specular[j]);
|
|
for (j=0; j<4; ++j)
|
|
material->Emissive[j] = os::Byteswap::byteswap(material->Emissive[j]);
|
|
material->Shininess = os::Byteswap::byteswap(material->Shininess);
|
|
material->Transparency = os::Byteswap::byteswap(material->Transparency);
|
|
#endif
|
|
pPtr += sizeof(MS3DMaterial);
|
|
|
|
Buffers.push_back (SMS3DMeshBuffer ());
|
|
SMS3DMeshBuffer& tmpBuffer = Buffers.getLast();
|
|
|
|
tmpBuffer.Material.MaterialType = video::EMT_SOLID;
|
|
|
|
tmpBuffer.Material.AmbientColor = video::SColorf(material->Ambient[0], material->Ambient[1], material->Ambient[2], material->Ambient[3]).toSColor ();
|
|
tmpBuffer.Material.DiffuseColor = video::SColorf(material->Diffuse[0], material->Diffuse[1], material->Diffuse[2], material->Diffuse[3]).toSColor ();
|
|
tmpBuffer.Material.EmissiveColor = video::SColorf(material->Emissive[0], material->Emissive[1], material->Emissive[2], material->Emissive[3]).toSColor ();
|
|
tmpBuffer.Material.SpecularColor = video::SColorf(material->Specular[0], material->Specular[1], material->Specular[2], material->Specular[3]).toSColor ();
|
|
tmpBuffer.Material.Shininess = material->Shininess;
|
|
tmpBuffer.Material.Textures[0] = Driver->getTexture((const c8*)material->Texture);
|
|
tmpBuffer.BoundingBox = &BoundingBox;
|
|
tmpBuffer.Vertices = &AnimatedVertices;
|
|
}
|
|
|
|
// animation time
|
|
f32 framesPerSecond = *(f32*)pPtr;
|
|
#ifdef __BIG_ENDIAN__
|
|
framesPerSecond = os::Byteswap::byteswap(framesPerSecond);
|
|
#endif
|
|
pPtr += sizeof(f32) * 2; // fps and current time
|
|
|
|
s32 frameCount = *(s32*)pPtr;
|
|
#ifdef __BIG_ENDIAN__
|
|
frameCount = os::Byteswap::byteswap(frameCount);
|
|
#endif
|
|
pPtr += sizeof(s32);
|
|
|
|
totalTime = (frameCount / framesPerSecond) * 1000.0f;
|
|
|
|
u16 jointCount = *(u16*)pPtr;
|
|
#ifdef __BIG_ENDIAN__
|
|
jointCount = os::Byteswap::byteswap(jointCount);
|
|
#endif
|
|
pPtr += sizeof(u16);
|
|
|
|
// load joints
|
|
SJoint t;
|
|
|
|
for (i=0; i<jointCount; ++i)
|
|
{
|
|
MS3DJoint *pJoint = (MS3DJoint*)pPtr;
|
|
#ifdef __BIG_ENDIAN__
|
|
for (j=0; j<3; ++j)
|
|
pJoint->Rotation[j] = os::Byteswap::byteswap(pJoint->Rotation[j]);
|
|
for (j=0; j<3; ++j)
|
|
pJoint->Translation[j] = os::Byteswap::byteswap(pJoint->Translation[j]);
|
|
pJoint->NumRotationKeyframes= os::Byteswap::byteswap(pJoint->NumRotationKeyframes);
|
|
pJoint->NumTranslationKeyframes = os::Byteswap::byteswap(pJoint->NumTranslationKeyframes);
|
|
#endif
|
|
pPtr += sizeof(MS3DJoint);
|
|
Joints.push_back(t);
|
|
SJoint& jnt = Joints[Joints.size()-1];
|
|
|
|
jnt.Name = pJoint->Name;
|
|
jnt.Index = i;
|
|
jnt.Rotation.X = pJoint->Rotation[0];
|
|
jnt.Rotation.Y = pJoint->Rotation[1];
|
|
jnt.Rotation.Z = pJoint->Rotation[2];
|
|
jnt.Translation.X = pJoint->Translation[0];
|
|
jnt.Translation.Y = pJoint->Translation[1];
|
|
jnt.Translation.Z = pJoint->Translation[2];
|
|
jnt.ParentName = pJoint->ParentName;
|
|
jnt.Parent = -1;
|
|
|
|
if (pJoint->NumRotationKeyframes ||
|
|
pJoint->NumTranslationKeyframes)
|
|
HasAnimation = true;
|
|
|
|
// get rotation keyframes
|
|
for (j=0; j<pJoint->NumRotationKeyframes; ++j)
|
|
{
|
|
MS3DKeyframe* kf = (MS3DKeyframe*)pPtr;
|
|
#ifdef __BIG_ENDIAN__
|
|
kf->Time = os::Byteswap::byteswap(kf->Time);
|
|
for (u32 l=0; l<3; ++l)
|
|
kf->Parameter[l] = os::Byteswap::byteswap(kf->Parameter[l]);
|
|
#endif
|
|
pPtr += sizeof(MS3DKeyframe);
|
|
|
|
SKeyframe k;
|
|
k.timeindex = kf->Time * 1000.0f;
|
|
k.data.X = kf->Parameter[0];
|
|
k.data.Y = kf->Parameter[1];
|
|
k.data.Z = kf->Parameter[2];
|
|
jnt.RotationKeys.push_back(k);
|
|
}
|
|
|
|
// get translation keyframes
|
|
for (j=0; j<pJoint->NumTranslationKeyframes; ++j)
|
|
{
|
|
MS3DKeyframe* kf = (MS3DKeyframe*)pPtr;
|
|
#ifdef __BIG_ENDIAN__
|
|
kf->Time = os::Byteswap::byteswap(kf->Time);
|
|
for (u32 l=0; l<3; ++l)
|
|
kf->Parameter[l] = os::Byteswap::byteswap(kf->Parameter[l]);
|
|
#endif
|
|
pPtr += sizeof(MS3DKeyframe);
|
|
|
|
SKeyframe k;
|
|
k.timeindex = kf->Time * 1000.0f;
|
|
k.data.X = kf->Parameter[0];
|
|
k.data.Y = kf->Parameter[1];
|
|
k.data.Z = kf->Parameter[2];
|
|
jnt.TranslationKeys.push_back(k);
|
|
}
|
|
}
|
|
|
|
//find parent of every joint
|
|
for (i=0; i<(s32)Joints.size(); ++i)
|
|
{
|
|
if (Joints[i].ParentName.size() != 0)
|
|
{
|
|
for (j=0; j<(s32)Joints.size(); ++j)
|
|
if (i != j && Joints[i].ParentName == Joints[j].Name)
|
|
{
|
|
Joints[i].Parent = j;
|
|
break;
|
|
}
|
|
|
|
if (Joints[i].Parent == -1)
|
|
os::Printer::log("Found joint in model without parent.", ELL_WARNING);
|
|
}
|
|
}
|
|
|
|
// sets up all joints with initial rotation and translation
|
|
for (i=0; i<(s32)Joints.size(); ++i)
|
|
{
|
|
SJoint& jnt = Joints[i];
|
|
|
|
jnt.RelativeTransformation.setRotationRadians(jnt.Rotation);
|
|
jnt.RelativeTransformation.setTranslation(jnt.Translation);
|
|
|
|
if (jnt.Parent == -1)
|
|
jnt.AbsoluteTransformation = jnt.RelativeTransformation;
|
|
else
|
|
{
|
|
jnt.AbsoluteTransformation = Joints[jnt.Parent].AbsoluteTransformation;
|
|
jnt.AbsoluteTransformation *= jnt.RelativeTransformation;
|
|
}
|
|
|
|
jnt.AbsoluteTransformationAnimated = jnt.AbsoluteTransformation;
|
|
}
|
|
|
|
// create vertices and indices, attach them to the joints.
|
|
video::S3DVertex v;
|
|
|
|
for (i=0; i<numTriangles; ++i)
|
|
{
|
|
for (j = 0; j<3; ++j)
|
|
{
|
|
v.TCoords.X = triangles[i].S[j];
|
|
v.TCoords.Y = triangles[i].T[j];
|
|
v.Normal.X = triangles[i].VertexNormals[j][0];
|
|
v.Normal.Y = triangles[i].VertexNormals[j][1];
|
|
v.Normal.Z = triangles[i].VertexNormals[j][2];
|
|
if(triangles[i].GroupIndex < Groups.size() && Groups[triangles[i].GroupIndex].MaterialIdx < Buffers.size())
|
|
v.Color = Buffers[Groups[triangles[i].GroupIndex].MaterialIdx].Material.DiffuseColor;
|
|
else
|
|
v.Color.set(255,255,255,255);
|
|
v.Pos.X = vertices[triangles[i].VertexIndices[j]].Vertex[0];
|
|
v.Pos.Y = vertices[triangles[i].VertexIndices[j]].Vertex[1];
|
|
v.Pos.Z = vertices[triangles[i].VertexIndices[j]].Vertex[2];
|
|
|
|
//look, if we already have this vertex in our vertex array
|
|
s32 index = -1;
|
|
for (u32 iV = 0; iV < Vertices.size(); ++iV)
|
|
{
|
|
if (v == Vertices[iV])
|
|
{
|
|
index = (s32)iV;
|
|
break;
|
|
}
|
|
}
|
|
if (index == -1)
|
|
{
|
|
s32 boneid = vertices[triangles[i].VertexIndices[j]].BoneID;
|
|
if (boneid>=0 && boneid<(s32)Joints.size())
|
|
Joints[boneid].VertexIds.push_back(Vertices.size());
|
|
|
|
Vertices.push_back(v);
|
|
index = Vertices.size() - 1;
|
|
}
|
|
Indices.push_back(index);
|
|
}
|
|
}
|
|
|
|
//create groups
|
|
s32 iIndex = -1;
|
|
for (i=0; i<(int)Groups.size(); ++i)
|
|
{
|
|
SGroup& grp = Groups[i];
|
|
|
|
if (grp.MaterialIdx >= Buffers.size())
|
|
grp.MaterialIdx = 0;
|
|
|
|
core::array<u16>& indices = Buffers[grp.MaterialIdx].Indices;
|
|
|
|
for (u32 k=0; k<grp.VertexIds.size(); ++k)
|
|
for (u32 l=0; l<3; ++l)
|
|
indices.push_back(Indices[++iIndex]);
|
|
}
|
|
|
|
// calculate bounding box
|
|
if (!Vertices.empty())
|
|
BoundingBox.reset(Vertices[0].Pos);
|
|
|
|
for (i=0; i<(s32)Vertices.size(); ++i)
|
|
BoundingBox.addInternalPoint(Vertices[i].Pos);
|
|
|
|
// inverse translate and rotate all vertices for making animation easier
|
|
if (HasAnimation)
|
|
for (i=0; i<(s32)Joints.size(); ++i)
|
|
{
|
|
for (j=0; j<(s32)Joints[i].VertexIds.size(); ++j)
|
|
{
|
|
Joints[i].AbsoluteTransformation.inverseTranslateVect(
|
|
Vertices[Joints[i].VertexIds[j]].Pos);
|
|
|
|
Joints[i].AbsoluteTransformation.inverseRotateVect(
|
|
Vertices[Joints[i].VertexIds[j]].Pos);
|
|
|
|
Joints[i].AbsoluteTransformation.inverseRotateVect(
|
|
Vertices[Joints[i].VertexIds[j]].Normal);
|
|
}
|
|
}
|
|
|
|
AnimatedVertices = Vertices;
|
|
|
|
delete [] buffer;
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
//! returns the amount of frames in milliseconds. If the amount is 1, it is a static (=non animated) mesh.
|
|
s32 CAnimatedMeshMS3D::getFrameCount()
|
|
{
|
|
return (s32)totalTime;
|
|
}
|
|
|
|
|
|
|
|
//! returns the animated mesh based on a detail level. 0 is the lowest, 255 the highest detail. Note, that some Meshes will ignore the detail level.
|
|
IMesh* CAnimatedMeshMS3D::getMesh(s32 frame, s32 detailLevel, s32 startFrameLoop, s32 endFrameLoop)
|
|
{
|
|
animate(frame);
|
|
return this;
|
|
}
|
|
|
|
|
|
|
|
void CAnimatedMeshMS3D::getKeyframeData(const core::array<SKeyframe>& keys, f32 time, core::vector3df& outdata) const
|
|
{
|
|
if (keys.size())
|
|
{
|
|
if (time < keys[0].timeindex)
|
|
{
|
|
outdata = keys[0].data;
|
|
return;
|
|
}
|
|
if (time > keys.getLast().timeindex)
|
|
{
|
|
outdata = keys.getLast().data;
|
|
return;
|
|
}
|
|
|
|
for (u32 i=0; i<keys.size()-1; ++i)
|
|
{
|
|
if (keys[i].timeindex <= time && keys[i+1].timeindex >= time)
|
|
{
|
|
f32 interpolate = (time - keys[i].timeindex)/(keys[i+1].timeindex - keys[i].timeindex);
|
|
outdata = keys[i].data + ((keys[i+1].data - keys[i].data) * interpolate);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CAnimatedMeshMS3D::getKeyframeRotation(const core::array<SKeyframe>& keys, f32 time, core::vector3df& outdata) const
|
|
{
|
|
if (keys.size())
|
|
{
|
|
if (time < keys[0].timeindex)
|
|
{
|
|
outdata = keys[0].data;
|
|
return;
|
|
}
|
|
if (time > keys.getLast().timeindex)
|
|
{
|
|
outdata = keys.getLast().data;
|
|
return;
|
|
}
|
|
|
|
for (u32 i=0; i<keys.size()-1; ++i)
|
|
{
|
|
if (keys[i].timeindex <= time && keys[i+1].timeindex >= time)
|
|
{
|
|
// core::quaternion q1(keys[i].data);
|
|
// core::quaternion q2(keys[i+1].data);
|
|
f32 interpolate = (time - keys[i].timeindex)/(keys[i+1].timeindex - keys[i].timeindex);
|
|
core::quaternion q;
|
|
q.slerp(keys[i].data, keys[i+1].data, interpolate);
|
|
q.toEuler(outdata);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//! Returns a pointer to a transformation matrix of a part of the
|
|
//! mesh based on a frame time.
|
|
core::matrix4* CAnimatedMeshMS3D::getMatrixOfJoint(s32 jointNumber, s32 frame)
|
|
{
|
|
if (jointNumber < 0 || jointNumber >= (s32)Joints.size())
|
|
return 0;
|
|
|
|
animate(frame);
|
|
|
|
return &Joints[jointNumber].AbsoluteTransformationAnimated;
|
|
}
|
|
|
|
|
|
//! Gets joint count.
|
|
s32 CAnimatedMeshMS3D::getJointCount() const
|
|
{
|
|
return Joints.size();
|
|
}
|
|
|
|
|
|
//! Gets the name of a joint.
|
|
const c8* CAnimatedMeshMS3D::getJointName(s32 number) const
|
|
{
|
|
if (number < 0 || number >= (s32)Joints.size())
|
|
return 0;
|
|
return Joints[number].Name.c_str();
|
|
}
|
|
|
|
|
|
//! Gets a joint number from its name
|
|
s32 CAnimatedMeshMS3D::getJointNumber(const c8* name) const
|
|
{
|
|
for (s32 i=0; i<(s32)Joints.size(); ++i)
|
|
if (Joints[i].Name == name)
|
|
return i;
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
void CAnimatedMeshMS3D::animate(s32 frame)
|
|
{
|
|
if (!HasAnimation || lastCalculatedFrame == frame)
|
|
return;
|
|
|
|
f32 time = (f32)frame;
|
|
core::matrix4 transform;
|
|
lastCalculatedFrame = frame;
|
|
|
|
for (s32 i=0; i<(s32)Joints.size(); ++i)
|
|
{
|
|
core::vector3df translation = Joints[i].Translation;
|
|
core::vector3df rotation = Joints[i].Rotation;
|
|
|
|
// find keyframe translation and rotation
|
|
getKeyframeData(Joints[i].TranslationKeys, time, translation);
|
|
getKeyframeRotation(Joints[i].RotationKeys, time, rotation);
|
|
|
|
transform.makeIdentity();
|
|
transform.setRotationRadians(rotation);
|
|
transform.setTranslation(translation);
|
|
|
|
Joints[i].AbsoluteTransformationAnimated = Joints[i].RelativeTransformation * transform;
|
|
|
|
if (Joints[i].Parent != -1)
|
|
Joints[i].AbsoluteTransformationAnimated = Joints[Joints[i].Parent].AbsoluteTransformationAnimated * Joints[i].AbsoluteTransformationAnimated;
|
|
|
|
if (i==0)
|
|
BoundingBox.reset(Joints[0].AbsoluteTransformationAnimated.getTranslation());
|
|
else
|
|
BoundingBox.addInternalPoint(Joints[i].AbsoluteTransformationAnimated.getTranslation());
|
|
|
|
|
|
// transform all vertices
|
|
for (s32 j=0; j<(s32)Joints[i].VertexIds.size(); ++j)
|
|
{
|
|
Joints[i].AbsoluteTransformationAnimated.transformVect(
|
|
AnimatedVertices[Joints[i].VertexIds[j]].Pos,
|
|
Vertices[Joints[i].VertexIds[j]].Pos
|
|
);
|
|
|
|
Joints[i].AbsoluteTransformationAnimated.transformVect(
|
|
AnimatedVertices[Joints[i].VertexIds[j]].Normal,
|
|
Vertices[Joints[i].VertexIds[j]].Normal
|
|
);
|
|
|
|
// TODO: this could be done much more faster by
|
|
// first getting 8 extreme points and putting them in.
|
|
BoundingBox.addInternalPoint(AnimatedVertices[Joints[i].VertexIds[j]].Pos);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//! returns amount of mesh buffers.
|
|
u32 CAnimatedMeshMS3D::getMeshBufferCount() const
|
|
{
|
|
return Buffers.size();
|
|
}
|
|
|
|
|
|
|
|
//! returns pointer to a mesh buffer
|
|
IMeshBuffer* CAnimatedMeshMS3D::getMeshBuffer(u32 nr) const
|
|
{
|
|
if (nr < Buffers.size())
|
|
return (IMeshBuffer*) &Buffers[nr];
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
//! Returns pointer to a mesh buffer which fits a material
|
|
IMeshBuffer* CAnimatedMeshMS3D::getMeshBuffer( const video::SMaterial &material) const
|
|
{
|
|
for (u32 i=0; i<Buffers.size(); ++i)
|
|
{
|
|
if (Buffers[i].getMaterial() == material)
|
|
return (IMeshBuffer*) &Buffers[i];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
//! returns an axis aligned bounding box
|
|
const core::aabbox3d<f32>& CAnimatedMeshMS3D::getBoundingBox() const
|
|
{
|
|
return BoundingBox;
|
|
}
|
|
|
|
|
|
//! set user axis aligned bounding box
|
|
void CAnimatedMeshMS3D::setBoundingBox( const core::aabbox3df& box)
|
|
{
|
|
BoundingBox = box;
|
|
}
|
|
|
|
//! sets a flag of all contained materials to a new value
|
|
void CAnimatedMeshMS3D::setMaterialFlag(video::E_MATERIAL_FLAG flag, bool newvalue)
|
|
{
|
|
for (s32 i=0; i<(int)Buffers.size(); ++i)
|
|
Buffers[i].Material.setFlag(flag, newvalue);
|
|
}
|
|
|
|
|
|
//! Returns the type of the animated mesh.
|
|
E_ANIMATED_MESH_TYPE CAnimatedMeshMS3D::getMeshType() const
|
|
{
|
|
return EAMT_MS3D;
|
|
}
|
|
|
|
//! constructor
|
|
CAnimatedMeshMS3D::SMS3DMeshBuffer::SMS3DMeshBuffer()
|
|
{
|
|
#ifdef _DEBUG
|
|
setDebugName("SMS3DMeshBuffer");
|
|
#endif
|
|
}
|
|
|
|
//! destructor
|
|
CAnimatedMeshMS3D::SMS3DMeshBuffer::~SMS3DMeshBuffer()
|
|
{
|
|
}
|
|
|
|
//! returns the material of this meshbuffer
|
|
const video::SMaterial& CAnimatedMeshMS3D::SMS3DMeshBuffer::getMaterial() const
|
|
{
|
|
return Material;
|
|
}
|
|
|
|
//! returns the material of this meshbuffer
|
|
video::SMaterial& CAnimatedMeshMS3D::SMS3DMeshBuffer::getMaterial()
|
|
{
|
|
return Material;
|
|
}
|
|
|
|
//! returns pointer to vertices
|
|
const void* CAnimatedMeshMS3D::SMS3DMeshBuffer::getVertices() const
|
|
{
|
|
return Vertices->const_pointer();
|
|
}
|
|
|
|
//! returns pointer to vertices
|
|
void* CAnimatedMeshMS3D::SMS3DMeshBuffer::getVertices()
|
|
{
|
|
return Vertices->pointer();
|
|
}
|
|
|
|
//! returns amount of vertices
|
|
u32 CAnimatedMeshMS3D::SMS3DMeshBuffer::getVertexCount() const
|
|
{
|
|
return Vertices->size();
|
|
}
|
|
|
|
//! returns pointer to Indices
|
|
const u16* CAnimatedMeshMS3D::SMS3DMeshBuffer::getIndices() const
|
|
{
|
|
return Indices.const_pointer();
|
|
}
|
|
|
|
//! returns pointer to Indices
|
|
u16* CAnimatedMeshMS3D::SMS3DMeshBuffer::getIndices()
|
|
{
|
|
return Indices.pointer();
|
|
}
|
|
|
|
//! returns amount of indices
|
|
u32 CAnimatedMeshMS3D::SMS3DMeshBuffer::getIndexCount() const
|
|
{
|
|
return Indices.size();
|
|
}
|
|
|
|
//! returns an axis aligned bounding box
|
|
const core::aabbox3d<f32>& CAnimatedMeshMS3D::SMS3DMeshBuffer::getBoundingBox() const
|
|
{
|
|
return *BoundingBox;
|
|
}
|
|
|
|
//! returns an axis aligned bounding box
|
|
void CAnimatedMeshMS3D::SMS3DMeshBuffer::setBoundingBox( const core::aabbox3df& box)
|
|
{
|
|
*BoundingBox = box;
|
|
}
|
|
|
|
//! returns which type of vertex data is stored.
|
|
video::E_VERTEX_TYPE CAnimatedMeshMS3D::SMS3DMeshBuffer::getVertexType() const
|
|
{
|
|
return video::EVT_STANDARD;
|
|
}
|
|
|
|
//! returns the byte size (stride, pitch) of the vertex
|
|
u32 CAnimatedMeshMS3D::SMS3DMeshBuffer::getVertexPitch() const
|
|
{
|
|
return sizeof ( video::S3DVertex );
|
|
}
|
|
|
|
|
|
} // end namespace scene
|
|
} // end namespace irr
|
|
|
|
|