Recently, trying to use FBX sdk to import a 3d model made with 3dmax, i almost immediately got in trouble with transformations. A very simple mesh(a sphere split in two halves) consisting of two nodes has one of it's nodes offset no matter what. I tried several(quite ambiguous) ways of calculating transform latest SDK documentation provides... But result is the same. I'll provide the code and the mesh in case anyone can point out any mistakes.
Helper Functions:
FbxAMatrix MeshManager::GetGlobalPosition(FbxNode* pNode, const FbxTime& pTime, FbxPose* pPose, FbxAMatrix* pParentGlobalPosition)
{
FbxAMatrix lGlobalPosition;
bool lPositionFound = false;
if (pPose)
{
int lNodeIndex = pPose->Find(pNode);
if (lNodeIndex > -1)
{
// The bind pose is always a global matrix.
// If we have a rest pose, we need to check if it is
// stored in global or local space.
if (pPose->IsBindPose() || !pPose->IsLocalMatrix(lNodeIndex))
{
lGlobalPosition = GetPoseMatrix(pPose, lNodeIndex);
}
else
{
// We have a local matrix, we need to convert it to
// a global space matrix.
FbxAMatrix lParentGlobalPosition;
if (pParentGlobalPosition)
{
lParentGlobalPosition = *pParentGlobalPosition;
}
else
{
if (pNode->GetParent())
{
lParentGlobalPosition = GetGlobalPosition(pNode->GetParent(), pTime, pPose);
}
}
FbxAMatrix lLocalPosition = GetPoseMatrix(pPose, lNodeIndex);
lGlobalPosition = lParentGlobalPosition * lLocalPosition;
}
lPositionFound = true;
}
}
if (!lPositionFound)
{
// There is no pose entry for that node, get the current global position instead.
// Ideally this would use parent global position and local position to compute the global position.
// Unfortunately the equation
// lGlobalPosition = pParentGlobalPosition * lLocalPosition
// does not hold when inheritance type is other than "Parent" (RSrs).
// To compute the parent rotation and scaling is tricky in the RrSs and Rrs cases.
lGlobalPosition = pNode->EvaluateGlobalTransform(pTime);
}
return lGlobalPosition;
}
// Get the matrix of the given pose
FbxAMatrix MeshManager::GetPoseMatrix(FbxPose* pPose, int pNodeIndex)
{
FbxAMatrix lPoseMatrix;
FbxMatrix lMatrix = pPose->GetMatrix(pNodeIndex);
memcpy((double*)lPoseMatrix, (double*)lMatrix, sizeof(lMatrix.mData));
return lPoseMatrix;
}
// Get the geometry offset to a node. It is never inherited by the children.
FbxAMatrix MeshManager::GetGeometry(FbxNode* pNode)
{
const FbxVector4 lT = pNode->GetGeometricTranslation(FbxNode::eSourcePivot);
const FbxVector4 lR = pNode->GetGeometricRotation(FbxNode::eSourcePivot);
const FbxVector4 lS = pNode->GetGeometricScaling(FbxNode::eSourcePivot);
return FbxAMatrix(lT, lR, lS);
}
mat4 FbxMatToGlm(const FbxAMatrix& mat) {
dvec4 c0 = glm::make_vec4((double*)mat.GetColumn(0).Buffer());
dvec4 c1 = glm::make_vec4((double*)mat.GetColumn(1).Buffer());
dvec4 c2 = glm::make_vec4((double*)mat.GetColumn(2).Buffer());
dvec4 c3 = glm::make_vec4((double*)mat.GetColumn(3).Buffer());
glm::mat4 convertMatr = mat4(c0, c1, c2, c3);
return inverse(convertMatr);
}
Mesh Extraction:
void MeshManager::extractMeshRecursive(FbxScene* mScene, FbxNode* pNode, FbxAMatrix& pParentGlobalPosition, shared_ptr<Mesh> mesh, unsigned ¤tNode) {
// Find out what type of node this is
FbxNodeAttribute* lNodeAttribute = pNode->GetNodeAttribute();
FbxAMatrix lGlobalPosition = GetGlobalPosition(pNode, 1, mScene->GetPose(-1) , &pParentGlobalPosition);
FbxAMatrix lGeometryOffset = GetGeometry(pNode);
FbxAMatrix lGlobalOffsetPosition = lGlobalPosition * lGeometryOffset;
if (lNodeAttribute)
{
// Get the actual node mesh data if it is a mesh this time
// (You could use this like the sample where they draw other nodes like cameras)
if (lNodeAttribute->GetAttributeType() == FbxNodeAttribute::eMesh)
{
// Draw the actual mesh data
FbxMesh* lMesh = pNode->GetMesh();
if (lMesh->IsTriangleMesh() == false) {
FbxGeometryConverter conv(mFbxManager);
conv.Triangulate(lNodeAttribute, true);
}
const uint lVertexCount = lMesh->GetControlPointsCount();
const uint lTriangleCount = lMesh->GetPolygonCount();
// May not have any vertex data
if (lVertexCount == 0) return;
mesh->nodes.push_back(MeshNode());
FbxVector4* pControlPoints = lMesh->GetControlPoints();
for (uint i = 0; i < lVertexCount; i++)
{
mesh->nodes[currentNode].vertices.push_back(vec3((float)pControlPoints[i].mData[0], (float)pControlPoints[i].mData[1], (float)pControlPoints[i].mData[2]));
}
mesh->nodes[currentNode].localTransform = FbxMatToGlm(lGlobalOffsetPosition);
}
currentNode++;
}
... Extracting other vertex attributes and materials ...
// Now check if this node has any children attached
const int lChildCount = pNode->GetChildCount();
for (int lChildIndex = 0; lChildIndex < lChildCount; ++lChildIndex)
{
// Draw this child
extractMeshRecursive(mScene, pNode->GetChild(lChildIndex), lGlobalPosition, mesh, currentNode);
}
}
I get a result that looks like this:
As opposed to:
A Mesh
Incorrect part was here:
mat4 FbxMatToGlm(const FbxAMatrix& mat) {
dvec4 c0 = glm::make_vec4((double*)mat.GetColumn(0).Buffer());
dvec4 c1 = glm::make_vec4((double*)mat.GetColumn(1).Buffer());
dvec4 c2 = glm::make_vec4((double*)mat.GetColumn(2).Buffer());
dvec4 c3 = glm::make_vec4((double*)mat.GetColumn(3).Buffer());
glm::mat4 convertMatr = mat4(c0, c1, c2, c3);
return inverse(convertMatr); // <--- Incorrect
}
There was no need to inverse the resulting matrix. It should've been transposed instead. Which i did at first, but unadjusted mesh scale was so huge i couldn't see it in my renderer and i started tinkering with it. After putting millimeters as Unit's in 3D Studio's FBX Export window, all transforms were correct.
Related
I'm trying to follow the tutorial at here ( at ogldev ) mentioned in this answer .
I am however facing a few issues which I believe to be related to the Row Major Order for Assimp vs the Column major order fro GLM, although I am quite not sure.
I've tried a few variations and orders to see if any of those work, but to no avail.
Here ( Gist ) is the Class which I use to load the complete MD5 file. And the current Result I have.
And, this is the part where I think it is going wrong, when I try to update the bone transformation matrices.
void SkeletalModel::ReadNodeHierarchyAnimation(float _animationTime, const aiNode* _node,
const glm::mat4& _parentTransform)
{
std::string node_name = _node->mName.data;
const aiAnimation * p_animation = scene->mAnimations[0];
glm::mat4 node_transformation(1.0f);
convert_aimatrix_to_glm(node_transformation, _node->mTransformation);
// Transpose it.
node_transformation = glm::transpose(node_transformation);
const aiNodeAnim * node_anim = FindNodeAnim(p_animation, node_name);
if (node_anim) {
//glm::mat4 transformation_matrix(1.0f);
glm::mat4 translation_matrix(1.0f);
glm::mat4 rotation_matrix(1.0f);
glm::mat4 scaling_matrix(1.0f);
aiVector3D translation;
CalcInterpolatedPosition(translation, _animationTime, node_anim);
translation_matrix = glm::translate(translation_matrix, glm::vec3(translation.x, translation.y, translation.z));
aiQuaternion rotation;
CalcInterpolatedRotation(rotation, _animationTime, node_anim);
// Transpose the matrix after this.
convert_aimatrix_to_glm(rotation_matrix, rotation.GetMatrix());
//rotation_matrix = glm::transpose(rotation_matrix);
aiVector3D scaling;
CalcInterpolatedScaling(scaling, _animationTime, node_anim);
scaling_matrix = glm::scale(scaling_matrix, glm::vec3(scaling.x, scaling.y, scaling.z));
node_transformation = scaling_matrix * rotation_matrix * translation_matrix;
//node_transformation = translation_matrix * rotation_matrix * scaling_matrix;
}
glm::mat4 global_transformation = node_transformation * _parentTransform;
if (boneMapping.find(node_name) != boneMapping.end()) {
// Update the Global Transformation.
auto bone_index = boneMapping[node_name];
//boneInfoData[bone_index].finalTransformation = globalInverseTransform * global_transformation * boneInfoData[bone_index].boneOffset;
boneInfoData[bone_index].finalTransformation = boneInfoData[bone_index].boneOffset * global_transformation * globalInverseTransform;
//boneInfoData[bone_index].finalTransformation = globalInverseTransform;
}
for (auto i = 0; i < _node->mNumChildren; i++) {
ReadNodeHierarchyAnimation(_animationTime, _node->mChildren[i], global_transformation);
}
}
My Current Output:
I tried going through each matrix used in the code to check whether I should tranpose it or not. Whether I should change the matrix multiplication order or not. I could not find my issue.
If anyone can point out my mistakes here or direct me to a different tutorial that would help me load animations, that would be great.
Also, I see suggestions to use a basic model in the initial stages of learning this. But I was told Obj format doesn't support animations, and I have been using just Obj before this. Can I use any other formats that blender exports in a manner similar to MD5 as shown in this tutorial?
I built an animated scene a few years ago using Assimp library, basically following these tutorials. http://ogldev.atspace.co.uk/www/tutorial38/tutorial38.html and http://sourceforge.net/projects/assimp/forums/forum/817654/topic/3880745
While I was using and old X format (blender can work with X, using an extension), I can definitely confirm you need to transpose the assimp animation matrices for use with GML.
Regarding using other formats, you can use what whatever you like provided they are supported by Blender (import, Editing, Export) and by Assimp. Be prepared for a fair bit of trial and error when changing formats!
Rather then me trying to understand your code, I will post the relevant fragments from my working system, that shows the calculation of bone matrices. Hopefully this will help you, as I remember having the same problem as you describe, and taking some time to track it down. Code is plain 'C'.
You can see where the transposition takes place at the end of the code.
// calculateAnimPose() calculates the bone transformations for a mesh at a particular time in an animation (in scene)
// Each bone transformation is relative to the rest pose.
void calculateAnimPose(aiMesh* mesh, const aiScene* scene, int animNum, float poseTime, mat4 *boneTransforms) {
if(mesh->mNumBones == 0 || animNum < 0) { // animNum = -1 for no animation
boneTransforms[0] = mat4(1.0); // so, just return a single identity matrix
return;
}
if(scene->mNumAnimations <= (unsigned int)animNum)
failInt("No animation with number:", animNum);
aiAnimation *anim = scene->mAnimations[animNum]; // animNum = 0 for the first animation
// Set transforms from bone channels
for(unsigned int chanID=0; chanID < anim->mNumChannels; chanID++) {
aiNodeAnim *channel = anim->mChannels[chanID];
aiVector3D curPosition;
aiQuaternion curRotation; // interpolation of scaling purposefully left out for simplicity.
// find the node which the channel affects
aiNode* targetNode = scene->mRootNode->FindNode( channel->mNodeName );
// find current positionKey
size_t posIndex = 0;
for(posIndex=0; posIndex+1 < channel->mNumPositionKeys; posIndex++)
if( channel->mPositionKeys[posIndex + 1].mTime > poseTime )
break; // the next key lies in the future - so use the current key
// This assumes that there is at least one key
if(posIndex+1 == channel-> mNumPositionKeys)
curPosition = channel->mPositionKeys[posIndex].mValue;
else {
float t0 = channel->mPositionKeys[posIndex].mTime; // Interpolate position/translation
float t1 = channel->mPositionKeys[posIndex+1].mTime;
float weight1 = (poseTime-t0)/(t1-t0);
curPosition = channel->mPositionKeys[posIndex].mValue * (1.0f - weight1) +
channel->mPositionKeys[posIndex+1].mValue * weight1;
}
// find current rotationKey
size_t rotIndex = 0;
for(rotIndex=0; rotIndex+1 < channel->mNumRotationKeys; rotIndex++)
if( channel->mRotationKeys[rotIndex + 1].mTime > poseTime )
break; // the next key lies in the future - so use the current key
if(rotIndex+1 == channel-> mNumRotationKeys)
curRotation = channel->mRotationKeys[rotIndex].mValue;
else {
float t0 = channel->mRotationKeys[rotIndex].mTime; // Interpolate using quaternions
float t1 = channel->mRotationKeys[rotIndex+1].mTime;
float weight1 = (poseTime-t0)/(t1-t0);
aiQuaternion::Interpolate(curRotation, channel->mRotationKeys[rotIndex].mValue,
channel->mRotationKeys[rotIndex+1].mValue, weight1);
curRotation = curRotation.Normalize();
}
aiMatrix4x4 trafo = aiMatrix4x4(curRotation.GetMatrix()); // now build a rotation matrix
trafo.a4 = curPosition.x; trafo.b4 = curPosition.y; trafo.c4 = curPosition.z; // add the translation
targetNode->mTransformation = trafo; // assign this transformation to the node
}
// Calculate the total transformation for each bone relative to the rest pose
for(unsigned int a=0; a<mesh->mNumBones; a++) {
const aiBone* bone = mesh->mBones[a];
aiMatrix4x4 bTrans = bone->mOffsetMatrix; // start with mesh-to-bone matrix to subtract rest pose
// Find the bone, then loop through the nodes/bones on the path up to the root.
for(aiNode* node = scene->mRootNode->FindNode(bone->mName); node!=NULL; node=node->mParent)
bTrans = node->mTransformation * bTrans; // add each bone's current relative transformation
boneTransforms[a] = mat4(vec4(bTrans.a1, bTrans.a2, bTrans.a3, bTrans.a4),
vec4(bTrans.b1, bTrans.b2, bTrans.b3, bTrans.b4),
vec4(bTrans.c1, bTrans.c2, bTrans.c3, bTrans.c4),
vec4(bTrans.d1, bTrans.d2, bTrans.d3, bTrans.d4)); // Convert to mat4
}
}
I want to load a model from an fbx file into my own graphic engine and render the meshes. But when I've load the file and try to render it, the meshes of some nodes did not display on the right position or with the correct rotation. Just like this:
I have tried almost all methods to transform the control points of meshes, such as using the code from fbx_sdk sample code:
FbxAMatrix CalculateGlobalTransform(FbxNode* pNode)
{
FbxAMatrix lTranlationM, lScalingM, lScalingPivotM, lScalingOffsetM, lRotationOffsetM, lRotationPivotM, lPreRotationM, lRotationM, lPostRotationM, lTransform;
FbxAMatrix lParentGX, lGlobalT, lGlobalRS;
if(!pNode)
{
lTransform.SetIdentity();
return lTransform;
}
// Construct translation matrix
FbxVector4 lTranslation = pNode->LclTranslation.Get();
lTranlationM.SetT(lTranslation);
// Construct rotation matrices
FbxVector4 lRotation = pNode->LclRotation.Get();
FbxVector4 lPreRotation = pNode->PreRotation.Get();
FbxVector4 lPostRotation = pNode->PostRotation.Get();
lRotationM.SetR(lRotation);
lPreRotationM.SetR(lPreRotation);
lPostRotationM.SetR(lPostRotation);
// Construct scaling matrix
FbxVector4 lScaling = pNode->LclScaling.Get();
lScalingM.SetS(lScaling);
// Construct offset and pivot matrices
FbxVector4 lScalingOffset = pNode->ScalingOffset.Get();
FbxVector4 lScalingPivot = pNode->ScalingPivot.Get();
FbxVector4 lRotationOffset = pNode->RotationOffset.Get();
FbxVector4 lRotationPivot = pNode->RotationPivot.Get();
lScalingOffsetM.SetT(lScalingOffset);
lScalingPivotM.SetT(lScalingPivot);
lRotationOffsetM.SetT(lRotationOffset);
lRotationPivotM.SetT(lRotationPivot);
// Calculate the global transform matrix of the parent node
FbxNode* lParentNode = pNode->GetParent();
if(lParentNode)
{
lParentGX = CalculateGlobalTransform(lParentNode);
}
else
{
lParentGX.SetIdentity();
}
//Construct Global Rotation
FbxAMatrix lLRM, lParentGRM;
FbxVector4 lParentGR = lParentGX.GetR();
lParentGRM.SetR(lParentGR);
lLRM = lPreRotationM * lRotationM * lPostRotationM;
//Construct Global Shear*Scaling
//FBX SDK does not support shear, to patch this, we use:
//Shear*Scaling = RotationMatrix.Inverse * TranslationMatrix.Inverse * WholeTranformMatrix
FbxAMatrix lLSM, lParentGSM, lParentGRSM, lParentTM;
FbxVector4 lParentGT = lParentGX.GetT();
lParentTM.SetT(lParentGT);
lParentGRSM = lParentTM.Inverse() * lParentGX;
lParentGSM = lParentGRM.Inverse() * lParentGRSM;
lLSM = lScalingM;
//Do not consider translation now
FbxTransform::EInheritType lInheritType = pNode->InheritType.Get();
if(lInheritType == FbxTransform::eInheritRrSs)
{
lGlobalRS = lParentGRM * lLRM * lParentGSM * lLSM;
}
else if(lInheritType == FbxTransform::eInheritRSrs)
{
lGlobalRS = lParentGRM * lParentGSM * lLRM * lLSM;
}
else if(lInheritType == FbxTransform::eInheritRrs)
{
FbxAMatrix lParentLSM;
FbxVector4 lParentLS = lParentNode->LclScaling.Get();
lParentLSM.SetS(lParentLS);
FbxAMatrix lParentGSM_noLocal = lParentGSM * lParentLSM.Inverse();
lGlobalRS = lParentGRM * lLRM * lParentGSM_noLocal * lLSM;
}
else
{
FBXSDK_printf("error, unknown inherit type! \n");
}
// Construct translation matrix
// Calculate the local transform matrix
lTransform = lTranlationM * lRotationOffsetM * lRotationPivotM * lPreRotationM * lRotationM * lPostRotationM * lRotationPivotM.Inverse()\
* lScalingOffsetM * lScalingPivotM * lScalingM * lScalingPivotM.Inverse();
FbxVector4 lLocalTWithAllPivotAndOffsetInfo = lTransform.GetT();
// Calculate global translation vector according to:
// GlobalTranslation = ParentGlobalTransform * LocalTranslationWithPivotAndOffsetInfo
FbxVector4 lGlobalTranslation = lParentGX.MultT(lLocalTWithAllPivotAndOffsetInfo);
lGlobalT.SetT(lGlobalTranslation);
//Construct the whole global transform
lTransform = lGlobalT * lGlobalRS;
return lTransform;
}
which works well in U3d:
But in my engine there are still meshes of two or three nodes of incorrect transform.
I've opened the fbx file in Autodesk Fbx Review and Unity3D and it works well. The problem has bothered me for a whole day, so it would be awesome if someone can help. Thanks
This happens because of node hierarchy. And your answer is not completely correct
rewrite yor function like this
FbxVector4 CMeshLoader::multT(FbxNode* pNode, FbxVector4 vector) {
FbxAMatrix matrixGeo;
matrixGeo.SetIdentity();
if (pNode->GetNodeAttribute())
{
const FbxVector4 lT = pNode->GetGeometricTranslation(FbxNode::eSourcePivot);
const FbxVector4 lR = pNode->GetGeometricRotation(FbxNode::eSourcePivot);
const FbxVector4 lS = pNode->GetGeometricScaling(FbxNode::eSourcePivot);
matrixGeo.SetT(lT);
matrixGeo.SetR(lR);
matrixGeo.SetS(lS);
}
FbxAMatrix localMatrix = pNode->EvaluateLocalTransform();
FbxNode* pParentNode = pNode->GetParent();
FbxAMatrix parentMatrix = pParentNode->EvaluateLocalTransform();
while ((pParentNode = pParentNode->GetParent()) != NULL)
{
parentMatrix = pParentNode->EvaluateLocalTransform() * parentMatrix;
}
FbxAMatrix matrix = parentMatrix * localMatrix * matrixGeo;
FbxVector4 result = matrix.MultT(vector);
return result;}
Today I've tried to get "Local Transform", "Global Transform" and "Geometric Transform" from all nodes of the FBX file, and I've found that some of the nodes hold a "Geometric Transform", which are the ones in wrong position. So I use the nodes' global transform to multiply its geometric transfom, which generated a new matrix. Then I use this matrix to transform all the vertices of the node's mesh, and finally I got the properiate model.
Here's the code to transform the vertices
FbxVector4 multT(FbxNode node, FbxVector4 vector){
FbxAMatrix matrixGeo;
matrixGeo.SetIdentity();
if (node->GetNodeAttribute())
{
const FbxVector4 lT = node->GetGeometricTranslation(FbxNode::eSourcePivot);
const FbxVector4 lR = node->GetGeometricRotation(FbxNode::eSourcePivot);
const FbxVector4 lS = node->GetGeometricScaling(FbxNode::eSourcePivot);
matrixGeo.SetT(lT);
matrixGeo.SetR(lR);
matrixGeo.SetS(lS);
}
FbxAMatrix globalMatrix = node->EvaluateLocalTransform();
FbxAMatrix matrix = globalMatrix*matrixGeo;
FbxVector4 result = matrix.MultT(vector);
return result;}
And the model rendered in my engine
enter image description here
I need to convert animation data from Autodesk's FBX file format to one that is compatible with DirectX; specifically, I need to calculate the offset matrices for my skinned mesh. I have written a converter( which in this case converts .fbx to my own 'scene' format ) in which I would like to calculate an offset matrix for my mesh. Here is code:
//
// Skin
//
if(bHasDeformer)
{
// iterate deformers( TODO: ACCOUNT FOR MULTIPLE DEFORMERS )
for(int i = 0; i < ncDeformers && i < 1; ++i)
{
// skin
FbxSkin *pSkin = (FbxSkin*)pMesh->GetDeformer(i, FbxDeformer::eSkin);
if(pSkin == NULL)
continue;
// bone count
int ncBones = pSkin->GetClusterCount();
// iterate bones
for (int boneIndex = 0; boneIndex < ncBones; ++boneIndex)
{
// cluster
FbxCluster* cluster = pSkin->GetCluster(boneIndex);
// bone ref
FbxNode* pBone = cluster->GetLink();
// Get the bind pose
FbxAMatrix bindPoseMatrix, transformMatrix;
cluster->GetTransformMatrix(transformMatrix);
cluster->GetTransformLinkMatrix(bindPoseMatrix);
// decomposed transform components
vS = bindPoseMatrix.GetS();
vR = bindPoseMatrix.GetR();
vT = bindPoseMatrix.GetT();
int *pVertexIndices = cluster->GetControlPointIndices();
double *pVertexWeights = cluster->GetControlPointWeights();
// Iterate through all the vertices, which are affected by the bone
int ncVertexIndices = cluster->GetControlPointIndicesCount();
for (int iBoneVertexIndex = 0; iBoneVertexIndex < ncVertexIndices; iBoneVertexIndex++)
{
// vertex
int niVertex = pVertexIndices[iBoneVertexIndex];
// weight
float fWeight = (float)pVertexWeights[iBoneVertexIndex];
}
}
}
How do I convert the fbx transforms to a bone offset matrix?
I have an Entity.h like this:
using namespace physx;
class Entity
{
public:
Entity(Ogre::Vector3 dims, Ogre::Vector3 pos, std::string mesh, std::string id);
virtual ~Entity(void);
virtual void update(Ogre::Real dt);
virtual void init(Ogre::SceneManager* sceneMgr, PxPhysics* physics, PxScene* scene, PxVec3 velocity=PxVec3(0, 0, 0));
protected:
Ogre::Entity* mOgreEntity = NULL;
Ogre::SceneNode* mOgreNode = NULL;
Ogre::Vector3 mPosition;
Ogre::Vector3 mDimensions;
std::string mMesh;
std::string mId;
PxRigidDynamic* mActor;
PxMaterial* mMaterial;
};
And here is my Entity source:
#include "Entity.h"
Entity::Entity(Ogre::Vector3 dims, Ogre::Vector3 pos, std::string mesh, std::string id)
{
mDimensions = dims;
mPosition = pos;
mMesh = mesh;
mId = id;
mActor = NULL;
mMaterial = NULL;
}
Entity::~Entity(void)
{
}
void Entity::update(Ogre::Real dt)
{
PxVec3 pos = mActor->getGlobalPose().p;
Ogre::Real r = 0;
mOgreNode->setPosition(Ogre::Vector3(pos.x + r, pos.y + r, pos.z + r));
}
void Entity::init(Ogre::SceneManager* sceneMgr, PxPhysics* physics, PxScene* scene, PxVec3 velocity)
{
// Create an Entity
mOgreEntity = sceneMgr->createEntity(mId, mMesh);
mOgreEntity->setCastShadows(true);
// Create a SceneNode and attach the Entity to it
mOgreNode = sceneMgr->getRootSceneNode()->createChildSceneNode(mId + "Node");
Ogre::AxisAlignedBox box = mOgreEntity->getBoundingBox();
Ogre::Vector3 realSizes = box.getSize();
mOgreNode->setPosition(mPosition);
mOgreNode->attachObject(mOgreEntity);
Ogre::Vector3 scaler = Ogre::Vector3(mDimensions.x / realSizes.x, mDimensions.y / realSizes.y, mDimensions.z / realSizes.z);
mOgreNode->scale(scaler);
mMaterial = physics->createMaterial(1.5f, 1.5f, 1.0f);
PxGeometry* geometry = NULL;
if(mMesh == "sphere.mesh")
{
PxGeometry g = PxSphereGeometry(mDimensions.x / 2); // Because it's a radius
geometry = &g;
} else {
// geometry = NULL;
}
PxTransform transform = PxTransform(PxVec3(mPosition.x, mPosition.y, mPosition.z));
mActor = PxCreateDynamic(*physics, transform, *geometry, *mMaterial, PxReal(.1));
// if(!mActor) {
// MessageBox( NULL, "no actor", "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
// return;
// }
mActor->setLinearVelocity(velocity);
// And add the actor to a scene:
scene->addActor(*mActor);
}
Now, if I create a single entity and initialize it works. Even wirh a second entity var it works as well. Now with an array:
Entity *mEntities[20];
for(int i = 0 ; i < 20 ; i++ ){
ostringstream nameStream;
nameStream << "Sphere_" << i;
string name = nameStream.str();
Entity* sphere = new Entity(Ogre::Vector3(i*5, i*4.5, i*6), Ogre::Vector3(i*5, i*4.5, i*6), "sphere.mesh", name);
sphere->init(mSceneMgr, mPhysics, gScene, PxVec3(-10.0f, 0, 0));
mEntities[i] = sphere;
}
I got Access violation. W/ the just-in-time debugger, it turned out that mActorwas null as well as mMaterial
EDIT:
This code does not work either:
mEntity = Entity(Ogre::Vector3(50.0f, 50.0f, 50.0f), Ogre::Vector3(50.0f, 40.5f, 60.0f), "sphere.mesh", "sphere");
mEntity.init(mSceneMgr, mPhysics, gScene, PxVec3(-10.0f, 0, 0));
1)
Entity* sphere = new Entity(Ogre::Vector3(i*5, i*4.5, i*6),
Ogre::Vector3(i*5, i*4.5, i*6),
"sphere.mesh",
"Sphere_"+i);
Look at the "Sphere_"+i
If the i is larger then length of ”Sphere_” you are passing pointer to some random memory. I assume that you wanted to create a string with i at the end.
Use sprintf or std::string for that.
2)
If you change the loop range from 20 to let's say 3 it will probably work. The problem is that your names will be:
Sphere_, phere_, here_
Because by doing "Sphere_"+i you are not adding integer to the string.
This is "equal" to:
char *string = "String";
string += 3;
3)
This code will generate string that you need:
std::ostringstream newStringStream;
newStringStream << "Sphere_" << i;
std::string newString = newStringStream.str();
Here is another issue:
PxGeometry* geometry = NULL;
if(mMesh == "sphere.mesh")
{
geometry = &PxSphereGeometry(mDimensions.x / 2); // Because it's a radius
}
The problem with this is that you are assigning to geometry the address of a temporary value. Once that line of code is completed, that temporary is gone.
The probable fix for this is to do this:
PxGeometry geometry;
if(mMesh == "sphere.mesh")
{
geometry = PxSphereGeometry(mDimensions.x / 2); // Because it's a radius
}
//...
mActor = PxCreateDynamic(*physics, transform, geometry, *mMaterial, PxReal(.1));
Now geometry is no longer a pointer, and you're assigning geometry to the value returned, (not address-of the value returned).
I am reading the documentation here:
http://docs.nvidia.com/gameworks/content/gameworkslibrary/physx/apireference/files/classPxSphereGeometry.html
So PxSphereGeometry(x) is a constructor call. So you need to assign the return value to a PxShpereGeometry, not a PxSphereGeometry*.
Edit: Your latest changes also do not have the desired effect:
if(mMesh == "sphere.mesh")
{
PxGeometry g = PxSphereGeometry(mDimensions.x / 2); // Because it's a radius
geometry = &g;
}
The g is local to the if() block. You assign the address of this g to geometry. Then when that block exits, g is gone, and now you have geometry pointing to something that no longer exists.
The difference between your edited code and the answer I gave is that my answer assigns the return value to an existing object. So I created a duplicate of the return value. What your doing in the edited code is not creating a duplicate, but pointing to a local object, which as explained, won't exist after it leaves scope.
So if you were to write code that follows the pattern of your edited code, and have it be valid, the change would look like this:
PxGeometry geometry;
if(mMesh == "sphere.mesh")
{
PxGeometry g = PxSphereGeometry(mDimensions.x / 2); // Because it's a radius
geometry = g;
}
However, this does extraneous work. The original answer is sufficient.
I tried the alternate way to create a rigid body and it worked!!!!
mActor = physics->createRigidDynamic(PxTransform(PxVec3(mPosition.x, mPosition.y, mPosition.z)));
PxShape* shape = mActor->createShape(PxSphereGeometry(mDimensions.x / 2), *mMaterial);
PxRigidBodyExt::updateMassAndInertia(*mActor, 0.4f);
I've been trying to realize a mesh that has all face normals pointing outward.
In order to realize this, I load a mesh from a *.ctm file, then walk over all
triangles to determine the normal using a cross product and if the normal
is pointing to the negative z direction, I flip v1 and v2 (thus the normal orientation).
After this is done I save the result to a *.ctm file and view it with Meshlab.
The result in Meshlab still shows that normals are pointing in both positive and
negative z direction ( can be seen from the black triangles). Also when viewing
the normals in Meshlab they are really pointing backwards.
Can anyone give me some advice on how to solve this?
The source code for the normalization part is:
pcl::PointCloud<pcl::PointXYZRGBA>::Ptr cloud1 (new pcl::PointCloud<pcl::PointXYZRGBA> ());
pcl::fromROSMsg (meshFixed.cloud,*cloud1);for(std::vector<pcl::Vertices>::iterator it = meshFixed.polygons.begin(); it != meshFixed.polygons.end(); ++it)
{
alglib::real_2d_array v0;
double _v0[] = {cloud1->points[it->vertices[0]].x,cloud1->points[it->vertices[0]].y,cloud1->points[it->vertices[0]].z};
v0.setcontent(3,1,_v0); //3 rows, 1col
alglib::real_2d_array v1;
double _v1[] = {cloud1->points[it->vertices[1]].x,cloud1->points[it->vertices[1]].y,cloud1->points[it->vertices[1]].z};
v1.setcontent(3,1,_v1); //3 rows, 1col
alglib::real_2d_array v2;
double _v2[] = {cloud1->points[it->vertices[2]].x,cloud1->points[it->vertices[2]].y,cloud1->points[it->vertices[2]].z};
v2.setcontent(1,3,_v2); //3 rows, 1col
alglib::real_2d_array normal;
normal = cross(v1-v0,v2-v0);
//if z<0 change indices order v1->v2 and v2->v1
alglib::real_2d_array normalizedNormal;
if(normal[2][0]<0)
{
int index1,index2;
index1 = it->vertices[1];
index2 = it->vertices[2];
it->vertices[1] = index2;
it->vertices[2] = index1;
//make normal of length 1
double normalScaling = 1.0/sqrt(dot(normal,normal));
normal[0][0] = -1*normal[0][0];
normal[1][0] = -1*normal[1][0];
normal[2][0] = -1*normal[2][0];
normalizedNormal = normalScaling * normal;
}
else
{
//make normal of length 1
double normalScaling = 1.0/sqrt(dot(normal,normal));
normalizedNormal = normalScaling * normal;
}
//add to normal cloud
pcl::Normal pclNormalizedNormal;
pclNormalizedNormal.normal_x = normalizedNormal[0][0];
pclNormalizedNormal.normal_y = normalizedNormal[1][0];
pclNormalizedNormal.normal_z = normalizedNormal[2][0];
normalsFixed.push_back(pclNormalizedNormal);
}
The result from this code is:
I've found some code in the VCG library to orient the face and vertex normals.
After using this a large part of the mesh has correct face normals, but not all.
The new code:
// VCG library implementation
MyMesh m;
// Convert pcl::PolygonMesh to VCG MyMesh
m.Clear();
// Create temporary cloud in to have handy struct object
pcl::PointCloud<pcl::PointXYZRGBA>::Ptr cloud1 (new pcl::PointCloud<pcl::PointXYZRGBA> ());
pcl::fromROSMsg (meshFixed.cloud,*cloud1);
// Now convert the vertices to VCG MyMesh
int vertCount = cloud1->width*cloud1->height;
vcg::tri::Allocator<MyMesh>::AddVertices(m, vertCount);
for(unsigned int i=0;i<vertCount;++i)
m.vert[i].P()=vcg::Point3f(cloud1->points[i].x,cloud1->points[i].y,cloud1->points[i].z);
// Now convert the polygon indices to VCG MyMesh => make VCG faces..
int triCount = meshFixed.polygons.size();
if(triCount==1)
{
if(meshFixed.polygons[0].vertices[0]==0 && meshFixed.polygons[0].vertices[1]==0 && meshFixed.polygons[0].vertices[2]==0)
triCount=0;
}
Allocator<MyMesh>::AddFaces(m, triCount);
for(unsigned int i=0;i<triCount;++i)
{
m.face[i].V(0)=&m.vert[meshFixed.polygons[i].vertices[0]];
m.face[i].V(1)=&m.vert[meshFixed.polygons[i].vertices[1]];
m.face[i].V(2)=&m.vert[meshFixed.polygons[i].vertices[2]];
}
vcg::tri::UpdateBounding<MyMesh>::Box(m);
vcg::tri::UpdateNormal<MyMesh>::PerFace(m);
vcg::tri::UpdateNormal<MyMesh>::PerVertexNormalizedPerFace(m);
printf("Input mesh vn:%i fn:%i\n",m.VN(),m.FN());
// Start to flip all normals to outside
vcg::face::FFAdj<MyMesh>::FFAdj();
vcg::tri::UpdateTopology<MyMesh>::FaceFace(m);
bool oriented, orientable;
if ( vcg::tri::Clean<MyMesh>::CountNonManifoldEdgeFF(m)>0 ) {
std::cout << "Mesh has some not 2-manifold faces, Orientability requires manifoldness" << std::endl; // text
return; // can't continue, mesh can't be processed
}
vcg::tri::Clean<MyMesh>::OrientCoherentlyMesh(m, oriented,orientable);
vcg::tri::Clean<MyMesh>::FlipNormalOutside(m);
vcg::tri::Clean<MyMesh>::FlipMesh(m);
//vcg::tri::UpdateTopology<MyMesh>::FaceFace(m);
//vcg::tri::UpdateTopology<MyMesh>::TestFaceFace(m);
vcg::tri::UpdateNormal<MyMesh>::PerVertexNormalizedPerFace(m);
vcg::tri::UpdateNormal<MyMesh>::PerVertexFromCurrentFaceNormal(m);
// now convert VCG back to pcl::PolygonMesh
pcl::PointCloud<pcl::PointXYZRGBA>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZRGBA>);
cloud->is_dense = false;
cloud->width = vertCount;
cloud->height = 1;
cloud->points.resize (vertCount);
// Now fill the pointcloud of the mesh
for(int i=0; i<vertCount; i++)
{
cloud->points[i].x = m.vert[i].P()[0];
cloud->points[i].y = m.vert[i].P()[1];
cloud->points[i].z = m.vert[i].P()[2];
}
pcl::toROSMsg(*cloud,meshFixed.cloud);
std::vector<pcl::Vertices> polygons;
// Now fill the indices of the triangles/faces of the mesh
for(int i=0; i<triCount; i++)
{
pcl::Vertices vertices;
vertices.vertices.push_back(m.face[i].V(0)-&*m.vert.begin());
vertices.vertices.push_back(m.face[i].V(1)-&*m.vert.begin());
vertices.vertices.push_back(m.face[i].V(2)-&*m.vert.begin());
polygons.push_back(vertices);
}
meshFixed.polygons = polygons;
Which results in: (Meshlab still shows normals are facing both sides)
I finally solved the problem. So I'm still using VCG library. From the above new code I slightly updated the following section:
vcg::tri::Clean<MyMesh>::OrientCoherentlyMesh(m, oriented,orientable);
//vcg::tri::Clean<MyMesh>::FlipNormalOutside(m);
//vcg::tri::Clean<MyMesh>::FlipMesh(m);
//vcg::tri::UpdateTopology<MyMesh>::FaceFace(m);
//vcg::tri::UpdateTopology<MyMesh>::TestFaceFace(m);
vcg::tri::UpdateNormal<MyMesh>::PerVertexNormalizedPerFace(m);
vcg::tri::UpdateNormal<MyMesh>::PerVertexFromCurrentFaceNormal(m);
Now I've updated the vcg::tri::Clean<MyMesh>::OrientCoherentlyMesh() function in clean.h. Here the update is to orient the first polygon of a group correctly. Also after swapping the edge the normal of the face is calculated and updated.
static void OrientCoherentlyMesh(MeshType &m, bool &Oriented, bool &Orientable)
{
RequireFFAdjacency(m);
assert(&Oriented != &Orientable);
assert(m.face.back().FFp(0)); // This algorithms require FF topology initialized
Orientable = true;
Oriented = true;
tri::UpdateSelection<MeshType>::FaceClear(m);
std::stack<FacePointer> faces;
for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
{
if (!fi->IsD() && !fi->IsS())
{
// each face put in the stack is selected (and oriented)
fi->SetS();
// New section of code to orient the initial face correctly
if(fi->N()[2]>0.0)
{
face::SwapEdge<FaceType,true>(*fi, 0);
face::ComputeNormal(*fi);
}
// End of new code section.
faces.push(&(*fi));
// empty the stack
while (!faces.empty())
{
FacePointer fp = faces.top();
faces.pop();
// make consistently oriented the adjacent faces
for (int j = 0; j < 3; j++)
{
//get one of the adjacent face
FacePointer fpaux = fp->FFp(j);
int iaux = fp->FFi(j);
if (!fpaux->IsD() && fpaux != fp && face::IsManifold<FaceType>(*fp, j))
{
if (!CheckOrientation(*fpaux, iaux))
{
Oriented = false;
if (!fpaux->IsS())
{
face::SwapEdge<FaceType,true>(*fpaux, iaux);
// New line to update face normal
face::ComputeNormal(*fpaux);
// end of new section.
assert(CheckOrientation(*fpaux, iaux));
}
else
{
Orientable = false;
break;
}
}
// put the oriented face into the stack
if (!fpaux->IsS())
{
fpaux->SetS();
faces.push(fpaux);
}
}
}
}
}
if (!Orientable) break;
}
}
Besides I also updated the function bool CheckOrientation(FaceType &f, int z) to perform a calculation based on normal z-direction.
template <class FaceType>
bool CheckOrientation(FaceType &f, int z)
{
// Added next section to calculate the difference between normal z-directions
FaceType *original = f.FFp(z);
double nf2,ng2;
nf2=f.N()[2];
ng2=original->N()[2];
// End of additional section
if (IsBorder(f, z))
return true;
else
{
FaceType *g = f.FFp(z);
int gi = f.FFi(z);
// changed if statement from: if (f.V0(z) == g->V1(gi))
if (nf2/abs(nf2)==ng2/abs(ng2))
return true;
else
return false;
}
}
The result is as I expect and desire from the algorithm: