Sign in to follow this  
newtechnology

DX11 Assimp Distorted Skinned Mesh

Recommended Posts

newtechnology    1955

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.
Edited by newtechnology

Share this post


Link to post
Share on other sites
Migi0027    4628

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

Edited by Migi0027

Share this post


Link to post
Share on other sites
newtechnology    1955

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.)

Share this post


Link to post
Share on other sites
n3Xus    951

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.

Edited by n3Xus

Share this post


Link to post
Share on other sites
newtechnology    1955

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.

Edited by newtechnology

Share this post


Link to post
Share on other sites
Buckeye    10747

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.

Edited by Buckeye

Share this post


Link to post
Share on other sites
newtechnology    1955

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.

Share this post


Link to post
Share on other sites
newtechnology    1955

OH YES! I got it working. It was because I was scaling model at the time of loading (multiplying vertex position with scaling factors.) instead of using matrix for scaling.

 

Thank you all for your help. 

Share this post


Link to post
Share on other sites
Buckeye    10747


I already said it works fine in assimp viewer


Editing a previous post and then claiming you already answered the question is considered poor style.

newtechnology
Posted Yesterday, 03:09 AM
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.

newtechnology
Posted Yesterday, 03:54 AM
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.
 

Share this post


Link to post
Share on other sites
newtechnology    1955

 


I already said it works fine in assimp viewer


Editing a previous post and then claiming you already answered the question is considered poor style.

newtechnology
Posted Yesterday, 03:09 AM
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.

newtechnology
Posted Yesterday, 03:54 AM
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 think you need glasses.

 

newtechnology

 

Posted Yesterday, 07:08 AM -> DO NOT MISS THIS

 

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.

 

 

Buckeye

 

Posted Yesterday, 06:09 PM -> DO NOT MISS THIS

 

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.

Edited by newtechnology

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this  

  • Similar Content

    • By isu diss
       I'm trying to code Rayleigh part of Nishita's model (Display Method of the Sky Color Taking into Account Multiple Scattering). I get black screen no colors. Can anyone find the issue for me?
       
      #define InnerRadius 6320000 #define OutterRadius 6420000 #define PI 3.141592653 #define Isteps 20 #define Ksteps 10 static float3 RayleighCoeffs = float3(6.55e-6, 1.73e-5, 2.30e-5); RWTexture2D<float4> SkyColors : register (u0); cbuffer CSCONSTANTBUF : register( b0 ) { float fHeight; float3 vSunDir; } float Density(float Height) { return exp(-Height/8340); } float RaySphereIntersection(float3 RayOrigin, float3 RayDirection, float3 SphereOrigin, float Radius) { float t1, t0; float3 L = SphereOrigin - RayOrigin; float tCA = dot(L, RayDirection); if (tCA < 0) return -1; float lenL = length(L); float D2 = (lenL*lenL) - (tCA*tCA); float Radius2 = (Radius*Radius); if (D2<=Radius2) { float tHC = sqrt(Radius2 - D2); t0 = tCA-tHC; t1 = tCA+tHC; } else return -1; return t1; } float RayleighPhaseFunction(float cosTheta) { return ((3/(16*PI))*(1+cosTheta*cosTheta)); } float OpticalDepth(float3 StartPosition, float3 EndPosition) { float3 Direction = normalize(EndPosition - StartPosition); float RayLength = RaySphereIntersection(StartPosition, Direction, float3(0, 0, 0), OutterRadius); float SampleLength = RayLength / Isteps; float3 tmpPos = StartPosition + 0.5 * SampleLength * Direction; float tmp; for (int i=0; i<Isteps; i++) { tmp += Density(length(tmpPos)-InnerRadius); tmpPos += SampleLength * Direction; } return tmp*SampleLength; } static float fExposure = -2; float3 HDR( float3 LDR) { return 1.0f - exp( fExposure * LDR ); } [numthreads(32, 32, 1)] //disptach 8, 8, 1 it's 256 by 256 image void ComputeSky(uint3 DTID : SV_DispatchThreadID) { float X = ((2 * DTID.x) / 255) - 1; float Y = 1 - ((2 * DTID.y) / 255); float r = sqrt(((X*X)+(Y*Y))); float Theta = r * (PI); float Phi = atan2(Y, X); static float3 Eye = float3(0, 10, 0); float ViewOD = 0, SunOD = 0, tmpDensity = 0; float3 Attenuation = 0, tmp = 0, Irgb = 0; //if (r<=1) { float3 ViewDir = normalize(float3(sin(Theta)*cos(Phi), cos(Theta),sin(Theta)*sin(Phi) )); float ViewRayLength = RaySphereIntersection(Eye, ViewDir, float3(0, 0, 0), OutterRadius); float SampleLength = ViewRayLength / Ksteps; //vSunDir = normalize(vSunDir); float cosTheta = dot(normalize(vSunDir), ViewDir); float3 tmpPos = Eye + 0.5 * SampleLength * ViewDir; for(int k=0; k<Ksteps; k++) { float SunRayLength = RaySphereIntersection(tmpPos, vSunDir, float3(0, 0, 0), OutterRadius); float3 TopAtmosphere = tmpPos + SunRayLength*vSunDir; ViewOD = OpticalDepth(Eye, tmpPos); SunOD = OpticalDepth(tmpPos, TopAtmosphere); tmpDensity = Density(length(tmpPos)-InnerRadius); Attenuation = exp(-RayleighCoeffs*(ViewOD+SunOD)); tmp += tmpDensity*Attenuation; tmpPos += SampleLength * ViewDir; } Irgb = RayleighCoeffs*RayleighPhaseFunction(cosTheta)*tmp*SampleLength; SkyColors[DTID.xy] = float4(Irgb, 1); } }  
    • By Endurion
      I have a gaming framework with an renderer interface. Those support DX8, DX9 and latest, DX11. Both DX8 and DX9 use fixed function pipeline, while DX11 obviously uses shaders. I've got most of the parts working fine, as in I can switch renderers and notice almost no difference. The most advanced features are 2 directional lights with a single texture  
      My last problem is lighting; albeit there's documentation on the D3D lighting model I still can't get the behaviour right. My mistake shows most prominently in the dark side opposite the lights. I'm pretty sure the ambient calculation is off, but that one's supposed to be the most simple one and should be hard to get wrong.
      Interestingly I've been searching high and low, and have yet to find a resource that shows how to build a HLSL shader where diffuse, ambient and specular are used together with material properties. I've got various shaders for all the variations I'm supporting. I stepped through the shader with the graphics debugger, but the calculation seems to do what I want. I'm just not sure the formula is correct.
      This one should suffice though, it's doing two directional lights, texture modulated with vertex color and a normal. Maybe someone can spot one (or more mistakes). And yes, this is in the vertex shader and I'm aware lighting will be as "bad" as in fixed function; that's my goal currently.
      // A constant buffer that stores the three basic column-major matrices for composing geometry. cbuffer ModelViewProjectionConstantBuffer : register(b0) { matrix model; matrix view; matrix projection; matrix ortho2d; }; struct DirectionLight { float3 Direction; float PaddingL1; float4 Ambient; float4 Diffuse; float4 Specular; }; cbuffer LightsConstantBuffer : register( b1 ) { float4 Ambient; float3 EyePos; float PaddingLC1; DirectionLight Light[8]; }; struct Material { float4 MaterialEmissive; float4 MaterialAmbient; float4 MaterialDiffuse; float4 MaterialSpecular; float MaterialSpecularPower; float3 MaterialPadding; }; cbuffer MaterialConstantBuffer : register( b2 ) { Material _Material; }; // Per-vertex data used as input to the vertex shader. struct VertexShaderInput { float3 pos : POSITION; float3 normal : NORMAL; float4 color : COLOR0; float2 tex : TEXCOORD0; }; // Per-pixel color data passed through the pixel shader. struct PixelShaderInput { float4 pos : SV_POSITION; float2 tex : TEXCOORD0; float4 color : COLOR0; }; // Simple shader to do vertex processing on the GPU. PixelShaderInput main(VertexShaderInput input) { PixelShaderInput output; float4 pos = float4( input.pos, 1.0f ); // Transform the vertex position into projected space. pos = mul(pos, model); pos = mul(pos, view); pos = mul(pos, projection); output.pos = pos; // pass texture coords output.tex = input.tex; // Calculate the normal vector against the world matrix only. //set required lighting vectors for interpolation float3 normal = mul( input.normal, ( float3x3 )model ); normal = normalize( normal ); float4 ambientEffect = Ambient; float4 diffuseEffect = float4( 0, 0, 0, 0 ); float4 specularEffect = float4( 0, 0, 0, 0 ); for ( int i = 0; i < 2; ++i ) { // Invert the light direction for calculations. float3 lightDir = -Light[i].Direction; float lightFactor = max( dot( lightDir, input.normal ), 0 ); ambientEffect += Light[i].Ambient * _Material.MaterialAmbient; diffuseEffect += saturate( Light[i].Diffuse * dot( normal, lightDir ) );// * _Material.MaterialDiffuse; //specularEffect += Light[i].Specular * dot( normal, halfangletolight ) * _Material.MaterialSpecularPower; } specularEffect *= _Material.MaterialSpecular; //ambientEffect.w = 1.0; ambientEffect = normalize( ambientEffect ); /* Ambient effect: (L1.ambient + L2.ambient) * object ambient color Diffuse effect: (L1.diffuse * Dot(VertexNormal, Light1.Direction) + L2.diffuse * Dot(VertexNormal, Light2.Direction)) * object diffuse color Specular effect: (L1.specular * Dot(VertexNormal, HalfAngleToLight1) * Object specular reflection power + L2.specular * Dot(VertexNormal, HalfAngleToLight2) * Object specular reflection power ) * object specular color Resulting color = Ambient effect + diffuse effect + specular effect*/ float4 totalFactor = ambientEffect + diffuseEffect + specularEffect; totalFactor.w = 1.0; output.color = input.color * totalFactor; return output; }   Edit: This message editor is driving me nuts (Arrrr!) - I don't write code in Word.
    • By Mercesa
      Hey folks. So I'm having this problem in which if my camera is close to a surface, the SSAO pass suddenly spikes up to around taking 16 milliseconds.
      When still looking towards the same surface, but less close. The framerate resolves itself and becomes regular again.
      This happens with ANY surface of my model, I am a bit clueless in regards to what could cause this. Any ideas?
      In attached image: y axis is time in ms, x axis is current frame. The dips in SSAO milliseconds are when I moved away from the surface, the peaks happen when I am very close to the surface.

       
      Edit: So I've done some more in-depth profiling with Nvidia nsight. So these are the facts from my results
      Count of command buffers goes from 4 (far away from surface) to ~20(close to surface).
      The command buffer duration in % goes from around ~30% to ~99%
      Sometimes the CPU duration takes up to 0.03 to 0.016 milliseconds per frame while comparatively usually it takes around 0.002 milliseconds.
      I am using a vertex shader which generates my full-screen quad and afterwards I do my SSAO calculations in my pixel shader, could this be a GPU driver bug? I'm a bit lost myself. It seems there could be a CPU/GPU resource stall. But why would the amount of command buffers be variable depending on distance from a surface?
       
       
      Edit n2: Any resolution above 720p starts to have this issue, and I am fairly certain my SSAO is not that performance heavy it would crap itself at a bit higher resolutions.
       
    • By turanszkij
      In DirectX 11 we have a 24 bit integer depth + 8bit stencil format for depth-stencil resources ( DXGI_FORMAT_D24_UNORM_S8_UINT ). However, in an AMD GPU documentation for consoles I have seen they mentioned, that internally this format is implemented as a 64 bit resource with 32 bits for depth (but just truncated for 24 bits) and 32 bits for stencil (truncated to 8 bits). AMD recommends using a 32 bit floating point depth buffer instead with 8 bit stencil which is this format: DXGI_FORMAT_D32_FLOAT_S8X24_UINT.
      Does anyone know why this is? What is the usual way of doing this, just follow the recommendation and use a 64 bit depthstencil? Are there performance considerations or is it just recommended to not waste memory? What about Nvidia and Intel, is using a 24 bit depthbuffer relevant on their hardware?
      Cheers!
       
    • By gsc
      Hi! I am trying to implement simple SSAO postprocess. The main source of my knowledge on this topic is that awesome tutorial.
      But unfortunately something doesn't work... And after a few long hours I need some help. Here is my hlsl shader:
      float3 randVec = _noise * 2.0f - 1.0f; // noise: vec: {[0;1], [0;1], 0} float3 tangent = normalize(randVec - normalVS * dot(randVec, normalVS)); float3 bitangent = cross(tangent, normalVS); float3x3 TBN = float3x3(tangent, bitangent, normalVS); float occlusion = 0.0; for (int i = 0; i < kernelSize; ++i) { float3 samplePos = samples[i].xyz; // samples: {[-1;1], [-1;1], [0;1]} samplePos = mul(samplePos, TBN); samplePos = positionVS.xyz + samplePos * ssaoRadius; float4 offset = float4(samplePos, 1.0f); offset = mul(offset, projectionMatrix); offset.xy /= offset.w; offset.y = -offset.y; offset.xy = offset.xy * 0.5f + 0.5f; float sampleDepth = tex_4.Sample(textureSampler, offset.xy).a; sampleDepth = vsPosFromDepth(sampleDepth, offset.xy).z; const float threshold = 0.025f; float rangeCheck = abs(positionVS.z - sampleDepth) < ssaoRadius ? 1.0 : 0.0; occlusion += (sampleDepth <= samplePos.z + threshold ? 1.0 : 0.0) * rangeCheck; } occlusion = saturate(1 - (occlusion / kernelSize)); And current result: http://imgur.com/UX2X1fc
      I will really appreciate for any advice!
  • Popular Now