Assimp Distorted Skinned Mesh

Started by
11 comments, last by 215648 9 years, 10 months ago

I'm trying to load skinned mesh using Scot Lee's assimp animation loader but all i get is a distorted mesh. I dunno why.

Here is the source code :

cAnimationController.h


#ifndef AV_SCENEANIMATOR_H
#define AV_SCENEANIMATOR_H
 
 
typedef XMFLOAT4X4 mat4;
typedef INT32 int32_t;
typedef UINT32 uint32_t;
 
class cBone {
public:
 
std::string Name;
mat4 Offset, LocalTransform, GlobalTransform, OriginalLocalTransform;
cBone* Parent;
std::vector<cBone*> Children;
 
cBone() :Parent(0){}
~cBone(){ for(size_t i(0); i< Children.size(); i++) delete Children[i]; }
};
class cAnimationChannel{
public:
std::string Name;
std::vector<aiVectorKey> mPositionKeys;
std::vector<aiQuatKey> mRotationKeys;
std::vector<aiVectorKey> mScalingKeys;
};
 
class cAnimEvaluator{
public:
 
cAnimEvaluator(): mLastTime(0.0f), TicksPerSecond(0.0f), Duration(0.0f), PlayAnimationForward(true) {}
cAnimEvaluator( const aiAnimation* pAnim);
void Evaluate( float pTime, std::map<std::string, cBone*>& bones);
void Save(std::ofstream& file);
void Load(std::ifstream& file);
std::vector<mat4>& GetTransforms(float dt){ return Transforms[GetFrameIndexAt(dt)]; }
unsigned int GetFrameIndexAt(float time);
 
std::string Name;
std::vector<cAnimationChannel> Channels;
bool PlayAnimationForward;// play forward == true, play backward == false
float mLastTime, TicksPerSecond, Duration; 
std::vector<std::tuple<unsigned int, unsigned int, unsigned int> > mLastPositions;
std::vector<std::vector<mat4>> Transforms;//, QuatTransforms;/** Array to return transformations results inside. */
};
 
 
class SceneAnimator{
public:
 
SceneAnimator(): Skeleton(0), CurrentAnimIndex(-1) {}
~SceneAnimator(){ Release(); }
 
void Init(const aiScene* pScene);// this must be called to fill the SceneAnimator with valid data
void Release();// frees all memory and initializes everything to a default state
void Save(std::ofstream& file);
void Load(std::ifstream& file);
bool HasSkeleton() const { return !Bones.empty(); }// lets the caller know if there is a skeleton present
// the set animation returns whether the animation changed or is still the same. 
bool SetAnimIndex( int32_t pAnimIndex);// this takes an index to set the current animation to
bool SetAnimation(const std::string& name);// this takes a string to set the animation to, i.e. SetAnimation("Idle");
// the next two functions are good if you want to change the direction of the current animation. You could use a forward walking animation and reverse it to get a walking backwards
void PlayAnimationForward() { Animations[CurrentAnimIndex].PlayAnimationForward = true; }
void PlayAnimationBackward() { Animations[CurrentAnimIndex].PlayAnimationForward = false; }
//this function will adjust the current animations speed by a percentage. So, passing 100, would do nothing, passing 50, would decrease the speed by half, and 150 increase it by 50%
void AdjustAnimationSpeedBy(float percent) { Animations[CurrentAnimIndex].TicksPerSecond*=percent/100.0f; }
//This will set the animation speed
void AdjustAnimationSpeedTo(float tickspersecond) { Animations[CurrentAnimIndex].TicksPerSecond=tickspersecond; }
// get the animationspeed... in ticks per second
float GetAnimationSpeed() const { return Animations[CurrentAnimIndex].TicksPerSecond; }
// get the transforms needed to pass to the vertex shader. This will wrap the dt value passed, so it is safe to pass 50000000 as a valid number
std::vector<mat4>& GetTransforms(float dt){ return Animations[CurrentAnimIndex].GetTransforms(dt); }
 
int32_t GetAnimationIndex() const { return CurrentAnimIndex; }
std::string GetAnimationName() const { return Animations[CurrentAnimIndex].Name;  }
//GetBoneIndex will return the index of the bone given its name. The index can be used to index directly into the vector returned from GetTransform
int GetBoneIndex(const std::string& bname){ std::map<std::string, unsigned int>::iterator found = BonesToIndex.find(bname); if(found!=BonesToIndex.end()) return found->second; else return -1;}
//GetBoneTransform will return the matrix of the bone given its name and the time. be careful with this to make sure and send the correct dt. If the dt is different from what the model is currently at, the transform will be off
mat4 GetBoneTransform(float dt, const std::string& bname) { int bindex=GetBoneIndex(bname); if(bindex == -1) return mat4(); return Animations[CurrentAnimIndex].GetTransforms(dt)[bindex]; }
// same as above, except takes the index
mat4 GetBoneTransform(float dt, unsigned int bindex) {  return Animations[CurrentAnimIndex].GetTransforms(dt)[bindex]; }
 
std::vector<cAnimEvaluator> Animations;// a std::vector that holds each animation 
int32_t CurrentAnimIndex;/** Current animation index */
 
protected: 
 
 
cBone* Skeleton;/** Root node of the internal scene structure */
std::map<std::string, cBone*> BonesByName;/** Name to node map to quickly find nodes by their name */
std::map<std::string, unsigned int> BonesToIndex;/** Name to node map to quickly find nodes by their name */
std::map<std::string, uint32_t> AnimationNameToId;// find animations quickly
std::vector<cBone*> Bones;// DO NOT DELETE THESE when the destructor runs... THEY ARE JUST COPIES!!
std::vector<mat4> Transforms;// temp array of transfrorms
 
void SaveSkeleton(std::ofstream& file, cBone* pNode);
cBone* LoadSkeleton(std::ifstream& file, cBone* pNode);
 
void UpdateTransforms(cBone* pNode);
void CalculateBoneToWorldTransform(cBone* pInternalNode);/** Calculates the global transformation matrix for the given internal node */
 
void Calculate( float pTime);
void CalcBoneMatrices();
 
void ExtractAnimations(const aiScene* pScene);
cBone* CreateBoneTree( aiNode* pNode, cBone* pParent);// Recursively creates an internal node structure matching the current scene and animation. 
 
};
 
#endif
cAnimationController.cpp


#include "stdafx.h"
#include "cAnimationController.h"
 
void TransformMatrix(mat4& out, const aiMatrix4x4& in)
{ // there is some type of alignment issue with my mat4 and the aimatrix4x4 class, so the copy must be manually
out._11=in.a1;
out._12=in.a2;
out._13=in.a3;
out._14=in.a4;
 
out._21=in.b1;
out._22=in.b2;
out._23=in.b3;
out._24=in.b4;
 
out._31=in.c1;
out._32=in.c2;
out._33=in.c3;
out._34=in.c4;
 
out._41=in.d1;
out._42=in.d2;
out._43=in.d3;
out._44=in.d4;
}
// ------------------------------------------------------------------------------------------------
// Constructor on a given animation. 
cAnimEvaluator::cAnimEvaluator( const aiAnimation* pAnim) {
mLastTime = 0.0;
TicksPerSecond = static_cast<float>(pAnim->mTicksPerSecond != 0.0f ? pAnim->mTicksPerSecond : 100.0f);
Duration = static_cast<float>(pAnim->mDuration);
Name = pAnim->mName.data;
//OUTPUT_DEBUG_MSG("Creating Animation named: "<<Name);
Channels.resize(pAnim->mNumChannels);
for( unsigned int a = 0; a < pAnim->mNumChannels; a++){ 
Channels[a].Name = pAnim->mChannels[a]->mNodeName.data;
for(unsigned int i(0); i< pAnim->mChannels[a]->mNumPositionKeys; i++) Channels[a].mPositionKeys.push_back(pAnim->mChannels[a]->mPositionKeys[i]);
for(unsigned int i(0); i< pAnim->mChannels[a]->mNumRotationKeys; i++) Channels[a].mRotationKeys.push_back(pAnim->mChannels[a]->mRotationKeys[i]);
for(unsigned int i(0); i< pAnim->mChannels[a]->mNumScalingKeys; i++) Channels[a].mScalingKeys.push_back(pAnim->mChannels[a]->mScalingKeys[i]);
}
mLastPositions.resize( pAnim->mNumChannels, std::make_tuple( 0, 0, 0));
//OUTPUT_DEBUG_MSG("Finished Creating Animation named: "<<Name);
}
 
unsigned int cAnimEvaluator::GetFrameIndexAt(float ptime){
// get a [0.f ... 1.f) value by allowing the percent to wrap around 1
ptime *= TicksPerSecond;
 
float time = 0.0f;
if( Duration > 0.0)
time = fmod( ptime, Duration);
 
float percent = time / Duration;
if(!PlayAnimationForward) percent= (percent-1.0f)*-1.0f;// this will invert the percent so the animation plays backwards
return static_cast<unsigned int>(( static_cast<float>(Transforms.size()) * percent));
}
void cAnimEvaluator::Save(std::ofstream& file){
uint32_t nsize = static_cast<uint32_t>(Name.size());
file.write(reinterpret_cast<char*>(&nsize), sizeof(uint32_t));// the size of the animation name
file.write(Name.c_str(), nsize);// the size of the animation name
file.write(reinterpret_cast<char*>(&Duration), sizeof(Duration));// the duration
file.write(reinterpret_cast<char*>(&TicksPerSecond), sizeof(TicksPerSecond));// the number of ticks per second
nsize = static_cast<uint32_t>(Channels.size());// number of animation channels,
file.write(reinterpret_cast<char*>(&nsize), sizeof(uint32_t));// the number animation channels
for(size_t j(0); j< Channels.size(); j++){// for each channel
nsize =static_cast<uint32_t>(Channels[j].Name.size());
file.write(reinterpret_cast<char*>(&nsize), sizeof(uint32_t));// the size of the name
file.write(Channels[j].Name.c_str(), nsize);// the size of the animation name
 
nsize =static_cast<uint32_t>(Channels[j].mPositionKeys.size());
file.write(reinterpret_cast<char*>(&nsize), sizeof(uint32_t));// the number of position keys
for(size_t i(0); i< nsize; i++){// for each channel
file.write(reinterpret_cast<char*>(&Channels[j].mPositionKeys[i].mTime), sizeof(Channels[j].mPositionKeys[i].mTime));// pos key
file.write(reinterpret_cast<char*>(&Channels[j].mPositionKeys[i].mValue), sizeof(Channels[j].mPositionKeys[i].mValue));// pos key
}
 
nsize =static_cast<uint32_t>(Channels[j].mRotationKeys.size());
file.write(reinterpret_cast<char*>(&nsize), sizeof(uint32_t));// the number of position keys
for(size_t i(0); i< nsize; i++){// for each channel
file.write(reinterpret_cast<char*>(&Channels[j].mRotationKeys[i].mTime), sizeof(Channels[j].mRotationKeys[i].mTime));// rot key
file.write(reinterpret_cast<char*>(&Channels[j].mRotationKeys[i].mValue), sizeof(Channels[j].mRotationKeys[i].mValue));// rot key
}
 
nsize =static_cast<uint32_t>(Channels[j].mScalingKeys.size());
file.write(reinterpret_cast<char*>(&nsize), sizeof(uint32_t));// the number of position keys
for(size_t i(0); i< nsize; i++){// for each channel
file.write(reinterpret_cast<char*>(&Channels[j].mScalingKeys[i].mTime), sizeof(Channels[j].mScalingKeys[i].mTime));// rot key
file.write(reinterpret_cast<char*>(&Channels[j].mScalingKeys[i].mValue), sizeof(Channels[j].mScalingKeys[i].mValue));// rot key
}
 
}
}
void cAnimEvaluator::Load(std::ifstream& file){
uint32_t nsize = 0;
file.read(reinterpret_cast<char*>(&nsize), sizeof(uint32_t));// the size of the animation name
char temp[250];
file.read(temp, nsize);// the size of the animation name
temp[nsize]=0;// null char
Name=temp;
//OUTPUT_DEBUG_MSG("Creating Animation named: "<<Name);
file.read(reinterpret_cast<char*>(&Duration), sizeof(Duration));// the duration
file.read(reinterpret_cast<char*>(&TicksPerSecond), sizeof(TicksPerSecond));// the number of ticks per second
file.read(reinterpret_cast<char*>(&nsize), sizeof(uint32_t));// the number animation channels
Channels.resize(nsize);
for(size_t j(0); j< Channels.size(); j++){// for each channel
nsize =0;
file.read(reinterpret_cast<char*>(&nsize), sizeof(uint32_t));// the size of the name
file.read(temp, nsize);// the size of the animation name
temp[nsize]=0;// null char
Channels[j].Name=temp;
 
nsize =static_cast<uint32_t>(Channels[j].mPositionKeys.size());
file.read(reinterpret_cast<char*>(&nsize), sizeof(uint32_t));// the number of position keys
Channels[j].mPositionKeys.resize(nsize);
for(size_t i(0); i< nsize; i++){// for each channel
file.read(reinterpret_cast<char*>(&Channels[j].mPositionKeys[i].mTime), sizeof(Channels[j].mPositionKeys[i].mTime));// pos key
file.read(reinterpret_cast<char*>(&Channels[j].mPositionKeys[i].mValue), sizeof(Channels[j].mPositionKeys[i].mValue));// pos key
}
 
nsize =static_cast<uint32_t>(Channels[j].mRotationKeys.size());
file.read(reinterpret_cast<char*>(&nsize), sizeof(uint32_t));// the number of position keys
Channels[j].mRotationKeys.resize(nsize);
for(size_t i(0); i< nsize; i++){// for each channel
file.read(reinterpret_cast<char*>(&Channels[j].mRotationKeys[i].mTime), sizeof(Channels[j].mRotationKeys[i].mTime));// pos key
file.read(reinterpret_cast<char*>(&Channels[j].mRotationKeys[i].mValue), sizeof(Channels[j].mRotationKeys[i].mValue));// pos key
}
 
nsize =static_cast<uint32_t>(Channels[j].mScalingKeys.size());
file.read(reinterpret_cast<char*>(&nsize), sizeof(uint32_t));// the number of position keys
Channels[j].mScalingKeys.resize(nsize);
for(size_t i(0); i< nsize; i++){// for each channel
file.read(reinterpret_cast<char*>(&Channels[j].mScalingKeys[i].mTime), sizeof(Channels[j].mScalingKeys[i].mTime));// pos key
file.read(reinterpret_cast<char*>(&Channels[j].mScalingKeys[i].mValue), sizeof(Channels[j].mScalingKeys[i].mValue));// pos key
}
}
mLastPositions.resize( Channels.size(), std::make_tuple( 0, 0, 0));
}
// ------------------------------------------------------------------------------------------------
// Evaluates the animation tracks for a given time stamp. 
void cAnimEvaluator::Evaluate( float pTime, std::map<std::string, cBone*>& bones) {
 
pTime *= TicksPerSecond;
 
float time = 0.0f;
if( Duration > 0.0)
time = fmod( pTime, Duration);
 
// calculate the transformations for each animation channel
for( unsigned int a = 0; a < Channels.size(); a++){
const cAnimationChannel* channel = &Channels[a];
std::map<std::string, cBone*>::iterator bonenode = bones.find(channel->Name);
 
if(bonenode == bones.end()) { 
MessageBox(0, L"Did not find the bone node", 0, 0);
continue;
}
 
// ******** Position *****
aiVector3D presentPosition( 0, 0, 0);
if( channel->mPositionKeys.size() > 0){
// Look for present frame number. Search from last position if time is after the last time, else from beginning
// Should be much quicker than always looking from start for the average use case.
unsigned int frame = (time >= mLastTime) ? std::get<0>(mLastPositions[a]): 0;
while( frame < channel->mPositionKeys.size() - 1){
if( time < channel->mPositionKeys[frame+1].mTime){
break;
}
frame++;
}
 
// interpolate between this frame's value and next frame's value
unsigned int nextFrame = (frame + 1) % channel->mPositionKeys.size();
 
const aiVectorKey& key = channel->mPositionKeys[frame];
const aiVectorKey& nextKey = channel->mPositionKeys[nextFrame];
double diffTime = nextKey.mTime - key.mTime;
if( diffTime < 0.0)
diffTime += Duration;
if( diffTime > 0) {
float factor = float( (time - key.mTime) / diffTime);
presentPosition = key.mValue + (nextKey.mValue - key.mValue) * factor;
} else {
presentPosition = key.mValue;
}
std::get<0>(mLastPositions[a]) = frame;
}
// ******** Rotation *********
aiQuaternion presentRotation( 1, 0, 0, 0);
if( channel->mRotationKeys.size() > 0)
{
unsigned int frame = (time >= mLastTime) ? std::get<1>(mLastPositions[a]) : 0;
while( frame < channel->mRotationKeys.size()  - 1){
if( time < channel->mRotationKeys[frame+1].mTime)
break;
frame++;
}
 
// interpolate between this frame's value and next frame's value
unsigned int nextFrame = (frame + 1) % channel->mRotationKeys.size() ;
 
const aiQuatKey& key = channel->mRotationKeys[frame];
const aiQuatKey& nextKey = channel->mRotationKeys[nextFrame];
double diffTime = nextKey.mTime - key.mTime;
if( diffTime < 0.0) diffTime += Duration;
if( diffTime > 0) {
float factor = float( (time - key.mTime) / diffTime);
aiQuaternion::Interpolate( presentRotation, key.mValue, nextKey.mValue, factor);
} else presentRotation = key.mValue;
std::get<1>(mLastPositions[a]) = frame;
}
 
// ******** Scaling **********
aiVector3D presentScaling( 1, 1, 1);
if( channel->mScalingKeys.size() > 0) {
unsigned int frame = (time >= mLastTime) ? std::get<2>(mLastPositions[a]) : 0;
while( frame < channel->mScalingKeys.size() - 1){
if( time < channel->mScalingKeys[frame+1].mTime)
break;
frame++;
}
 
presentScaling = channel->mScalingKeys[frame].mValue;
std::get<2>(mLastPositions[a]) = frame;
}
 
aiMatrix4x4 mat = aiMatrix4x4( presentRotation.GetMatrix());
 
mat.a1 *= presentScaling.x; mat.b1 *= presentScaling.x; mat.c1 *= presentScaling.x;
mat.a2 *= presentScaling.y; mat.b2 *= presentScaling.y; mat.c2 *= presentScaling.y;
mat.a3 *= presentScaling.z; mat.b3 *= presentScaling.z; mat.c3 *= presentScaling.z;
mat.a4 = presentPosition.x; mat.b4 = presentPosition.y; mat.c4 = presentPosition.z;
mat.Transpose();
 
TransformMatrix(bonenode->second->LocalTransform, mat);
}
mLastTime = time;
}
 
void SceneAnimator::Release(){// this should clean everything up 
CurrentAnimIndex = -1;
Animations.clear();// clear all animations
delete Skeleton;// This node will delete all children recursivly
Skeleton = NULL;// make sure to zero it out
}
void SceneAnimator::Init(const aiScene* pScene){// this will build the skeleton based on the scene passed to it and CLEAR EVERYTHING
if(!pScene->HasAnimations()) return;
Release();
 
Skeleton = CreateBoneTree( pScene->mRootNode, NULL);
ExtractAnimations(pScene);
 
for (unsigned int i = 0; i < pScene->mNumMeshes;++i){
const aiMesh* mesh = pScene->mMeshes[i];
 
for (unsigned int n = 0; n < mesh->mNumBones;++n){
const aiBone* bone = mesh->mBones[n];
std::map<std::string, cBone*>::iterator found = BonesByName.find(bone->mName.data);
if(found != BonesByName.end()){// FOUND IT!!! woohoo, make sure its not already in the bone list
bool skip = false;
for(size_t j(0); j< Bones.size(); j++){
std::string bname = bone->mName.data;
if(Bones[j]->Name == bname) {
skip = true;// already inserted, skip this so as not to insert the same bone multiple times
break;
}
}
if(!skip){// only insert the bone if it has not already been inserted
std::string tes = found->second->Name;
 
TransformMatrix(found->second->Offset, bone->mOffsetMatrix);
 
//found->second->Offset.Transpose();// transpoce their matrix to get in the correct format
XMMATRIX M = XMLoadFloat4x4(&found->second->Offset);
M = XMMatrixTranspose(M);
 
XMStoreFloat4x4(&found->second->Offset, M);
 
Bones.push_back(found->second);
 
BonesToIndex[found->first] = Bones.size()-1;
}
} 
}
}
Transforms.resize( Bones.size());
float timestep = 1.0f/30.0f;// 30 per second
for(size_t i(0); i< Animations.size(); i++){// pre calculate the animations
SetAnimIndex(i);
float dt = 0;
for(float ticks = 0; ticks < Animations[i].Duration; ticks += Animations[i].TicksPerSecond/30.0f){
dt +=timestep;
Calculate(dt);
Animations[i].Transforms.push_back(std::vector<mat4>());
std::vector<mat4>& trans = Animations[i].Transforms.back();
for( size_t a = 0; a < Transforms.size(); ++a){
XMMATRIX rotationmat =  XMLoadFloat4x4(&Bones[a]->Offset) * XMLoadFloat4x4(&Bones[a]->GlobalTransform);
mat4 rotationmat_;
XMStoreFloat4x4(&rotationmat_, rotationmat);
trans.push_back(rotationmat_);
}
}
}
 
MessageBox(0, L"Finished loading animation", 0, 0);
}
void SceneAnimator::Save(std::ofstream& file){
// first recursivly save the skeleton
if(Skeleton)
SaveSkeleton(file, Skeleton);
 
uint32_t nsize = static_cast<uint32_t>(Animations.size());
file.write(reinterpret_cast<char*>(&nsize), sizeof(uint32_t));// the number of animations 
for(uint32_t i(0); i< nsize; i++){
Animations[i].Save(file);
}
 
nsize = static_cast<uint32_t>(Bones.size());
file.write(reinterpret_cast<char*>(&nsize), sizeof(uint32_t));// the number of bones
 
for(uint32_t i(0); i< Bones.size(); i++){
nsize = static_cast<uint32_t>(Bones[i]->Name.size());
file.write(reinterpret_cast<char*>(&nsize), sizeof(uint32_t));// the size of the bone name
file.write(Bones[i]->Name.c_str(), nsize);// the name of the bone
}
 
}
void SceneAnimator::Load(std::ifstream& file){
Release();// make sure to clear this before writing new data
Skeleton = LoadSkeleton(file, NULL);
uint32_t nsize = 0;
file.read(reinterpret_cast<char*>(&nsize), sizeof(uint32_t));// the number of animations
Animations.resize(nsize);
//OUTPUT_DEBUG_MSG("Extracting Animations . . ");
for(uint32_t i(0); i< nsize; i++){
Animations[i].Load(file);
}
for(uint32_t i(0); i< Animations.size(); i++){// get all the animation names so I can reference them by name and get the correct id
AnimationNameToId.insert(std::map<std::string, uint32_t>::value_type(Animations[i].Name, i));
}
if(Animations.size() >0) CurrentAnimIndex =0;// set it to the first animation if there are any
char bname[250];
file.read(reinterpret_cast<char*>(&nsize), sizeof(uint32_t));// the number of bones
Bones.resize(nsize);
 
for(uint32_t i(0); i< Bones.size(); i++){ 
file.read(reinterpret_cast<char*>(&nsize), sizeof(uint32_t));// the size of the bone name
file.read(bname, nsize);// the size of the bone name
bname[nsize]=0;
std::map<std::string, cBone*>::iterator found = BonesByName.find(bname);
BonesToIndex[found->first] = i;
cBone* tep = found->second;
Bones[i]=tep;
}
 
Transforms.resize( Bones.size());
float timestep = 1.0f/30.0f;// 30 per second
for(size_t i(0); i< Animations.size(); i++){// pre calculate the animations
SetAnimIndex(i);
float dt = 0;
for(float ticks = 0; ticks < Animations[i].Duration; ticks += Animations[i].TicksPerSecond/30.0f){
dt +=timestep;
Calculate(dt);
Animations[i].Transforms.push_back(std::vector<mat4>());
std::vector<mat4>& trans = Animations[i].Transforms.back();
for( size_t a = 0; a < Transforms.size(); ++a){
XMMATRIX rotationmat =  XMLoadFloat4x4(&Bones[a]->Offset) * XMLoadFloat4x4(&Bones[a]->GlobalTransform);
mat4 rotationmat_;
XMStoreFloat4x4(&rotationmat_, rotationmat);
trans.push_back(rotationmat_);
}
}
}
//OUTPUT_DEBUG_MSG("Finished loading animations with "<<Bones.size()<<" bones");
}
 
void SceneAnimator::ExtractAnimations(const aiScene* pScene){
//OUTPUT_DEBUG_MSG("Extracting Animations . . ");
for(size_t i(0); i< pScene->mNumAnimations; i++){
Animations.push_back(cAnimEvaluator(pScene->mAnimations[i]) );// add the animations
}
for(uint32_t i(0); i< Animations.size(); i++){// get all the animation names so I can reference them by name and get the correct id
AnimationNameToId.insert(std::map<std::string, uint32_t>::value_type(Animations[i].Name, i));
}
CurrentAnimIndex=0;
SetAnimation("Idle");
}
bool SceneAnimator::SetAnimation(const std::string& name){
std::map<std::string, uint32_t>::iterator itr = AnimationNameToId.find(name);
int32_t oldindex = CurrentAnimIndex;
if(itr !=AnimationNameToId.end()) CurrentAnimIndex = itr->second;
return oldindex != CurrentAnimIndex;
}
bool SceneAnimator::SetAnimIndex( int32_t  pAnimIndex){
if(pAnimIndex >= Animations.size()) return false;// no change, or the animations data is out of bounds
int32_t oldindex = CurrentAnimIndex;
CurrentAnimIndex = pAnimIndex;// only set this after the checks for good data and the object was actually inserted
return oldindex != CurrentAnimIndex;
}
 
// ------------------------------------------------------------------------------------------------
// Calculates the node transformations for the scene. 
void SceneAnimator::Calculate(float pTime){
if( (CurrentAnimIndex < 0) | (CurrentAnimIndex >= Animations.size()) ) return;// invalid animation
 
Animations[CurrentAnimIndex].Evaluate( pTime, BonesByName);
UpdateTransforms(Skeleton);
}
/*
void UQTtoUDQ(vec4* dual, quat q, vec4& tran ) {
    dual[0].x = q.x ; 
    dual[0].y = q.y ; 
    dual[0].z = q.z ; 
    dual[0].w = q.w ; 
    dual[1].x = 0.5f *  ( tran[0] * q.w + tran[1] * q.z - tran[2] * q.y ) ; 
    dual[1].y = 0.5f *  (-tran[0] * q.z + tran[1] * q.w + tran[2] * q.x ) ; 
    dual[1].z = 0.5f *  ( tran[0] * q.y - tran[1] * q.x + tran[2] * q.w ) ; 
    dual[1].w = -0.5f * ( tran[0] * q.x + tran[1] * q.y + tran[2] * q.z ) ; 
}
*/
// ------------------------------------------------------------------------------------------------
// Calculates the bone matrices for the given mesh. 
void SceneAnimator::CalcBoneMatrices(){
for( size_t a = 0; a < Transforms.size(); ++a){
 
XMMATRIX M =  XMLoadFloat4x4(&Bones[a]->Offset) * XMLoadFloat4x4(&Bones[a]->GlobalTransform);
XMStoreFloat4x4(&Transforms[a], M);
/*
mat4 rotationmat = Transforms[a];
quat q;
q.frommatrix(rotationmat);
 
vec4 dual[2] ;
UQTtoUDQ( dual, q, Transforms[a].row3_v);
QuatTransforms[a].row0_v =dual[0];
QuatTransforms[a].row1_v =dual[1];
*/
}
 
}
 
// ------------------------------------------------------------------------------------------------
// Recursively creates an internal node structure matching the current scene and animation.
cBone* SceneAnimator::CreateBoneTree( aiNode* pNode, cBone* pParent){
cBone* internalNode = new cBone();// create a node
internalNode->Name = pNode->mName.data;// get the name of the bone
internalNode->Parent = pParent; //set the parent, in the case this is theroot node, it will be null
 
BonesByName[internalNode->Name] = internalNode;// use the name as a key
TransformMatrix(internalNode->LocalTransform, pNode->mTransformation);
 
//internalNode->LocalTransform.Transpose();
 
XMMATRIX M = XMLoadFloat4x4(&internalNode->LocalTransform);
M = XMMatrixTranspose(M);
 
XMStoreFloat4x4(&internalNode->LocalTransform, M);
 
internalNode->OriginalLocalTransform = internalNode->LocalTransform;// a copy saved
CalculateBoneToWorldTransform(internalNode);
 
// continue for all child nodes and assign the created internal nodes as our children
for( unsigned int a = 0; a < pNode->mNumChildren; a++){// recursivly call this function on all children
internalNode->Children.push_back(CreateBoneTree( pNode->mChildren[a], internalNode));
}
return internalNode;
}
 
// ------------------------------------------------------------------------------------------------
// Recursively updates the internal node transformations from the given matrix array
void SceneAnimator::UpdateTransforms(cBone* pNode) {
CalculateBoneToWorldTransform( pNode);// update global transform as well
for( std::vector<cBone*>::iterator it = pNode->Children.begin(); it != pNode->Children.end(); ++it)// continue for all children
UpdateTransforms( *it);
}
 
// ------------------------------------------------------------------------------------------------
// Calculates the global transformation matrix for the given internal node
void SceneAnimator::CalculateBoneToWorldTransform(cBone* child){
child->GlobalTransform = child->LocalTransform;
cBone* parent = child->Parent;
while( parent )
{// this will climb the nodes up along through the parents concentating all the matrices to get the Object to World transform, or in this case, the Bone To World transform
 
//child->GlobalTransform *= parent->LocalTransform;
//parent  = parent->Parent;// get the parent of the bone we are working on 
 
XMMATRIX Child = XMLoadFloat4x4(&child->GlobalTransform);
XMMATRIX Parent = XMLoadFloat4x4(&parent->LocalTransform);
 
Child *= Parent;
 
XMStoreFloat4x4(&child->GlobalTransform, Child);
 
parent = parent->Parent;
 
}
}
 
 
void SceneAnimator::SaveSkeleton(std::ofstream& file, cBone* parent){
uint32_t nsize = static_cast<uint32_t>(parent->Name.size());
file.write(reinterpret_cast<char*>(&nsize), sizeof(uint32_t));// the number of chars
file.write(parent->Name.c_str(), nsize);// the name of the bone
file.write(reinterpret_cast<char*>(&parent->Offset), sizeof(parent->Offset));// the bone offsets
file.write(reinterpret_cast<char*>(&parent->OriginalLocalTransform), sizeof(parent->OriginalLocalTransform));// original bind pose
nsize = static_cast<uint32_t>(parent->Children.size());// number of children
file.write(reinterpret_cast<char*>(&nsize), sizeof(uint32_t));// the number of children
for( std::vector<cBone*>::iterator it = parent->Children.begin(); it != parent->Children.end(); ++it)// continue for all children
SaveSkeleton(file, *it); 
}
cBone* SceneAnimator::LoadSkeleton(std::ifstream& file, cBone* parent){
cBone* internalNode = new cBone();// create a node
internalNode->Parent = parent; //set the parent, in the case this is theroot node, it will be null
uint32_t nsize=0;
file.read(reinterpret_cast<char*>(&nsize), sizeof(uint32_t));// the number of chars
char temp[250];
file.read(temp, nsize);// the name of the bone
temp[nsize]=0;
internalNode->Name = temp;
BonesByName[internalNode->Name] = internalNode;// use the name as a key
file.read(reinterpret_cast<char*>(&internalNode->Offset), sizeof(internalNode->Offset));// the bone offsets
file.read(reinterpret_cast<char*>(&internalNode->OriginalLocalTransform), sizeof(internalNode->OriginalLocalTransform));// original bind pose
 
internalNode->LocalTransform = internalNode->OriginalLocalTransform;// a copy saved
CalculateBoneToWorldTransform(internalNode);
 
file.read(reinterpret_cast<char*>(&nsize), sizeof(uint32_t));// the number of children
 
// continue for all child nodes and assign the created internal nodes as our children
for( unsigned int a = 0; a < nsize && file; a++){// recursivly call this function on all children
internalNode->Children.push_back(LoadSkeleton(file, internalNode));
}
return internalNode;
 
}

SkinnedModel.h


#ifndef _SKINNED_MODEL_H_
#define _SKINNED_MODEL_H_
 
class SkinnedModel
{
public:
struct InitInfo
{
TextureMgr* Mgr;
XMFLOAT3 Scale;
 
bool UseDefaultMaterial;
Material Material;
};
 
public:
SkinnedModel(const std::string& modelpath, InitInfo& info);
~SkinnedModel();
 
void Render(CXMMATRIX World, CXMMATRIX ViewProj);
void Update(float dt);
 
private:
void LoadSkinnedModel16(const std::string& modelpath);
void LoadTextures(aiMaterial* Mat);
void LoadMaterials(aiMaterial* Mat);
private:
DirectionalLight Lights[3];
PointLight PointLights[3];
 
float TimePos;
 
InitInfo mInfo;
ModelData mModel;
SceneAnimator mSceneAnimator;
 
std::vector<Material> Materials;
 
std::vector<ID3D11ShaderResourceView*> DiffuseMapSRV;
std::vector<ID3D11ShaderResourceView*> NormalMapSRV;
 
 
std::vector<XMFLOAT4X4> FinalTransforms;
};
 
 
 
 
#endif

SkinnedModel.cpp


#include "stdafx.h"
 
//=================================================
// Assimp Skinned Model Loader by newtechnology
// This Assimp Skinned Model Loader uses Scot lee's animation code for loading skinned models
// Status : Being developed (not completed)
//=================================================
 
SkinnedModel::SkinnedModel(const std::string& modelpath, InitInfo& info)
{
mInfo = info;
 
 
Lights[0].Ambient  = XMFLOAT4(0.5f, 0.5f, 0.5f, 1.0f);
Lights[0].Diffuse  = XMFLOAT4(0.7f, 0.7f, 0.7f, 1.0f);
Lights[0].Specular = XMFLOAT4(0.2f, 0.2f, 0.2f, 1.0f);
Lights[0].Direction = XMFLOAT3(-1.0f, -1.0f, 0.0f);
 
XMFLOAT3 pos[3];
 
for (int i = 0; i < 3; ++i)
{
pos[i].x = float(rand() % 100);
pos[i].y = float(rand() % 100);
pos[i].z = float(rand() % 100);
}
 
PointLights[0].Ambient = XMFLOAT4(0.2f, 0.2f, 0.2f, 1.0f);
PointLights[0].Diffuse = XMFLOAT4(0.3f, 0.3f, 0.3f, 1.0f);
PointLights[0].Specular = XMFLOAT4(0.1f, 0.1f, 0.1f, 1.0f);
PointLights[0].Range = 25.0f;
PointLights[0].Att   = XMFLOAT3(0.0f, 0.1f, 0.0f);
PointLights[0].Position = pos[0];
 
PointLights[1].Ambient = XMFLOAT4(0.2f, 0.2f, 0.2f, 1.0f);
PointLights[1].Diffuse = XMFLOAT4(0.2f, 0.2f, 0.2f, 1.0f);
PointLights[1].Specular = XMFLOAT4(0.1f, 0.1f, 0.1f, 1.0f);
PointLights[1].Range = 40.0f;
PointLights[1].Att   = XMFLOAT3(0.0f, 0.1f, 0.0f);
PointLights[1].Position = pos[1];
 
PointLights[2].Ambient = XMFLOAT4(0.3f, 0.3f, 0.3f, 1.0f);
PointLights[2].Diffuse = XMFLOAT4(0.4f, 0.4f, 0.4f, 1.0f);
PointLights[2].Specular = XMFLOAT4(0.1f, 0.1f, 0.1f, 1.0f);
PointLights[2].Range = 60.0f;
PointLights[2].Att   = XMFLOAT3(0.0f, 0.1f, 0.0f);
PointLights[2].Position = pos[2];
 
LoadSkinnedModel16(modelpath);
}
 
SkinnedModel::~SkinnedModel()
{
 
}
 
void SkinnedModel::LoadMaterials(aiMaterial* Mat)
{
Material tempMat;
 
    aiColor4D color(0.0f, 0.0f, 0.0f, 0.0f);
 
    tempMat.Ambient = XMFLOAT4(0.0f, 0.0f, 0.0f, 0.0f);
tempMat.Diffuse = XMFLOAT4(0.0f, 0.0f, 0.0f, 0.0f);
    tempMat.Specular = XMFLOAT4(0.0f, 0.0f, 0.0f, 0.0f);
    tempMat.Reflect = XMFLOAT4(0.0f, 0.0f, 0.0f, 0.0f);
 
    Mat->Get(AI_MATKEY_COLOR_AMBIENT, color);
 
    tempMat.Ambient = XMFLOAT4(color.r, color.g, color.b, color.a);
 
    Mat->Get(AI_MATKEY_COLOR_DIFFUSE, color);
 
    tempMat.Diffuse = XMFLOAT4(color.r, color.g, color.b, color.a);
 
    Mat->Get(AI_MATKEY_COLOR_SPECULAR, color);
 
    tempMat.Specular = XMFLOAT4(color.r, color.g, color.b, color.a);
 
    Mat->Get(AI_MATKEY_COLOR_REFLECTIVE, color);
 
    tempMat.Reflect = XMFLOAT4(color.r, color.g, color.b, color.a);
 
if (tempMat.Ambient.x == 0 && tempMat.Ambient.y == 0 && tempMat.Ambient.z == 0 && tempMat.Ambient.w == 0)
tempMat.Ambient = XMFLOAT4(0.5f, 0.5f, 0.5f, 1.0f);
 
    if (tempMat.Diffuse.x == 0 && tempMat.Diffuse.y == 0 && tempMat.Diffuse.z == 0 && tempMat.Diffuse.w == 0)
tempMat.Diffuse = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
 
    if (tempMat.Specular.x == 0 && tempMat.Specular.y == 0 && tempMat.Specular.z == 0 && tempMat.Specular.w == 0)
tempMat.Specular = XMFLOAT4(0.6f, 0.6f, 0.6f, 16.0f);
 
   Materials.push_back(tempMat);
}
 
void SkinnedModel::LoadTextures(aiMaterial* Mat)
{
aiString path;
 
if (Mat->GetTextureCount(aiTextureType_DIFFUSE) > 0 && Mat->GetTexture(aiTextureType_DIFFUSE, 0, &path) == AI_SUCCESS)
{
 
std::string file = path.C_Str();
std::string NoExtension = removeExtension(file);
   std::string newName = NoExtension + ".dds";
   newName = "Resources\\Textures\\" + newName;
 
#if defined (DEBUG) || (_DEBUG)
OutputDebugStringA(newName.c_str());
OutputDebugStringA("\n");
 
if (!FileExists(newName))
ShowError(newName.c_str());
#endif
 
ID3D11ShaderResourceView* srv = mInfo.Mgr->CreateTexture(newName);
 
   DiffuseMapSRV.push_back(srv);
 
}
else
{
ID3D11ShaderResourceView* srv = nullptr;
 
DiffuseMapSRV.push_back(srv);
NormalMapSRV.push_back(srv);
 
return;
}
 
std::string file = path.C_Str();
std::string NoExtension = removeExtension(file);
 
std::string NormalMapTexturePath = NoExtension + "_NRM"; //add normal map prefix
NormalMapTexturePath = NormalMapTexturePath + ".dds"; //add extension
 
NormalMapTexturePath = "Resources\\Textures\\" + NormalMapTexturePath;
 
#if defined(DEBUG) || (_DEBUG)
if (!FileExists(NormalMapTexturePath))
ShowError("A Normal map is missing.");
#endif
 
ID3D11ShaderResourceView* srv = mInfo.Mgr->CreateTexture(NormalMapTexturePath);
NormalMapSRV.push_back(srv);
}
 
void SkinnedModel::LoadSkinnedModel16(const std::string& path)
{
Assimp::Importer imp;
 
std::vector<Vertex::PosNormalTexTanSkinned> vertices;
std::vector<USHORT> indices;
std::vector<Subset> Subsets;
 
const aiScene* pScene = imp.ReadFile(path, aiProcessPreset_TargetRealtime_Quality | aiProcess_ConvertToLeftHanded );
 
if (pScene == NULL)
ShowError(imp.GetErrorString());
 
if (mInfo.Mgr == nullptr)
ShowError("No Address found in pointer mInfo::Mgr");
 
mSceneAnimator.Init(pScene);
 
for (UINT i = 0; i < pScene->mNumMeshes; ++i)
{
aiMesh* mesh = pScene->mMeshes[i];
 
Subset subset;
 
subset.VertexCount = mesh->mNumVertices;
subset.VertexStart = vertices.size();
subset.FaceStart = indices.size() / 3;
subset.FaceCount = mesh->mNumFaces;
subset.ID = mesh->mMaterialIndex;
 
mModel.mNumFaces += subset.FaceCount;
mModel.mNumVertices += subset.VertexCount;
 
std::vector<std::vector<aiVertexWeight>> WeightsPerVertex(mesh->mNumVertices);
 
for (UINT j = 0; j < mesh->mNumBones; ++j)
{
int BoneIndex = mSceneAnimator.GetBoneIndex(mesh->mBones[j]->mName.C_Str());
 
if (BoneIndex == -1)
ShowError("Error BoneIndex");
 
for (UINT k = 0; k < mesh->mBones[j]->mNumWeights; ++k)
{
aiVertexWeight VertexWeight =  mesh->mBones[j]->mWeights[k];
WeightsPerVertex[VertexWeight.mVertexId].push_back(aiVertexWeight(BoneIndex, VertexWeight.mWeight));
}
}
 
for (UINT j = 0; j < subset.VertexCount; ++j)
{
Vertex::PosNormalTexTanSkinned vertex;
 
vertex.Pos.x = mesh->mVertices[j].x;
vertex.Pos.y = mesh->mVertices[j].y;
vertex.Pos.z = mesh->mVertices[j].z;
 
vertex.TangentU.x = mesh->mTangents[j].x;
vertex.TangentU.y = mesh->mTangents[j].y;
vertex.TangentU.z = mesh->mTangents[j].z;
 
vertex.Normal.x = mesh->mNormals[j].x;
vertex.Normal.y = mesh->mNormals[j].y;
vertex.Normal.z = mesh->mNormals[j].z;
 
if (mesh->HasTextureCoords(0))
{
  vertex.Tex.x = mesh->mTextureCoords[0][j].x;
  vertex.Tex.y =  mesh->mTextureCoords[0][j].y;
}
 
for (UINT k = 0; k < WeightsPerVertex[j].size(); ++k)
{
vertex.BoneIndices[k] = WeightsPerVertex[j][k].mVertexId;
 
vertex.Weights.x = 0.0f;
vertex.Weights.y = 0.0f;
vertex.Weights.z = 0.0f;
 
vertex.Weights.x = WeightsPerVertex[j][0].mWeight;
vertex.Weights.y = WeightsPerVertex[j][1].mWeight;
vertex.Weights.z = WeightsPerVertex[j][2].mWeight;
}
 
vertices.push_back(vertex);
}
 
for (UINT j = 0; j < subset.FaceCount; ++j)
{
for (UINT index = 0; index < mesh->mFaces[j].mNumIndices; ++index)
{
indices.push_back(subset.VertexStart + mesh->mFaces[j].mIndices[index]);
}
}
 
aiMaterial* Mat = pScene->mMaterials[subset.ID];
 
if (mInfo.UseDefaultMaterial)
   { 
LoadMaterials(Mat);
}
else
{
Materials.push_back(mInfo.Material);
}
 
LoadTextures(Mat);
 
Subsets.push_back(subset);
}
 
mModel.mSubsetCount = Subsets.size();
 
mModel.Mesh.SetSubsetTable(Subsets);
mModel.Mesh.SetVertices(&vertices[0], vertices.size());
mModel.Mesh.SetIndices(&indices[0], indices.size());
 
}
 
 
void SkinnedModel::Render(CXMMATRIX World, CXMMATRIX ViewProj)
{
 
ID3DX11EffectTechnique* activeTech = Effects::NormalMapFX->Light1TexSkinnedTech;
 
pDeviceContext->IASetInputLayout(InputLayouts::PosNormalTexTanSkinned);
pDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); 
 
XMMATRIX W = World;
XMMATRIX worldInvTranspose = MathHelper::InverseTranspose(W);
XMMATRIX WorldViewProj = W * ViewProj;
XMMATRIX TexTransform = XMMatrixIdentity();
XMMATRIX ShadowTransform = W * XMLoadFloat4x4(&d3d->m_ShadowTransform);
 
 
Effects::NormalMapFX->SetEyePosW(d3d->m_Cam.GetPosition());
Effects::NormalMapFX->SetDirLights(Lights);
Effects::NormalMapFX->SetPointLights(PointLights);
Effects::NormalMapFX->SetShadowMap(d3d->GetShadowMap());
 
Effects::NormalMapFX->SetWorld(W);
Effects::NormalMapFX->SetWorldInvTranspose(worldInvTranspose);
Effects::NormalMapFX->SetWorldViewProj(WorldViewProj);
Effects::NormalMapFX->SetTexTransform(TexTransform);
Effects::NormalMapFX->SetShadowTransform(ShadowTransform);
Effects::NormalMapFX->SetBoneTransforms(&FinalTransforms[0], FinalTransforms.size()); 
 
 
D3DX11_TECHNIQUE_DESC techDesc;
    activeTech->GetDesc(&techDesc);
 
float blendFactor[4] = {0.0f, 0.0f, 0.0f, 0.0f};
 
    for(UINT p = 0; p < techDesc.Passes; ++p)
    {
for (UINT i = 0; i < mModel.mSubsetCount; ++i)
   {   
 
if (DiffuseMapSRV[i] == nullptr)
continue;
 
   Effects::NormalMapFX->SetMaterial(Materials[i]);
Effects::NormalMapFX->SetDiffuseMap(DiffuseMapSRV[i]);
Effects::NormalMapFX->SetNormalMap(NormalMapSRV[i]);
 
   activeTech->GetPassByIndex(p)->Apply(0, pDeviceContext);
 
   mModel.Mesh.Draw(i);
 
   }
}
 
}
 
void SkinnedModel::Update(float dt)
{
TimePos += dt;
 
 
mSceneAnimator.SetAnimIndex(0);
 
FinalTransforms = mSceneAnimator.GetTransforms(dt);
 
if (dt > 5.0f)
dt = 0.0f;
}
Screenshot:
hLkTI9v.png
Any help is appreciated.
I think the problem is with extracting weights and bone Indices.
Advertisement

That's an awful lot of code.

Could you give us a screenshot of the result, even though it doesnt give any sence? And how do you use the cAnimationController, I personally tried it myself, and you'll have to communicate with it which Im sure that you have, we just need to see how you communicate with this class.

EDIT: I see you added the screenshot, thank you.

This is just to help us reach your goal, as I doubt a lot of people would go through all that code.

-MIGI0027

FastCall22: "I want to make the distinction that my laptop is a whore-box that connects to different network"

Blog about... stuff (GDNet, WordPress): www.gamedev.net/blog/1882-the-cuboid-zone/, cuboidzone.wordpress.com/

That's an awful lot of code.

Could you give us a screenshot of the result, even though it doesnt give any sence? And how do you use the cAnimationController, I personally tried it myself, and you'll have to communicate with it which Im sure that you have, we just need to see how you communicate with this class.

This is just to help us reach your goal, as I doubt a lot of people would go through all that code.

-MIGI0027

Added screenshot. (You were just too early, I was going to add one screenshot.)

Test it by rigging and exporting one triangle so you can go through the data by hand in the debugger. Best option in this case.

The Tiny Model renders fine (without any animations) if I set all vertex weights to 0.

In that case this could mean the data from the other mesh is bad. E.g. does it load fine with assimp viewer ?

In that case this could mean the data from the other mesh is bad. E.g. does it load fine with assimp viewer ?

No. I think the problem is the method by which I extract Bone Indices and Bone weights. Look at SkinnedMesh.cpp for the loading code. And yes, it loads fine in assimp viewer.

I also discovered that sum of weights are greater than 1. This means that there is some problem with extracting weights and bone indices.

I don't think you understood (or answered) unbird's question. Have you tried opening the file with the assimp viewer? I.e., not your own code, but the viewer than comes with the assimp download. Confirming that the file data is good and can be displayed correctly should be your first priority.

You don't mention what type of file you're loading. If it's in text format in its original form (or you can transform it to text), I would suggest you follow your code, step-by-step, confirming that the data you read in and store is reasonable by comparing it with the file itself.

Regarding bone weights, you can add checks for the number of bone indices and the sum of the weights for each vertex as you load it and popup a messagebox or output debug info if something isn't appropriate. E.g., report if the number of bone indices is greater than your shader expects or will handle. Does every bone-weights-sum for every vertex = 1? etc.

In addition, after the bone indices/weights are loaded for a vertex, you can sort them by weight, throw away those in excess of the number you want, and renormalize the remaining weights so they always sum to one.

You can ensure a better display: if, for example, your shader will handle 4 indices/weights, then, in the shader code, use only the first 3 weights and set the 4th weight to 1.0 - weight0 - weight1 - weight2.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

I don't think you understood (or answered) unbird's question. Have you tried opening the file with the assimp viewer? I.e., not your own code, but the viewer than comes with the assimp download. Confirming that the file data is good and can be displayed correctly should be your first priority.

You don't mention what type of file you're loading. If it's in text format in its original form (or you can transform it to text), I would suggest you follow your code, step-by-step, confirming that the data you read in and store is reasonable by comparing it with the file itself.

Actually I understood. I already said it works fine in assimp viewer and the file type is .x and its in text format.

This topic is closed to new replies.

Advertisement