1958 lines
50 KiB
C++
1958 lines
50 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
|
|
|
|
// B3D file loader by Luke Hoschke, File format by Mark Sibly
|
|
|
|
#include "CAnimatedMeshB3d.h"
|
|
#include "os.h"
|
|
#include "IVideoDriver.h"
|
|
#include "ISceneNode.h"
|
|
|
|
namespace irr
|
|
{
|
|
namespace scene
|
|
{
|
|
|
|
|
|
struct B3dChunkHeader
|
|
{
|
|
c8 name[4];
|
|
s32 size;
|
|
};
|
|
|
|
|
|
//! constructor
|
|
CAnimatedMeshB3d::CAnimatedMeshB3d(video::IVideoDriver* driver)
|
|
: Driver(driver)
|
|
{
|
|
if (Driver)
|
|
Driver->grab();
|
|
}
|
|
|
|
|
|
//! destructor
|
|
CAnimatedMeshB3d::~CAnimatedMeshB3d()
|
|
{
|
|
|
|
if (Driver)
|
|
Driver->drop();
|
|
|
|
s32 n;
|
|
for (n=BaseVertices.size()-1; n>=0; --n)
|
|
{
|
|
delete BaseVertices[n];
|
|
BaseVertices.erase(n);
|
|
}
|
|
|
|
for (n=Buffers.size()-1; n>=0; --n)
|
|
{
|
|
delete Buffers[n];
|
|
Buffers.erase(n);
|
|
}
|
|
|
|
for (n=Materials.size()-1; n>=0; --n)
|
|
{
|
|
if (Materials[n].Material)
|
|
delete Materials[n].Material;
|
|
Materials.erase(n);
|
|
}
|
|
|
|
for (n=Nodes.size()-1; n>=0; --n)
|
|
{
|
|
delete Nodes[n];
|
|
Nodes.erase(n);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
core::stringc CAnimatedMeshB3d::readString(io::IReadFile* file)
|
|
{
|
|
core::stringc newstring;
|
|
|
|
while (file->getPos() <= file->getSize() )
|
|
{
|
|
c8 character;
|
|
file->read(&character, sizeof(character));
|
|
|
|
if (character==0)
|
|
return newstring;
|
|
|
|
newstring.append(character);
|
|
}
|
|
return newstring;
|
|
}
|
|
|
|
|
|
|
|
core::stringc CAnimatedMeshB3d::stripPathString(core::string<c8> oldstring, bool keepPath)
|
|
{
|
|
s32 lastA=oldstring.findLast('/'); // forward slash
|
|
s32 lastB=oldstring.findLast('\\'); // back slash
|
|
|
|
if (!keepPath)
|
|
{
|
|
if (lastA==-1 && lastB==-1)
|
|
return oldstring;
|
|
}
|
|
else
|
|
{
|
|
if (lastA==-1 && lastB==-1)
|
|
return core::stringc();
|
|
}
|
|
|
|
|
|
if (lastA > lastB)
|
|
{
|
|
if (!keepPath)
|
|
return oldstring.subString(lastA+1, oldstring.size() - (lastA+1));
|
|
else
|
|
return oldstring.subString(0, lastA + 1);
|
|
}
|
|
else
|
|
{
|
|
if (!keepPath)
|
|
return oldstring.subString(lastB+1, oldstring.size() - (lastB+1));
|
|
else
|
|
return oldstring.subString(0, lastB + 1);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void CAnimatedMeshB3d::readFloats(io::IReadFile* file, f32* vec, u32 count)
|
|
{
|
|
file->read(vec, count*sizeof(f32));
|
|
#ifdef __BIG_ENDIAN__
|
|
for (u32 n=0; n<count; ++n)
|
|
vec[n] = os::Byteswap::byteswap(vec[n]);
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool CAnimatedMeshB3d::ReadChunkTEXS(io::IReadFile* file)
|
|
{
|
|
|
|
bool Previous32BitTextureFlag = Driver->getTextureCreationFlag(video::ETCF_ALWAYS_32_BIT);
|
|
|
|
Driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT, true);
|
|
|
|
|
|
while(B3dStack.getLast().startposition + B3dStack.getLast().length>file->getPos()) //this chunk repeats
|
|
{
|
|
core::stringc TextureName=readString(file);
|
|
|
|
TextureName=stripPathString(file->getFileName(),true) + stripPathString(TextureName,false);
|
|
|
|
SB3dTexture B3dTexture;
|
|
|
|
B3dTexture.Texture=Driver->getTexture ( TextureName.c_str() );
|
|
|
|
file->read(&B3dTexture.Flags, sizeof(s32));
|
|
file->read(&B3dTexture.Blend, sizeof(s32));
|
|
readFloats(file, &B3dTexture.Xpos, 1);
|
|
readFloats(file, &B3dTexture.Ypos, 1);
|
|
readFloats(file, &B3dTexture.Xscale, 1);
|
|
readFloats(file, &B3dTexture.Yscale, 1);
|
|
readFloats(file, &B3dTexture.Angle, 1);
|
|
|
|
#ifdef __BIG_ENDIAN__
|
|
B3dTexture.Flags = os::Byteswap::byteswap(B3dTexture.Flags);
|
|
B3dTexture.Blend = os::Byteswap::byteswap(B3dTexture.Blend);
|
|
#endif
|
|
|
|
Textures.push_back(B3dTexture);
|
|
|
|
/*
|
|
Flags:
|
|
1: Color (default)
|
|
2: Alpha
|
|
4: Masked
|
|
8: Mipmapped
|
|
16: Clamp U
|
|
32: Clamp V
|
|
64: Spherical environment map
|
|
128: Cubic environment map
|
|
256: Store texture in vram
|
|
512: Force the use of high color textures
|
|
65536: texture uses secondary UV values
|
|
|
|
Blend:
|
|
0: Do not blend
|
|
1: No blend, or Alpha (alpha when texture loaded with alpha flag - not recommended for multitexturing - see below)
|
|
2: Multiply (default)
|
|
3: Add
|
|
4: Dot3
|
|
5: Multiply 2
|
|
*/
|
|
}
|
|
|
|
B3dStack.erase(B3dStack.size()-1);
|
|
|
|
Driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT, Previous32BitTextureFlag);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CAnimatedMeshB3d::ReadChunkBRUS(io::IReadFile* file)
|
|
{
|
|
|
|
s32 n_texs;
|
|
|
|
file->read(&n_texs, sizeof(s32));
|
|
|
|
#ifdef __BIG_ENDIAN__
|
|
n_texs = os::Byteswap::byteswap(n_texs);
|
|
#endif
|
|
|
|
while(B3dStack.getLast().startposition + B3dStack.getLast().length>file->getPos()) //this chunk repeats
|
|
{
|
|
// This is what blitz basic calls a brush, like a Irrlicht Material
|
|
core::stringc MaterialName=readString(file); //Not used but we still need the read it
|
|
|
|
SB3dMaterial B3dMaterial;
|
|
|
|
B3dMaterial.Material = new irr::video::SMaterial();
|
|
|
|
B3dMaterial.Textures[0]=0;
|
|
B3dMaterial.Textures[1]=0;
|
|
|
|
s32 texture_id[8];
|
|
texture_id[0]=-1;
|
|
texture_id[1]=-1;
|
|
|
|
file->read(&B3dMaterial.red, sizeof(B3dMaterial.red));
|
|
file->read(&B3dMaterial.green, sizeof(B3dMaterial.green));
|
|
file->read(&B3dMaterial.blue, sizeof(B3dMaterial.blue));
|
|
file->read(&B3dMaterial.alpha, sizeof(B3dMaterial.alpha));
|
|
file->read(&B3dMaterial.shininess, sizeof(B3dMaterial.shininess));
|
|
file->read(&B3dMaterial.blend, sizeof(B3dMaterial.blend));
|
|
file->read(&B3dMaterial.fx, sizeof(B3dMaterial.fx));
|
|
|
|
for (s32 n=0; n < n_texs; ++n)
|
|
{
|
|
file->read(&texture_id[n], sizeof(s32)); //I'm not sure of getting the sizeof an array
|
|
//cout << "Material is using texture id:"<< texture_id[n] <<endl;//for debuging
|
|
}
|
|
|
|
#ifdef __BIG_ENDIAN__
|
|
B3dMaterial.red = os::Byteswap::byteswap(B3dMaterial.red);
|
|
B3dMaterial.green = os::Byteswap::byteswap(B3dMaterial.green);
|
|
B3dMaterial.blue = os::Byteswap::byteswap(B3dMaterial.blue);
|
|
B3dMaterial.alpha = os::Byteswap::byteswap(B3dMaterial.alpha);
|
|
B3dMaterial.shininess = os::Byteswap::byteswap(B3dMaterial.shininess);
|
|
B3dMaterial.blend = os::Byteswap::byteswap(B3dMaterial.blend);
|
|
B3dMaterial.fx = os::Byteswap::byteswap(B3dMaterial.fx);
|
|
|
|
for (s32 n=0; n < n_texs; ++n)
|
|
texture_id[n] = os::Byteswap::byteswap(texture_id[n]);
|
|
#endif
|
|
|
|
if (texture_id[0] != -1)
|
|
B3dMaterial.Textures[0]=&Textures[texture_id[0]];
|
|
if (texture_id[1] != -1)
|
|
B3dMaterial.Textures[1]=&Textures[texture_id[1]];
|
|
|
|
//Hack, Fixes problems when the lightmap is on the first texture
|
|
if (texture_id[0] != -1)
|
|
if (Textures[texture_id[0]].Flags & 65536) // 65536 = secondary UV
|
|
{
|
|
SB3dTexture *TmpTexture;
|
|
TmpTexture = B3dMaterial.Textures[1];
|
|
B3dMaterial.Textures[1] = B3dMaterial.Textures[0];
|
|
B3dMaterial.Textures[0] = TmpTexture;
|
|
}
|
|
|
|
if (B3dMaterial.Textures[0] != 0)
|
|
B3dMaterial.Material->Textures[0] = B3dMaterial.Textures[0]->Texture;
|
|
if (B3dMaterial.Textures[1] != 0)
|
|
B3dMaterial.Material->Textures[1] = B3dMaterial.Textures[1]->Texture;
|
|
|
|
//the other textures are skipped, irrlicht I think can only have 2 Textures per Material
|
|
|
|
if (B3dMaterial.Textures[1] != 0 && B3dMaterial.Textures[0] == 0) //It could happen
|
|
{
|
|
B3dMaterial.Textures[0] = B3dMaterial.Textures[1];
|
|
B3dMaterial.Textures[1] = 0;
|
|
}
|
|
|
|
//Hacky code to convert blitz fx to irrlicht...
|
|
if (B3dMaterial.Textures[1]) //Two textures
|
|
{
|
|
if (B3dMaterial.alpha==1)
|
|
{
|
|
if (B3dMaterial.Textures[1]->Blend & 5)
|
|
{
|
|
//B3dMaterial.Material->MaterialType = video::EMT_LIGHTMAP;
|
|
B3dMaterial.Material->MaterialType = video::EMT_LIGHTMAP_M2 ;
|
|
}
|
|
else
|
|
{
|
|
B3dMaterial.Material->MaterialType = video::EMT_LIGHTMAP;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//B3dMaterial.Material->MaterialType = video::EMT_LIGHTMAP;
|
|
//B3dMaterial.Material->MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
|
|
//B3dMaterial.Material->MaterialTypeParam=B3dMaterial.alpha;
|
|
B3dMaterial.Material->MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
|
|
}
|
|
}
|
|
else if (B3dMaterial.Textures[0]) //one texture
|
|
{
|
|
if (B3dMaterial.Textures[0]->Flags & 2) // Alpha mapped
|
|
{
|
|
B3dMaterial.Material->MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
|
|
}
|
|
else if (B3dMaterial.Textures[0]->Flags & 4) // Masked
|
|
{
|
|
// Not working like blitz basic, because Irrlicht is using alpha to mask, not colour like blitz basic
|
|
|
|
B3dMaterial.Material->MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
|
|
|
|
}
|
|
else if (B3dMaterial.alpha == 1)
|
|
{
|
|
B3dMaterial.Material->MaterialType = video::EMT_SOLID;
|
|
}
|
|
else
|
|
{
|
|
//B3dMaterial.Material->MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
|
|
//B3dMaterial.Material->MaterialTypeParam=B3dMaterial.alpha;
|
|
B3dMaterial.Material->MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
|
|
}
|
|
}
|
|
else //No texture
|
|
{
|
|
|
|
if (B3dMaterial.alpha == 1)
|
|
{
|
|
B3dMaterial.Material->MaterialType = video::EMT_SOLID;
|
|
}
|
|
else
|
|
{
|
|
B3dMaterial.Material->MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
|
|
}
|
|
}
|
|
|
|
|
|
if (B3dMaterial.fx & 32) //force vertex alpha-blending
|
|
{
|
|
B3dMaterial.Material->MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
|
|
//B3dMaterial.Material->Lighting=false;
|
|
}
|
|
|
|
|
|
// Material fx...
|
|
|
|
if (B3dMaterial.fx & 1) //full-bright
|
|
{
|
|
//B3dMaterial.Material->AmbientColor = video::SColor(1, 1, 1, 1).toSColor ();
|
|
B3dMaterial.Material->AmbientColor = video::SColor(255, 255, 255, 255);
|
|
//B3dMaterial.Material->EmissiveColor = video::SColorf(1, 1, 1, 0).toSColor ();//Would be too bright and brighter than blitz basic
|
|
B3dMaterial.Material->Lighting = false;
|
|
}
|
|
else
|
|
{
|
|
B3dMaterial.Material->AmbientColor = video::SColorf(B3dMaterial.red, B3dMaterial.green, B3dMaterial.blue, B3dMaterial.alpha).toSColor ();
|
|
}
|
|
|
|
if (B3dMaterial.fx & 4) //flatshaded
|
|
B3dMaterial.Material->GouraudShading = false;
|
|
|
|
if (B3dMaterial.fx & 16) //disable backface culling
|
|
B3dMaterial.Material->BackfaceCulling = false;
|
|
|
|
|
|
B3dMaterial.Material->DiffuseColor = video::SColorf(B3dMaterial.red, B3dMaterial.green, B3dMaterial.blue, B3dMaterial.alpha).toSColor ();
|
|
B3dMaterial.Material->EmissiveColor = video::SColorf(0.5, 0.5, 0.5, 0).toSColor (); //Thoughts, I'm no sure what I should set it to?
|
|
//B3dMaterial.Material->SpecularColor = video::SColorf(0, 0, 0, 0).toSColor (); //?
|
|
B3dMaterial.Material->Shininess = B3dMaterial.shininess;
|
|
|
|
Materials.push_back(B3dMaterial);
|
|
}
|
|
|
|
B3dStack.erase(B3dStack.size()-1);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
bool CAnimatedMeshB3d::ReadChunkMESH(io::IReadFile* file, SB3dNode *InNode)
|
|
{
|
|
|
|
s32 Vertices_Start=BaseVertices.size(); //B3Ds have Vertex ID's local within the mesh I don't want this
|
|
|
|
s32 brush_id;
|
|
|
|
file->read(&brush_id, sizeof(brush_id));
|
|
|
|
#ifdef __BIG_ENDIAN__
|
|
brush_id = os::Byteswap::byteswap(brush_id);
|
|
#endif
|
|
|
|
NormalsInFile=false;
|
|
|
|
|
|
|
|
while(B3dStack.getLast().startposition + B3dStack.getLast().length>file->getPos()) //this chunk repeats
|
|
{
|
|
|
|
B3dStack.push_back(B3dChunk());
|
|
|
|
B3dChunkHeader header;
|
|
file->read(&header, sizeof(header));
|
|
|
|
#ifdef __BIG_ENDIAN__
|
|
header.size = os::Byteswap::byteswap(header.size);
|
|
#endif
|
|
|
|
B3dStack.getLast().name[0]=header.name[0];
|
|
B3dStack.getLast().name[1]=header.name[1]; //Not sure of an easier way
|
|
B3dStack.getLast().name[2]=header.name[2];
|
|
B3dStack.getLast().name[3]=header.name[3];
|
|
|
|
B3dStack.getLast().length = header.size + 8;
|
|
|
|
B3dStack.getLast().startposition = file->getPos() - 8;
|
|
|
|
bool read=false;
|
|
|
|
if ( strncmp( B3dStack.getLast().name, "VRTS", 4 ) == 0 )
|
|
{
|
|
read=true;
|
|
if (!ReadChunkVRTS(file,InNode ,0, Vertices_Start))
|
|
return false;
|
|
}
|
|
else if ( strncmp( B3dStack.getLast().name, "TRIS", 4 ) == 0 )
|
|
{
|
|
read=true;
|
|
|
|
SB3DMeshBuffer *MeshBuffer = new SB3DMeshBuffer();
|
|
MeshBuffer->VertexType=video::EVT_STANDARD;
|
|
|
|
if (brush_id!=-1)
|
|
MeshBuffer->Material=(*Materials[brush_id].Material);
|
|
|
|
if(ReadChunkTRIS(file,InNode ,MeshBuffer, Vertices_Start)==false)
|
|
return false;
|
|
|
|
MeshBuffer->recalculateBoundingBox();
|
|
|
|
if (!NormalsInFile && MeshBuffer->Material.Lighting) // No point wasting time of lightmapped levels
|
|
{
|
|
s32 i;
|
|
|
|
for ( i=0; i<(s32)MeshBuffer->Indices.size(); i+=3)
|
|
{
|
|
core::plane3d<f32> p( MeshBuffer->getVertex(MeshBuffer->Indices[i+0])->Pos,
|
|
MeshBuffer->getVertex(MeshBuffer->Indices[i+1])->Pos,
|
|
MeshBuffer->getVertex(MeshBuffer->Indices[i+2])->Pos);
|
|
|
|
MeshBuffer->getVertex(MeshBuffer->Indices[i+0])->Normal += p.Normal;
|
|
MeshBuffer->getVertex(MeshBuffer->Indices[i+1])->Normal += p.Normal;
|
|
MeshBuffer->getVertex(MeshBuffer->Indices[i+2])->Normal += p.Normal;
|
|
}
|
|
|
|
for ( i = 0; i<(s32)MeshBuffer->getVertexCount(); ++i )
|
|
{
|
|
MeshBuffer->getVertex(i)->Normal.normalize ();
|
|
BaseVertices[Vertices_Start+i]->Normal=MeshBuffer->getVertex(i)->Normal;
|
|
}
|
|
}
|
|
|
|
Buffers.push_back(MeshBuffer);
|
|
|
|
}
|
|
|
|
if (!read)
|
|
{
|
|
os::Printer::log("Unknown chunk found in mesh - skipping");
|
|
file->seek( (B3dStack.getLast().startposition + B3dStack.getLast().length) , false);
|
|
B3dStack.erase(B3dStack.size()-1);
|
|
}
|
|
}
|
|
|
|
|
|
B3dStack.erase(B3dStack.size()-1);
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
VRTS:
|
|
int flags ;1=normal values present, 2=rgba values present
|
|
int tex_coord_sets ;texture coords per vertex (eg: 1 for simple U/V) max=8
|
|
int tex_coord_set_size ;components per set (eg: 2 for simple U/V) max=4
|
|
{
|
|
float x,y,z ;always present
|
|
float nx,ny,nz ;vertex normal: present if (flags&1)
|
|
float red,green,blue,alpha ;vertex color: present if (flags&2)
|
|
float tex_coords[tex_coord_sets][tex_coord_set_size] ;tex coords
|
|
}
|
|
*/
|
|
|
|
bool CAnimatedMeshB3d::ReadChunkVRTS(io::IReadFile* file, SB3dNode *InNode, SB3DMeshBuffer *MeshBuffer,s32 Vertices_Start)
|
|
{
|
|
|
|
s32 flags, tex_coord_sets, tex_coord_set_size;
|
|
|
|
file->read(&flags, sizeof(flags));
|
|
file->read(&tex_coord_sets, sizeof(tex_coord_sets));
|
|
file->read(&tex_coord_set_size, sizeof(tex_coord_set_size));
|
|
|
|
#ifdef __BIG_ENDIAN__
|
|
flags = os::Byteswap::byteswap(flags);
|
|
tex_coord_sets = os::Byteswap::byteswap(tex_coord_sets);
|
|
tex_coord_set_size = os::Byteswap::byteswap(tex_coord_set_size);
|
|
#endif
|
|
|
|
if (tex_coord_sets >= 3 || tex_coord_set_size >= 4) // Something is wrong
|
|
{
|
|
os::Printer::log("tex_coord_sets or tex_coord_set_size too big", file->getFileName(), ELL_ERROR);
|
|
return false;
|
|
}
|
|
|
|
//------ Allocate Memory, for speed -----------//
|
|
|
|
s32 MemoryNeeded = B3dStack.getLast().length / sizeof(f32);
|
|
s32 NumberOfReads = 3;
|
|
|
|
if (flags & 1) NumberOfReads += 3;
|
|
if (flags & 2) NumberOfReads += 4;
|
|
|
|
for (s32 i=0; i<tex_coord_sets; ++i)
|
|
for (s32 j=0; j<tex_coord_set_size; ++j)
|
|
NumberOfReads += 1;
|
|
|
|
MemoryNeeded /= NumberOfReads;
|
|
|
|
//Vertices_GlobalPosition.reallocate(MemoryNeeded+Vertices_GlobalPosition.size()+1);
|
|
//Normal_GlobalRotation.reallocate(MemoryNeeded+Normal_GlobalRotation.size()+1);
|
|
|
|
BaseVertices.reallocate(MemoryNeeded + BaseVertices.size() + 1);
|
|
|
|
Vertices_Moved.reallocate(MemoryNeeded + Vertices_Moved.size() + 1);
|
|
AnimatedVertices_VertexID.reallocate(MemoryNeeded + AnimatedVertices_VertexID.size() + 1);
|
|
AnimatedVertices_MeshBuffer.reallocate(MemoryNeeded + AnimatedVertices_MeshBuffer.size() + 1);
|
|
Vertices_Alpha.reallocate(MemoryNeeded + Vertices_Alpha.size() + 1);
|
|
|
|
while(B3dStack.getLast().startposition + B3dStack.getLast().length > file->getPos()) // this chunk repeats
|
|
{
|
|
f32 x=0.0f, y=0.0f, z=0.0f;
|
|
f32 nx=0.0f, ny=0.0f, nz=0.0f;
|
|
f32 red=1.0f, green=1.0f, blue=1.0f, alpha=1.0f;
|
|
f32 tex_coords[3][4];
|
|
|
|
file->read(&x, sizeof(x));
|
|
file->read(&y, sizeof(y));
|
|
file->read(&z, sizeof(z));
|
|
|
|
if (flags & 1)
|
|
{
|
|
NormalsInFile = true;
|
|
file->read(&nx, sizeof(nx));
|
|
file->read(&ny, sizeof(ny));
|
|
file->read(&nz, sizeof(nz));
|
|
}
|
|
|
|
if (flags & 2)
|
|
{
|
|
file->read(&red, sizeof(red));
|
|
file->read(&green, sizeof(green));
|
|
file->read(&blue, sizeof(blue));
|
|
file->read(&alpha, sizeof(alpha));
|
|
}
|
|
|
|
for (s32 i=0; i<tex_coord_sets; ++i)
|
|
for (s32 j=0; j<tex_coord_set_size; ++j)
|
|
file->read(&tex_coords[i][j], sizeof(f32));
|
|
|
|
#ifdef __BIG_ENDIAN__
|
|
x = os::Byteswap::byteswap(x);
|
|
y = os::Byteswap::byteswap(y);
|
|
z = os::Byteswap::byteswap(z);
|
|
|
|
if (flags&1)
|
|
{
|
|
nx = os::Byteswap::byteswap(nx);
|
|
ny = os::Byteswap::byteswap(ny);
|
|
nz = os::Byteswap::byteswap(nz);
|
|
}
|
|
|
|
if (flags & 2)
|
|
{
|
|
red = os::Byteswap::byteswap(red);
|
|
green = os::Byteswap::byteswap(green);
|
|
blue = os::Byteswap::byteswap(blue);
|
|
alpha = os::Byteswap::byteswap(alpha);
|
|
}
|
|
|
|
for (s32 i=0; i<=tex_coord_sets-1; i++)
|
|
for (s32 j=0; j<=tex_coord_set_size-1; j++)
|
|
tex_coords[i][j] = os::Byteswap::byteswap(tex_coords[i][j]);
|
|
#endif
|
|
|
|
f32 tu=0.0f, tv=0.0f;
|
|
|
|
if (tex_coord_sets >= 1 && tex_coord_set_size >= 2)
|
|
{
|
|
tu=tex_coords[0][0];
|
|
tv=tex_coords[0][1];
|
|
}
|
|
|
|
f32 tu2=0.0f, tv2=0.0f;
|
|
|
|
if (tex_coord_sets>=2 && tex_coord_set_size>=2)
|
|
{
|
|
tu2=tex_coords[1][0];
|
|
tv2=tex_coords[1][1];
|
|
}
|
|
|
|
// Create Vertex...
|
|
video::S3DVertex2TCoords *Vertex=new video::S3DVertex2TCoords
|
|
(x, y, z, video::SColorf(red, green, blue, alpha).toSColor(), tu, tv, tu2, tv2);
|
|
//video::S3DVertex *Vertex=new video::S3DVertex
|
|
// (x, y, z, nx, ny, nz, video::SColorf(red, green, blue, alpha).toSColor(), tu, tv);
|
|
|
|
Vertex->Normal = core::vector3df(nx, ny, nz); // should this be effected by the Node's Global Matrix (eg rotation)?
|
|
|
|
// Transform the Vertex position by nested node...
|
|
core::matrix4 VertexMatrix;
|
|
VertexMatrix.setTranslation(Vertex->Pos);
|
|
|
|
//Vertices_GlobalPosition.push_back(Vertex->Pos);
|
|
|
|
//Normal_GlobalRotation.push_back(Vertex->Normal);
|
|
|
|
VertexMatrix=InNode->GlobalMatrix*VertexMatrix;
|
|
Vertex->Pos=VertexMatrix.getTranslation();
|
|
|
|
|
|
//Add it...
|
|
|
|
BaseVertices.push_back(Vertex);
|
|
|
|
Vertices_Moved.push_back(false);
|
|
AnimatedVertices_VertexID.push_back(-1);
|
|
AnimatedVertices_MeshBuffer.push_back(0);
|
|
Vertices_Alpha.push_back(alpha);
|
|
}
|
|
|
|
B3dStack.erase(B3dStack.size()-1);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool CAnimatedMeshB3d::ReadChunkTRIS(io::IReadFile* file, SB3dNode *InNode, SB3DMeshBuffer *MeshBuffer,s32 Vertices_Start)
|
|
{
|
|
|
|
s32 triangle_brush_id; // Note: Irrlicht can't have different brushes for each triangle (I'm using a workaround)
|
|
|
|
file->read(&triangle_brush_id, sizeof(triangle_brush_id));
|
|
|
|
#ifdef __BIG_ENDIAN__
|
|
triangle_brush_id = os::Byteswap::byteswap(triangle_brush_id);
|
|
#endif
|
|
|
|
SB3dMaterial *B3dMaterial;
|
|
|
|
if (triangle_brush_id != -1)
|
|
B3dMaterial = &Materials[triangle_brush_id];
|
|
else
|
|
B3dMaterial = 0;
|
|
|
|
if (B3dMaterial)
|
|
MeshBuffer->Material = (*B3dMaterial->Material);
|
|
|
|
s32 MemoryNeeded = B3dStack.getLast().length / sizeof(s32);
|
|
|
|
MeshBuffer->Indices.reallocate(MemoryNeeded + MeshBuffer->Indices.size() + 1);
|
|
|
|
while(B3dStack.getLast().startposition + B3dStack.getLast().length > file->getPos()) // this chunk repeats
|
|
{
|
|
s32 vertex_id[3];
|
|
|
|
file->read(&vertex_id[0], sizeof(s32));
|
|
file->read(&vertex_id[1], sizeof(s32));
|
|
file->read(&vertex_id[2], sizeof(s32));
|
|
|
|
#ifdef __BIG_ENDIAN__
|
|
vertex_id[0] = os::Byteswap::byteswap(vertex_id[0]);
|
|
vertex_id[1] = os::Byteswap::byteswap(vertex_id[1]);
|
|
vertex_id[2] = os::Byteswap::byteswap(vertex_id[2]);
|
|
#endif
|
|
|
|
|
|
vertex_id[0] += Vertices_Start;
|
|
vertex_id[1] += Vertices_Start;
|
|
vertex_id[2] += Vertices_Start;
|
|
|
|
for(s32 i=0; i<3; ++i)
|
|
{
|
|
if (AnimatedVertices_VertexID[ vertex_id[i] ] == -1)
|
|
{
|
|
if (BaseVertices[ vertex_id[i] ]->TCoords2 != core::vector2d<f32>(0,0))
|
|
{
|
|
MeshBuffer->MoveTo_2TCoords();
|
|
}
|
|
|
|
|
|
if (MeshBuffer->VertexType == video::EVT_STANDARD)
|
|
MeshBuffer->Vertices_Standard.push_back( *((video::S3DVertex*)BaseVertices[ vertex_id[i] ] ) );
|
|
else
|
|
MeshBuffer->Vertices_2TCoords.push_back(*BaseVertices[ vertex_id[i] ] );
|
|
|
|
AnimatedVertices_VertexID[ vertex_id[i] ] = MeshBuffer->getVertexCount()-1;
|
|
AnimatedVertices_MeshBuffer[ vertex_id[i] ] = MeshBuffer;
|
|
|
|
// Apply Material...
|
|
irr::video::S3DVertex *Vertex=MeshBuffer->getVertex(MeshBuffer->getVertexCount()-1);
|
|
|
|
|
|
if (Vertices_Alpha[ vertex_id[i] ] != 1.0f)
|
|
Vertex->Color.setAlpha( (s32)(Vertices_Alpha[ vertex_id[i] ] * 255.0f) );
|
|
else if (B3dMaterial)
|
|
Vertex->Color.setAlpha( (s32)(B3dMaterial->alpha * 255.0f) );
|
|
|
|
if (B3dMaterial)
|
|
{
|
|
// A bit of a hack, there
|
|
if (B3dMaterial->Textures[0])
|
|
{
|
|
Vertex->TCoords.X *=B3dMaterial->Textures[0]->Xscale;
|
|
Vertex->TCoords.Y *=B3dMaterial->Textures[0]->Yscale;
|
|
}
|
|
|
|
/*
|
|
if (B3dMaterial->Textures[1])
|
|
{
|
|
Vertex->TCoords2.X *=B3dMaterial->Textures[1]->Xscale;
|
|
Vertex->TCoords2.Y *=B3dMaterial->Textures[1]->Yscale;
|
|
}
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
|
|
MeshBuffer->Indices.push_back( AnimatedVertices_VertexID[ vertex_id[0] ] );
|
|
MeshBuffer->Indices.push_back( AnimatedVertices_VertexID[ vertex_id[1] ] );
|
|
MeshBuffer->Indices.push_back( AnimatedVertices_VertexID[ vertex_id[2] ] );
|
|
}
|
|
|
|
B3dStack.erase(B3dStack.size()-1);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CAnimatedMeshB3d::ReadChunkNODE(io::IReadFile* file, SB3dNode *InNode)
|
|
{
|
|
|
|
core::stringc NodeName = readString(file);
|
|
|
|
f32 position[3];
|
|
f32 scale[3];
|
|
f32 rotation[4];
|
|
|
|
s32 n;
|
|
|
|
for (n=0; n<=2; n++)
|
|
file->read(&position[n], sizeof(f32));
|
|
|
|
for (n=0; n<=2; n++)
|
|
file->read(&scale[n], sizeof(f32));
|
|
|
|
for (n=0; n<=3; n++)
|
|
file->read(&rotation[n], sizeof(f32));
|
|
|
|
#ifdef __BIG_ENDIAN__
|
|
for (n=0; n<=2; n++)
|
|
position[n] = os::Byteswap::byteswap(position[n]);
|
|
|
|
for (n=0; n<=2; n++)
|
|
scale[n] = os::Byteswap::byteswap(scale[n]);
|
|
|
|
for (n=0; n<=3; n++)
|
|
rotation[n] = os::Byteswap::byteswap(rotation[n]);
|
|
#endif
|
|
|
|
|
|
SB3dNode *Node = new SB3dNode();
|
|
Node->Name = NodeName;
|
|
|
|
Node->Animate = true;
|
|
Node->AnimatingPositionKeys = false;
|
|
Node->AnimatingScaleKeys = false;
|
|
Node->AnimatingRotationKeys = false;
|
|
Node->HasScaleAnimation = false;
|
|
|
|
Node->position = core::vector3df(position[0],position[1],position[2]);
|
|
Node->scale = core::vector3df(scale[0],scale[1],scale[2]);
|
|
Node->rotation = core::quaternion(rotation[1], rotation[2], rotation[3], rotation[0]); // meant to be in this order
|
|
Node->Animatedposition = Node->position;
|
|
Node->Animatedscale = Node->scale;
|
|
Node->Animatedrotation = Node->rotation;
|
|
|
|
irr::core::matrix4 positionMatrix;
|
|
positionMatrix.setTranslation(Node->position);
|
|
irr::core::matrix4 scaleMatrix;
|
|
scaleMatrix.setScale(Node->scale);
|
|
irr::core::matrix4 rotationMatrix = Node->rotation.getMatrix();
|
|
Node->LocalMatrix = positionMatrix * rotationMatrix * scaleMatrix;
|
|
Node->LocalAnimatedMatrix = Node->LocalMatrix; //For non-animated meshes
|
|
|
|
if (!InNode)
|
|
{
|
|
Node->GlobalMatrix = Node->LocalMatrix;
|
|
RootNodes.push_back(Node);
|
|
}
|
|
else
|
|
{
|
|
Node->GlobalMatrix = InNode->GlobalMatrix * Node->LocalMatrix;
|
|
}
|
|
|
|
Node->GlobalInversedMatrix = Node->GlobalMatrix;
|
|
Node->GlobalInversedMatrix.makeInverse(); // slow
|
|
|
|
Nodes.push_back(Node);
|
|
|
|
if (InNode != 0)
|
|
InNode->Nodes.push_back(Node);
|
|
|
|
while(B3dStack.getLast().startposition + B3dStack.getLast().length > file->getPos()) // this chunk repeats
|
|
{
|
|
B3dStack.push_back(B3dChunk());
|
|
|
|
B3dChunkHeader header;
|
|
file->read(&header, sizeof(header));
|
|
|
|
#ifdef __BIG_ENDIAN__
|
|
header.size = os::Byteswap::byteswap(header.size);
|
|
#endif
|
|
|
|
B3dStack.getLast().name[0] = header.name[0];
|
|
B3dStack.getLast().name[1] = header.name[1]; //Not sure of an easier way
|
|
B3dStack.getLast().name[2] = header.name[2];
|
|
B3dStack.getLast().name[3] = header.name[3];
|
|
B3dStack.getLast().length = header.size+8;
|
|
B3dStack.getLast().startposition = file->getPos() - 8;
|
|
|
|
bool read = false;
|
|
|
|
if ( strncmp( B3dStack.getLast().name, "NODE", 4 ) == 0 )
|
|
{
|
|
read = true;
|
|
if (!ReadChunkNODE(file, Node))
|
|
return false;
|
|
}
|
|
else if ( strncmp( B3dStack.getLast().name, "MESH", 4 ) == 0 )
|
|
{
|
|
read = true;
|
|
if (!ReadChunkMESH(file,Node))
|
|
return false;
|
|
}
|
|
else if ( strncmp( B3dStack.getLast().name, "ANIM", 4 ) == 0 )
|
|
{
|
|
read = true;
|
|
if (!ReadChunkANIM(file,Node))
|
|
return false;
|
|
}
|
|
else if ( strncmp( B3dStack.getLast().name, "BONE", 4 ) == 0 )
|
|
{
|
|
read = true;
|
|
if (!ReadChunkBONE(file,Node))
|
|
return false;
|
|
}
|
|
else if ( strncmp( B3dStack.getLast().name, "KEYS", 4 ) == 0 )
|
|
{
|
|
read = true;
|
|
if(!ReadChunkKEYS(file,Node))
|
|
return false;
|
|
}
|
|
|
|
if (!read)
|
|
{
|
|
os::Printer::log("Unknown chunk found in node - skipping");
|
|
file->seek( (B3dStack.getLast().startposition + B3dStack.getLast().length), false);
|
|
B3dStack.erase(B3dStack.size()-1);
|
|
}
|
|
}
|
|
|
|
B3dStack.erase(B3dStack.size()-1);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CAnimatedMeshB3d::ReadChunkBONE(io::IReadFile* file, SB3dNode *InNode)
|
|
{
|
|
|
|
if (B3dStack.getLast().length > 8)
|
|
{
|
|
while(B3dStack.getLast().startposition + B3dStack.getLast().length>file->getPos()) // this chunk repeats
|
|
{
|
|
SB3dBone Bone;
|
|
|
|
file->read(&Bone.vertex_id, sizeof(Bone.vertex_id));
|
|
file->read(&Bone.weight, sizeof(Bone.weight));
|
|
|
|
#ifdef __BIG_ENDIAN__
|
|
Bone.vertex_id = os::Byteswap::byteswap(Bone.vertex_id);
|
|
Bone.weight = os::Byteswap::byteswap(Bone.weight);
|
|
#endif
|
|
|
|
if (Bone.weight != 0 || Bone.weight < 0)
|
|
{
|
|
HasBones = true;
|
|
InNode->Bones.push_back(Bone);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
B3dStack.erase(B3dStack.size()-1);
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CAnimatedMeshB3d::ReadChunkKEYS(io::IReadFile* file, SB3dNode *InNode)
|
|
{
|
|
|
|
s32 flags;
|
|
file->read(&flags, sizeof(flags));
|
|
#ifdef __BIG_ENDIAN__
|
|
flags = os::Byteswap::byteswap(flags);
|
|
#endif
|
|
|
|
while(B3dStack.getLast().startposition + B3dStack.getLast().length>file->getPos()) //this chunk repeats
|
|
{
|
|
|
|
if (flags & 2) //scale
|
|
InNode->HasScaleAnimation = true;
|
|
|
|
s32 frame;
|
|
|
|
f32 positionData[3];
|
|
f32 scaleData[3];
|
|
f32 rotationData[4];
|
|
|
|
file->read(&frame, sizeof(frame));
|
|
|
|
if (flags&1)
|
|
readFloats(file, positionData, 3);
|
|
|
|
if (flags&2)
|
|
readFloats(file, scaleData, 3);
|
|
|
|
if (flags&4)
|
|
readFloats(file, rotationData, 4);
|
|
|
|
#ifdef __BIG_ENDIAN__
|
|
frame = os::Byteswap::byteswap(frame);
|
|
#endif
|
|
|
|
frame *= 100; // Scale the animation frames up
|
|
|
|
core::vector3df position = core::vector3df(positionData[0], positionData[1], positionData[2]);
|
|
core::vector3df scale = core::vector3df(scaleData[0], scaleData[1], scaleData[2]);
|
|
core::quaternion rotation = core::quaternion(rotationData[1], rotationData[2], rotationData[3], rotationData[0]); // meant to be in this order
|
|
|
|
|
|
// Workout what types of keys need to animate
|
|
|
|
if (InNode->PositionKeys.size() > 0)
|
|
if (flags&1 && InNode->Animatedposition != position)
|
|
InNode->AnimatingPositionKeys = true;
|
|
|
|
if (InNode->ScaleKeys.size() > 0)
|
|
if (flags&2 && InNode->Animatedscale != scale)
|
|
InNode->AnimatingScaleKeys = true;
|
|
|
|
if (InNode->RotationKeys.size() > 0)
|
|
{
|
|
if (flags & 4)
|
|
{
|
|
if (InNode->Animatedrotation.W != rotation.W
|
|
|| InNode->Animatedrotation.X != rotation.X
|
|
|| InNode->Animatedrotation.Y != rotation.Y
|
|
|| InNode->Animatedrotation.Z != rotation.Z)
|
|
{
|
|
InNode->AnimatingRotationKeys = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (flags & 1)
|
|
InNode->Animatedposition = position;
|
|
|
|
if (flags & 2)
|
|
InNode->Animatedscale = scale;
|
|
|
|
if (flags & 4)
|
|
InNode->Animatedrotation = rotation;
|
|
|
|
|
|
// Add key frame
|
|
|
|
if (flags & 1)
|
|
{
|
|
SB3dPositionKey Key;
|
|
Key.frame = frame;
|
|
Key.position = position;
|
|
InNode->PositionKeys.push_back(Key);
|
|
}
|
|
if (flags & 2)
|
|
{
|
|
SB3dScaleKey Key;
|
|
Key.frame = frame;
|
|
Key.scale=scale;
|
|
InNode->ScaleKeys.push_back(Key);
|
|
}
|
|
if (flags & 4)
|
|
{
|
|
SB3dRotationKey Key;
|
|
Key.frame = frame;
|
|
Key.rotation = rotation;
|
|
InNode->RotationKeys.push_back(Key);
|
|
}
|
|
|
|
}
|
|
|
|
B3dStack.erase(B3dStack.size()-1);
|
|
return true;
|
|
}
|
|
|
|
|
|
bool CAnimatedMeshB3d::ReadChunkANIM(io::IReadFile* file, SB3dNode *InNode)
|
|
{
|
|
|
|
file->read(&AnimFlags, sizeof(s32));
|
|
file->read(&AnimFrames, sizeof(s32));
|
|
readFloats(file, &AnimFPS, 1);
|
|
|
|
#ifdef __BIG_ENDIAN__
|
|
AnimFlags = os::Byteswap::byteswap(AnimFlags);
|
|
AnimFrames = os::Byteswap::byteswap(AnimFrames);
|
|
#endif
|
|
|
|
AnimFrames*=100;
|
|
|
|
totalTime=(f32)AnimFrames;
|
|
HasAnimation = 1;
|
|
lastCalculatedFrame = -1;
|
|
|
|
B3dStack.erase(B3dStack.size()-1);
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
bool CAnimatedMeshB3d::loadFile(io::IReadFile* file)
|
|
{
|
|
if (!file)
|
|
return false;
|
|
|
|
totalTime = 0;
|
|
HasAnimation = 0;
|
|
|
|
HasBones = 0;
|
|
lastCalculatedFrame = -1;
|
|
lastAnimateMode = -1;
|
|
|
|
AnimFlags = 0; // Unused for now
|
|
AnimFrames = 1; // how many frames in anim
|
|
AnimFPS = 0.0f;
|
|
|
|
AnimateNormals = false;
|
|
|
|
InterpolationMode = 1; // Set linear interpolation animation
|
|
AnimateMode = 3; // Update both the nodes and the skin in animation
|
|
|
|
B3dChunkHeader header;
|
|
|
|
file->read(&header, sizeof(header));
|
|
|
|
#ifdef __BIG_ENDIAN__
|
|
header.size = os::Byteswap::byteswap(header.size);
|
|
#endif
|
|
|
|
if ( strncmp( header.name, "BB3D", 4 ) != 0 )
|
|
{
|
|
os::Printer::log("File is not a b3d file. Loading failed", file->getFileName(), ELL_ERROR);
|
|
return false;
|
|
}
|
|
|
|
// header Chunk size here
|
|
|
|
B3dStack.clear();
|
|
|
|
B3dStack.push_back(B3dChunk());
|
|
B3dStack.getLast().name[0] = header.name[0];
|
|
B3dStack.getLast().name[1] = header.name[1];
|
|
B3dStack.getLast().name[2] = header.name[2];
|
|
B3dStack.getLast().name[3] = header.name[3];
|
|
B3dStack.getLast().startposition = file->getPos()-8;
|
|
B3dStack.getLast().length = header.size+8;
|
|
|
|
//Get file version...
|
|
|
|
u32 FileVersion;
|
|
file->read(&FileVersion, sizeof(FileVersion));
|
|
#ifdef __BIG_ENDIAN__
|
|
FileVersion = os::Byteswap::byteswap(FileVersion);
|
|
#endif
|
|
|
|
while (B3dStack.getLast().startposition + B3dStack.getLast().length > file->getPos())
|
|
{
|
|
|
|
B3dStack.push_back(B3dChunk());
|
|
|
|
file->read(&header, sizeof(header));
|
|
|
|
#ifdef __BIG_ENDIAN__
|
|
header.size = os::Byteswap::byteswap(header.size);
|
|
#endif
|
|
|
|
B3dStack.getLast().name[0] = header.name[0];
|
|
B3dStack.getLast().name[1] = header.name[1];
|
|
B3dStack.getLast().name[2] = header.name[2];
|
|
B3dStack.getLast().name[3] = header.name[3];
|
|
B3dStack.getLast().startposition = file->getPos() - 8;
|
|
B3dStack.getLast().length = header.size + 8;
|
|
|
|
bool read = false;
|
|
|
|
if ( strncmp( B3dStack.getLast().name, "TEXS", 4 ) == 0 )
|
|
{
|
|
read = true;
|
|
if (!ReadChunkTEXS(file))
|
|
return false;
|
|
}
|
|
else if ( strncmp( B3dStack.getLast().name, "BRUS", 4 ) == 0 )
|
|
{
|
|
read = true;
|
|
if (!ReadChunkBRUS(file))
|
|
return false;
|
|
}
|
|
else if ( strncmp( B3dStack.getLast().name, "NODE", 4 ) == 0 )
|
|
{
|
|
read = true;
|
|
if (!ReadChunkNODE(file, (SB3dNode*)0) )
|
|
return false;
|
|
}
|
|
|
|
if (!read)
|
|
{
|
|
os::Printer::log("Unknown chunk found in mesh base - skipping");
|
|
|
|
file->seek( (B3dStack.getLast().startposition + B3dStack.getLast().length) , false);
|
|
|
|
B3dStack.erase(B3dStack.size()-1);
|
|
|
|
}
|
|
}
|
|
|
|
B3dStack.clear();
|
|
|
|
if (HasBones)
|
|
normalizeWeights();
|
|
|
|
|
|
// Get BoundingBox...
|
|
if (Buffers.empty())
|
|
BoundingBox.reset(0,0,0);
|
|
else
|
|
{
|
|
BoundingBox.reset(Buffers[0]->BoundingBox.MaxEdge);
|
|
for (s32 i=0; i<(s32)Buffers.size(); ++i)
|
|
{
|
|
BoundingBox.addInternalBox(Buffers[i]->BoundingBox);
|
|
}
|
|
}
|
|
|
|
// For skinning
|
|
for (s32 i=0; i<(s32)Nodes.size(); ++i)
|
|
{
|
|
SB3dNode *Node = Nodes[i];
|
|
for (s32 j=0; j<(s32)Node->Bones.size(); ++j)
|
|
{
|
|
Node->Bones[j].pos = BaseVertices[Node->Bones[j].vertex_id]->Pos;
|
|
Node->Bones[j].normal = BaseVertices[Node->Bones[j].vertex_id]->Normal;
|
|
Node->Bones[j].vertex =
|
|
AnimatedVertices_MeshBuffer[ Node->Bones[j].vertex_id ]->getVertex(
|
|
AnimatedVertices_VertexID[ Node->Bones[j].vertex_id ] );
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void CAnimatedMeshB3d::normalizeWeights()
|
|
{
|
|
|
|
// Normalise the weights on bones
|
|
|
|
s32 i;
|
|
core::array<f32> Vertices_TotalWeight;
|
|
|
|
Vertices_TotalWeight.set_used(BaseVertices.size());
|
|
|
|
for (i=0; i<(s32)Vertices_TotalWeight.size(); ++i)
|
|
Vertices_TotalWeight[i] = 0;
|
|
|
|
for (i=0; i<(s32)Nodes.size(); ++i)
|
|
{
|
|
SB3dNode *Node=Nodes[i];
|
|
for (s32 j=0; j<(s32)Node->Bones.size(); ++j)
|
|
Vertices_TotalWeight[ Node->Bones[j].vertex_id ] += Node->Bones[j].weight;
|
|
}
|
|
|
|
for (i=0; i<(s32)Nodes.size(); ++i)
|
|
{
|
|
SB3dNode *Node=Nodes[i];
|
|
for (s32 j=0; j<(s32)Node->Bones.size(); ++j)
|
|
{
|
|
f32 total = Vertices_TotalWeight[ Node->Bones[j].vertex_id ];
|
|
if (total != 0 && total != 1)
|
|
Node->Bones[j].weight /= total;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
//! returns the amount of frames in milliseconds. If the amount is 1, it is a static (=non animated) mesh.
|
|
s32 CAnimatedMeshB3d::getFrameCount()
|
|
{
|
|
return AnimFrames;
|
|
}
|
|
|
|
//! 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* CAnimatedMeshB3d::getMesh(s32 frame, s32 detailLevel, s32 startFrameLoop, s32 endFrameLoop)
|
|
{
|
|
animate(frame,startFrameLoop, endFrameLoop);
|
|
return this;
|
|
}
|
|
|
|
//! Returns a pointer to a transformation matrix of a part of the
|
|
//! mesh based on a frame time.
|
|
core::matrix4* CAnimatedMeshB3d::getMatrixOfJoint(s32 jointNumber, s32 frame)
|
|
{
|
|
if (!HasAnimation || jointNumber < 0 || jointNumber >= (s32)Nodes.size())
|
|
return 0;
|
|
|
|
return &Nodes[jointNumber]->GlobalAnimatedMatrix;
|
|
}
|
|
|
|
core::matrix4* CAnimatedMeshB3d::getLocalMatrixOfJoint(s32 jointNumber)
|
|
{
|
|
if (!HasAnimation || jointNumber < 0 || jointNumber >= (s32)Nodes.size())
|
|
return 0;
|
|
|
|
return &Nodes[jointNumber]->LocalMatrix;
|
|
}
|
|
|
|
|
|
core::matrix4* CAnimatedMeshB3d::getMatrixOfJointUnanimated(s32 jointNumber)
|
|
{
|
|
if (!HasAnimation || jointNumber < 0 || jointNumber >= (s32)Nodes.size())
|
|
return 0;
|
|
|
|
return &Nodes[jointNumber]->GlobalMatrix;
|
|
}
|
|
|
|
//! Gets joint count.
|
|
s32 CAnimatedMeshB3d::getJointCount() const
|
|
{
|
|
return Nodes.size();
|
|
}
|
|
|
|
|
|
//! Gets the name of a joint.
|
|
void CAnimatedMeshB3d::setJointAnimation(s32 jointNumber, bool On)
|
|
{
|
|
if (jointNumber < 0 || jointNumber >= (s32)Nodes.size())
|
|
return;
|
|
Nodes[jointNumber]->Animate = On;
|
|
}
|
|
|
|
|
|
//! Gets the name of a joint.
|
|
const c8* CAnimatedMeshB3d::getJointName(s32 number) const
|
|
{
|
|
if (number < 0 || number >= (s32)Nodes.size())
|
|
return 0;
|
|
return Nodes[number]->Name.c_str();
|
|
}
|
|
|
|
|
|
|
|
//! Gets a joint number from its name
|
|
s32 CAnimatedMeshB3d::getJointNumber(const c8* name) const
|
|
{
|
|
for (s32 i=0; i<(s32)Nodes.size(); ++i)
|
|
if (Nodes[i]->Name == name)
|
|
return i;
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
void CAnimatedMeshB3d::CalculateGlobalMatrixes(SB3dNode *Node,SB3dNode *ParentNode)
|
|
{
|
|
if (!Node && ParentNode) // bit of protection from endless loops
|
|
return;
|
|
|
|
if (!Node)
|
|
{
|
|
for (s32 i=0; i<(s32)RootNodes.size(); ++i)
|
|
CalculateGlobalMatrixes(RootNodes[i],0);
|
|
return;
|
|
}
|
|
|
|
if (!ParentNode)
|
|
Node->GlobalAnimatedMatrix = Node->LocalAnimatedMatrix;
|
|
else
|
|
Node->GlobalAnimatedMatrix = ParentNode->GlobalAnimatedMatrix * Node->LocalAnimatedMatrix;
|
|
|
|
for (s32 j=0; j<(s32)Node->Nodes.size(); ++j)
|
|
CalculateGlobalMatrixes(Node->Nodes[j],Node);
|
|
}
|
|
|
|
|
|
void CAnimatedMeshB3d::animateSkin(f32 frame,f32 startFrame, f32 endFrame,SB3dNode *Node,SB3dNode *ParentNode)
|
|
{
|
|
// Get animated matrix...
|
|
if (!ParentNode)
|
|
Node->GlobalAnimatedMatrix = Node->LocalAnimatedMatrix;
|
|
else
|
|
Node->GlobalAnimatedMatrix = ParentNode->GlobalAnimatedMatrix * Node->LocalAnimatedMatrix;
|
|
|
|
if (Node->Bones.size())
|
|
{
|
|
core::matrix4 VerticesMatrixMove(core::matrix4::EM4CONST_NOTHING);
|
|
VerticesMatrixMove.setbyproduct(Node->GlobalAnimatedMatrix,Node->GlobalInversedMatrix);
|
|
core::vector3df ThisVertexMove, ThisNormalMove;
|
|
SB3dBone *Bone;
|
|
|
|
//__ Skin Vertex's Normals __//
|
|
if (AnimateNormals)
|
|
{
|
|
for (u32 i=0; i<Node->Bones.size(); ++i)
|
|
{
|
|
Bone=&Node->Bones[i];
|
|
//Transform normal...
|
|
//Normal_Rotation=&Base_Vertex->Normal;
|
|
ThisNormalMove.X = Bone->normal.X*VerticesMatrixMove[0] + Bone->normal.Y*VerticesMatrixMove[4] + Bone->normal.Z*VerticesMatrixMove[8];
|
|
ThisNormalMove.Y = Bone->normal.X*VerticesMatrixMove[1] + Bone->normal.Y*VerticesMatrixMove[5] + Bone->normal.Z*VerticesMatrixMove[9];
|
|
ThisNormalMove.Z = Bone->normal.X*VerticesMatrixMove[2] + Bone->normal.Y*VerticesMatrixMove[6] + Bone->normal.Z*VerticesMatrixMove[10];
|
|
|
|
if (!Vertices_Moved[Bone->vertex_id])
|
|
Bone->vertex->Normal = ThisNormalMove*Bone->weight;
|
|
else
|
|
Bone->vertex->Normal = Bone->vertex->Normal + (ThisNormalMove * Bone->weight);
|
|
}
|
|
}
|
|
|
|
|
|
//__ Skin Vertex's Position __//
|
|
for (u32 i=0; i<Node->Bones.size(); ++i)
|
|
{
|
|
Bone=&Node->Bones[i];
|
|
|
|
// Transform vector...
|
|
ThisVertexMove.X = VerticesMatrixMove[0]*Bone->pos.X + VerticesMatrixMove[4]*Bone->pos.Y + VerticesMatrixMove[8]*Bone->pos.Z + VerticesMatrixMove[12];
|
|
ThisVertexMove.Y = VerticesMatrixMove[1]*Bone->pos.X + VerticesMatrixMove[5]*Bone->pos.Y + VerticesMatrixMove[9]*Bone->pos.Z + VerticesMatrixMove[13];
|
|
ThisVertexMove.Z = VerticesMatrixMove[2]*Bone->pos.X + VerticesMatrixMove[6]*Bone->pos.Y + VerticesMatrixMove[10]*Bone->pos.Z + VerticesMatrixMove[14];
|
|
|
|
if (!Vertices_Moved[Bone->vertex_id])
|
|
{
|
|
Vertices_Moved[Bone->vertex_id] = true;
|
|
Bone->vertex->Pos = ThisVertexMove * Bone->weight;
|
|
}
|
|
else
|
|
{
|
|
Bone->vertex->Pos = Bone->vertex->Pos + (ThisVertexMove*Bone->weight);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
for (s32 j=0; j<(s32)Node->Nodes.size(); ++j)
|
|
animateSkin(frame, startFrame, endFrame, Node->Nodes[j], Node);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CAnimatedMeshB3d::getNodeAnimation(f32 frame,SB3dNode *Node,core::vector3df &position, core::vector3df &scale, core::quaternion &rotation)
|
|
{
|
|
|
|
bool foundPosition = false;
|
|
bool foundScale = false;
|
|
bool foundRotation = false;
|
|
|
|
s32 LastPosition = -1;
|
|
s32 LastScale = -1;
|
|
s32 LastRotation = -1;
|
|
|
|
s32 j, k;
|
|
|
|
if (Node->AnimatingPositionKeys)
|
|
{
|
|
j=0;
|
|
for (k=0; k < (s32)Node->PositionKeys.size(); k += 40)
|
|
{
|
|
if (Node->PositionKeys[k].frame < frame)
|
|
j=k;
|
|
else
|
|
break;
|
|
}
|
|
|
|
for (; j<(s32)Node->PositionKeys.size(); j++)
|
|
{
|
|
SB3dPositionKey *Key=&Node->PositionKeys[j];
|
|
if (Key->frame >= frame)
|
|
{
|
|
if (InterpolationMode == 0)
|
|
{
|
|
// Constant interpolate...
|
|
if (LastPosition != -1)
|
|
position = Node->PositionKeys[LastPosition].position;
|
|
else
|
|
position = Key->position;
|
|
|
|
}
|
|
else if (InterpolationMode == 1)
|
|
{
|
|
//Linear interpolate...
|
|
if (LastPosition == -1)
|
|
position = Key->position;
|
|
else
|
|
{
|
|
SB3dPositionKey *LastKey = &Node->PositionKeys[LastPosition];
|
|
if (Key->position == LastKey->position)
|
|
position = Key->position;
|
|
else
|
|
{
|
|
f32 fd1 = frame-LastKey->frame;
|
|
f32 fd2 = Key->frame-frame;
|
|
position = ((Key->position-LastKey->position)/(fd1+fd2))*fd1 + LastKey->position;
|
|
}
|
|
}
|
|
}
|
|
foundPosition = true;
|
|
break;
|
|
}
|
|
LastPosition = j;
|
|
|
|
}
|
|
if (!foundPosition && LastPosition != -1)
|
|
position = Node->PositionKeys[LastPosition].position;
|
|
}
|
|
|
|
|
|
|
|
if (Node->AnimatingRotationKeys)
|
|
{
|
|
|
|
j=0;
|
|
for (k=0; k<(s32)Node->RotationKeys.size(); k += 40)
|
|
{
|
|
if (Node->RotationKeys[k].frame < frame)
|
|
j=k;
|
|
else
|
|
break;
|
|
}
|
|
|
|
for (; j<(s32)Node->RotationKeys.size(); ++j)
|
|
{
|
|
SB3dRotationKey *Key = &Node->RotationKeys[j];
|
|
|
|
if (Key->frame >= frame)
|
|
{
|
|
if (InterpolationMode == 0)
|
|
{
|
|
// Constant interpolate...
|
|
if (LastRotation != -1)
|
|
rotation = Node->RotationKeys[LastRotation].rotation;
|
|
else
|
|
rotation = Key->rotation;
|
|
}
|
|
else if (InterpolationMode == 1)
|
|
{
|
|
// Linear interpolate...
|
|
|
|
if (LastRotation == -1)
|
|
rotation = Key->rotation;
|
|
else
|
|
{
|
|
SB3dRotationKey *LastKey=&Node->RotationKeys[LastRotation];
|
|
if (Key->rotation == LastKey->rotation)
|
|
rotation = Key->rotation;
|
|
else
|
|
{
|
|
f32 fd1 = frame-LastKey->frame;
|
|
f32 fd2 = Key->frame - frame;
|
|
f32 t = (1.0f/(fd1+fd2))*fd1;
|
|
rotation.slerp(LastKey->rotation, Key->rotation, t);
|
|
}
|
|
}
|
|
}
|
|
foundRotation = true;
|
|
break;
|
|
}
|
|
LastRotation = j;
|
|
|
|
}
|
|
if (!foundRotation && LastRotation != -1)
|
|
rotation = Node->RotationKeys[LastRotation].rotation;
|
|
}
|
|
|
|
if (Node->AnimatingScaleKeys)
|
|
{
|
|
j=0;
|
|
for (k=0; k<(s32)Node->ScaleKeys.size(); k+=40)
|
|
{
|
|
if (Node->ScaleKeys[k].frame < frame)
|
|
j = k;
|
|
else
|
|
break;
|
|
}
|
|
|
|
for (; j<(s32)Node->ScaleKeys.size(); j++)
|
|
{
|
|
SB3dScaleKey *Key = &Node->ScaleKeys[j];
|
|
|
|
if (Key->frame >= frame)
|
|
{
|
|
if (InterpolationMode == 0)
|
|
{
|
|
// Constant interpolate...
|
|
if (LastScale != -1)
|
|
scale = Node->ScaleKeys[LastScale].scale;
|
|
else
|
|
scale = Key->scale;
|
|
}
|
|
else if (InterpolationMode == 1)
|
|
{
|
|
// Linear interpolate...
|
|
|
|
if (LastScale == -1)
|
|
scale = Key->scale;
|
|
else
|
|
{
|
|
SB3dScaleKey *LastKey = &Node->ScaleKeys[LastScale];
|
|
if (Key->scale == LastKey->scale)
|
|
scale = Key->scale;
|
|
else
|
|
{
|
|
f32 fd1 = frame-LastKey->frame;
|
|
f32 fd2 = Key->frame - frame;
|
|
scale = (((Key->scale-LastKey->scale)/(fd1+fd2))*fd1) + LastKey->scale;
|
|
}
|
|
}
|
|
}
|
|
foundScale = true;
|
|
break;
|
|
}
|
|
LastScale = j;
|
|
|
|
}
|
|
if (!foundScale && LastScale != -1)
|
|
scale = Node->ScaleKeys[LastScale].scale;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void CAnimatedMeshB3d::animateNodes(f32 frame,f32 startFrame, f32 endFrame)
|
|
{
|
|
for (s32 i=0; i<(s32)Nodes.size(); ++i)
|
|
{
|
|
SB3dNode *Node = Nodes[i];
|
|
|
|
if (Node->Animate)
|
|
{
|
|
|
|
//Get keyframe...
|
|
core::vector3df position = Node->position;
|
|
core::vector3df scale = Node->scale;
|
|
core::quaternion rotation = Node->rotation;
|
|
|
|
getNodeAnimation(frame, Node, position, scale, rotation);
|
|
|
|
Node->Animatedposition = position;
|
|
Node->Animatedscale = scale;
|
|
Node->Animatedrotation = rotation;
|
|
|
|
core::matrix4 positionMatrix;
|
|
positionMatrix.setTranslation(Node->Animatedposition);
|
|
|
|
core::matrix4 rotationMatrix;
|
|
rotationMatrix = Node->Animatedrotation.getMatrix();
|
|
|
|
if (!Node->HasScaleAnimation)
|
|
{
|
|
Node->LocalAnimatedMatrix = positionMatrix * rotationMatrix;
|
|
}
|
|
else
|
|
{
|
|
core::matrix4 scaleMatrix;
|
|
scaleMatrix.setScale(Node->Animatedscale);
|
|
Node->LocalAnimatedMatrix = positionMatrix * rotationMatrix * scaleMatrix;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CAnimatedMeshB3d::animate(s32 intframe,s32 startFrameLoop, s32 endFrameLoop)// Why cannot this be "animate(f32 frame)", it would make so much nicer animations
|
|
{
|
|
|
|
if ( !HasAnimation || (lastCalculatedFrame == intframe && lastAnimateMode == AnimateMode) )
|
|
return;
|
|
|
|
lastCalculatedFrame = intframe;
|
|
lastAnimateMode = AnimateMode;
|
|
|
|
f32 frame = (f32)intframe;
|
|
f32 startFrame = (f32)startFrameLoop;
|
|
f32 endFrame = (f32)endFrameLoop;
|
|
|
|
|
|
|
|
if (AnimateMode & 1) // Update Nodes
|
|
animateNodes(frame, startFrame, endFrame);
|
|
|
|
|
|
|
|
if (AnimateMode & 2) // Update skin
|
|
{
|
|
// Reset skin
|
|
s32 i;
|
|
for (i=0; i<(s32)Vertices_Moved.size(); ++i)
|
|
Vertices_Moved[i]=false;
|
|
|
|
for (i=0; i<(s32)RootNodes.size(); ++i)
|
|
{
|
|
animateSkin(frame, startFrame, endFrame, RootNodes[i], 0);
|
|
}
|
|
/*
|
|
if (AnimateNormals)
|
|
{
|
|
for (i=0; i<(s32)Nodes.size(); ++i)
|
|
{
|
|
SB3dNode *Node = Nodes[i];
|
|
for (s32 j=0; j<(s32)Node->Bones.size(); ++j)
|
|
{
|
|
u16 VertexID = AnimatedVertices_VertexID[ Node->Bones[j].vertex_id ];
|
|
SB3DMeshBuffer *MeshBuffer = AnimatedVertices_MeshBuffer[ Node->Bones[j].vertex_id ];
|
|
video::S3DVertex2TCoords *Vertex = &MeshBuffer->Vertices[VertexID];
|
|
//video::S3DVertex *Vertex = &MeshBuffer->Vertices[VertexID];
|
|
//Vertex->Normal.normalize();
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
//! returns amount of mesh buffers.
|
|
u32 CAnimatedMeshB3d::getMeshBufferCount() const
|
|
{
|
|
return Buffers.size();
|
|
}
|
|
|
|
|
|
//! returns pointer to a mesh buffer
|
|
IMeshBuffer* CAnimatedMeshB3d::getMeshBuffer(u32 nr) const
|
|
{
|
|
if (nr < Buffers.size())
|
|
return Buffers[nr];
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
//! Returns pointer to a mesh buffer which fits a material
|
|
IMeshBuffer* CAnimatedMeshB3d::getMeshBuffer( const video::SMaterial &material) const
|
|
{
|
|
for (u32 i=0; i < Buffers.size(); ++i)
|
|
{
|
|
if (Buffers[i]->getMaterial() == material)
|
|
return Buffers[i];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//! returns an axis aligned bounding box
|
|
const core::aabbox3d<f32>& CAnimatedMeshB3d::getBoundingBox() const
|
|
{
|
|
return BoundingBox;
|
|
}
|
|
|
|
//! set user axis aligned bounding box
|
|
void CAnimatedMeshB3d::setBoundingBox( const core::aabbox3df& box)
|
|
{
|
|
BoundingBox = box;
|
|
}
|
|
|
|
//! sets a flag of all contained materials to a new value
|
|
void CAnimatedMeshB3d::setMaterialFlag(video::E_MATERIAL_FLAG flag, bool newvalue)
|
|
{
|
|
for (s32 i=0; i < (s32)Buffers.size(); ++i)
|
|
Buffers[i]->Material.setFlag(flag, newvalue);
|
|
}
|
|
|
|
//! Returns the type of the animated mesh.
|
|
E_ANIMATED_MESH_TYPE CAnimatedMeshB3d::getMeshType() const
|
|
{
|
|
return EAMT_B3D;
|
|
}
|
|
|
|
//!Update Normals when Animating
|
|
//!False= Don't (default)
|
|
//!True= Update normals, slower
|
|
void CAnimatedMeshB3d::updateNormalsWhenAnimating(bool on)
|
|
{
|
|
AnimateNormals = on;
|
|
}
|
|
|
|
//!Sets Interpolation Mode
|
|
//!0- Constant
|
|
//!1- Linear (default)
|
|
void CAnimatedMeshB3d::setInterpolationMode(s32 mode)
|
|
{
|
|
InterpolationMode = mode;
|
|
}
|
|
|
|
//!Want should happen on when animating
|
|
//!0-Nothing
|
|
//!1-Update nodes only
|
|
//!2-Update skin only
|
|
//!3-Update both nodes and skin (default)
|
|
void CAnimatedMeshB3d::setAnimateMode(s32 mode)
|
|
{
|
|
AnimateMode = mode;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void CAnimatedMeshB3d::recoverJointsFromMesh(core::array<ISceneNode*> &JointChildSceneNodes)
|
|
{
|
|
// Note: This function works because of the way the b3d fomat nests nodes, other mesh loaders may need a different function
|
|
for (s32 i=0; i<(s32)Nodes.size(); ++i)
|
|
{
|
|
ISceneNode* node = JointChildSceneNodes[i];
|
|
SB3dNode *B3dNode = Nodes[i];
|
|
node->setPosition( B3dNode->LocalAnimatedMatrix.getTranslation() );
|
|
node->setRotation( B3dNode->LocalAnimatedMatrix.getRotationDegrees() );
|
|
//node->setScale( B3dNode->LocalAnimatedMatrix.getScale() );
|
|
node->updateAbsolutePosition(); // works because of nests nodes
|
|
}
|
|
}
|
|
|
|
void CAnimatedMeshB3d::tranferJointsToMesh(core::array<ISceneNode*> &JointChildSceneNodes)
|
|
{
|
|
for (s32 i=0;i<(s32)Nodes.size();++i)
|
|
{
|
|
ISceneNode* node = JointChildSceneNodes[i];
|
|
SB3dNode *B3dNode = Nodes[i];
|
|
B3dNode->LocalAnimatedMatrix.setTranslation( node->getPosition() );
|
|
B3dNode->LocalAnimatedMatrix.setRotationDegrees( node->getRotation() );
|
|
//B3dNode->LocalAnimatedMatrix.setScale( node->getScale() );
|
|
}
|
|
lastCalculatedFrame = -1;
|
|
}
|
|
|
|
void CAnimatedMeshB3d::createJoints(core::array<ISceneNode*> &JointChildSceneNodes, ISceneNode* AnimatedMeshSceneNode, ISceneManager* SceneManager)
|
|
{
|
|
// Note: This function works because of the way the b3d format nests nodes, other mesh loaders may need a different function
|
|
createSkelton_Helper(SceneManager, JointChildSceneNodes, AnimatedMeshSceneNode, 0, 0, 0);
|
|
}
|
|
|
|
void CAnimatedMeshB3d::createSkelton_Helper(ISceneManager* SceneManager, core::array<ISceneNode*> &JointChildSceneNodes, ISceneNode* AnimatedMeshSceneNode, ISceneNode* ParentNode, SB3dNode *ParentB3dNode,SB3dNode *B3dNode)
|
|
{
|
|
// Note: This function works because of the way the b3d fomat nests nodes, other mesh loaders may need a different function
|
|
if (!ParentNode)
|
|
{
|
|
for (s32 i=0; i<(s32)RootNodes.size(); ++i)
|
|
{
|
|
B3dNode = RootNodes[i];
|
|
ISceneNode* node = SceneManager->addEmptySceneNode(AnimatedMeshSceneNode);
|
|
JointChildSceneNodes.push_back(node);
|
|
for (s32 j=0; j<(s32)B3dNode->Nodes.size(); ++j)
|
|
createSkelton_Helper(SceneManager, JointChildSceneNodes, AnimatedMeshSceneNode, node, B3dNode, B3dNode->Nodes[j]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ISceneNode* node = SceneManager->addEmptySceneNode(ParentNode);
|
|
JointChildSceneNodes.push_back(node);
|
|
for (s32 j=0; j<(s32)B3dNode->Nodes.size(); ++j)
|
|
createSkelton_Helper(SceneManager, JointChildSceneNodes, AnimatedMeshSceneNode, node, B3dNode, B3dNode->Nodes[j]);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void CAnimatedMeshB3d::convertToTangents()
|
|
{
|
|
|
|
// now calculate tangents
|
|
for (u32 b=0; b < Buffers.size(); ++b)
|
|
{
|
|
if (Buffers[b])
|
|
{
|
|
|
|
Buffers[b]->MoveTo_Tangents();
|
|
|
|
s32 idxCnt = Buffers[b]->getIndexCount();
|
|
|
|
u16* idx = Buffers[b]->getIndices();
|
|
video::S3DVertexTangents* v =
|
|
(video::S3DVertexTangents*)Buffers[b]->getVertices();
|
|
|
|
for (s32 i=0; i<idxCnt; i+=3)
|
|
{
|
|
calculateTangents(
|
|
v[idx[i+0]].Normal,
|
|
v[idx[i+0]].Tangent,
|
|
v[idx[i+0]].Binormal,
|
|
v[idx[i+0]].Pos,
|
|
v[idx[i+1]].Pos,
|
|
v[idx[i+2]].Pos,
|
|
v[idx[i+0]].TCoords,
|
|
v[idx[i+1]].TCoords,
|
|
v[idx[i+2]].TCoords);
|
|
|
|
calculateTangents(
|
|
v[idx[i+1]].Normal,
|
|
v[idx[i+1]].Tangent,
|
|
v[idx[i+1]].Binormal,
|
|
v[idx[i+1]].Pos,
|
|
v[idx[i+2]].Pos,
|
|
v[idx[i+0]].Pos,
|
|
v[idx[i+1]].TCoords,
|
|
v[idx[i+2]].TCoords,
|
|
v[idx[i+0]].TCoords);
|
|
|
|
calculateTangents(
|
|
v[idx[i+2]].Normal,
|
|
v[idx[i+2]].Tangent,
|
|
v[idx[i+2]].Binormal,
|
|
v[idx[i+2]].Pos,
|
|
v[idx[i+0]].Pos,
|
|
v[idx[i+1]].Pos,
|
|
v[idx[i+2]].TCoords,
|
|
v[idx[i+0]].TCoords,
|
|
v[idx[i+1]].TCoords);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// For skinning
|
|
for (s32 i=0; i < (s32)Nodes.size(); ++i)
|
|
{
|
|
SB3dNode *Node=Nodes[i];
|
|
for (s32 j=0; j<(s32)Node->Bones.size(); ++j)
|
|
{
|
|
Node->Bones[j].pos = BaseVertices[Node->Bones[j].vertex_id]->Pos;
|
|
Node->Bones[j].normal = BaseVertices[Node->Bones[j].vertex_id]->Normal;
|
|
Node->Bones[j].vertex =
|
|
AnimatedVertices_MeshBuffer[ Node->Bones[j].vertex_id ]->getVertex(
|
|
AnimatedVertices_VertexID[ Node->Bones[j].vertex_id ] );
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CAnimatedMeshB3d::calculateTangents(
|
|
core::vector3df& normal,
|
|
core::vector3df& tangent,
|
|
core::vector3df& binormal,
|
|
core::vector3df& vt1, core::vector3df& vt2, core::vector3df& vt3, // vertices
|
|
core::vector2df& tc1, core::vector2df& tc2, core::vector2df& tc3) // texture coords
|
|
{
|
|
core::vector3df v1 = vt1 - vt2;
|
|
core::vector3df v2 = vt3 - vt1;
|
|
normal = v2.crossProduct(v1);
|
|
normal.normalize();
|
|
|
|
// binormal
|
|
|
|
f32 deltaX1 = tc1.X - tc2.X;
|
|
f32 deltaX2 = tc3.X - tc1.X;
|
|
binormal = (v1 * deltaX2) - (v2 * deltaX1);
|
|
binormal.normalize();
|
|
|
|
// tangent
|
|
|
|
f32 deltaY1 = tc1.Y - tc2.Y;
|
|
f32 deltaY2 = tc3.Y - tc1.Y;
|
|
tangent = (v1 * deltaY2) - (v2 * deltaY1);
|
|
tangent.normalize();
|
|
|
|
// adjust
|
|
|
|
core::vector3df txb = tangent.crossProduct(binormal);
|
|
if (txb.dotProduct(normal) < 0.0f)
|
|
{
|
|
tangent *= -1.0f;
|
|
binormal *= -1.0f;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} // end namespace scene
|
|
} // end namespace irr
|
|
|
|
|