Is it possible to morph a mesh in Irrlicht?
Is it possible to morph a mesh in Irrlicht?
Hi!
I have a character which I want to make thin or fat. And I want to be able to pick different body parts to modify in this way. I've found posts talking about morphing meshes, but I can't find that functionality in Irrlicht.
Is it possible to morph a mesh in Irrlicht?
Do someone know any code snippets that shows how to do this?
This is an old post I've found about the subject:
http://irrlicht.sourceforge.net/phpBB2/ ... ph&start=0
I'm greatful for any suggestions.
BR
I have a character which I want to make thin or fat. And I want to be able to pick different body parts to modify in this way. I've found posts talking about morphing meshes, but I can't find that functionality in Irrlicht.
Is it possible to morph a mesh in Irrlicht?
Do someone know any code snippets that shows how to do this?
This is an old post I've found about the subject:
http://irrlicht.sourceforge.net/phpBB2/ ... ph&start=0
I'm greatful for any suggestions.
BR
-
- Posts: 1638
- Joined: Mon Apr 30, 2007 3:24 am
- Location: Montreal, CANADA
- Contact:
Yes. I think it's possible. But the morph targets must be created in a 3D application before. The morphing animation will also need to be created in the 3D application (as making a fat man going slim). This animation will be saved then in the proper format that support vertex animation.
The MD2 use this king of vertex animation, and I think DirectX and B3D can use Bone and vertex animations.
Then in IRRlicht, you simply play back that animation.
The MD2 use this king of vertex animation, and I think DirectX and B3D can use Bone and vertex animations.
Then in IRRlicht, you simply play back that animation.
ok, I was hoping that I only needed to create the morph targets with a 3d application and then compute the morph deformation in Irrlicht. If I use the same mesh as base for modeling, I'm hoping that the index of the indices are not changed.
Too bad that fmx didn't show any code on how he solved it. It would be interesting to see his solution.
Too bad that fmx didn't show any code on how he solved it. It would be interesting to see his solution.
It is fairly easy to do in Irrlicht. I use it for sails on a sailboat - with blending.
just use IMesh->getMeshBuffer(0)->getVertices() for each pose and copy them all into 1 array. Then you can use 2 pointers and pointer arithmetic to get the start positions(in the array) for 2 poses to morph between and use vector3d.interpolate to get the final vertex position and set it to the mesh buffer of the visible mesh. You might need to do normals too. You can also blend between morphs using interpolation.
I use Milkshape and just start each pose with the relaxed pose, and after moving the verts, save it with a new name.( Haven't tried it yet,but it could be possible to have them all in 1 file, with unique materials,it might also possible to use bones then export the mesh only.)
Once its working you can save them to a file so you won't need to reload the meshes.
I'm a bit busy but I'll try to dig up some code this weekend.
You can probably find code if you do enough searching. try "MeshBuffer"
P.S.
The first thing to do is look-up interpolation on Wikipedia if you don't already understand it.
That should work.pir wrote:ok, I was hoping that I only needed to create the morph targets with a 3d application and then compute the morph deformation in Irrlicht. If I use the same mesh as base for modeling, I'm hoping that the index of the indices are not changed.
.
just use IMesh->getMeshBuffer(0)->getVertices() for each pose and copy them all into 1 array. Then you can use 2 pointers and pointer arithmetic to get the start positions(in the array) for 2 poses to morph between and use vector3d.interpolate to get the final vertex position and set it to the mesh buffer of the visible mesh. You might need to do normals too. You can also blend between morphs using interpolation.
I use Milkshape and just start each pose with the relaxed pose, and after moving the verts, save it with a new name.( Haven't tried it yet,but it could be possible to have them all in 1 file, with unique materials,it might also possible to use bones then export the mesh only.)
Once its working you can save them to a file so you won't need to reload the meshes.
I'm a bit busy but I'll try to dig up some code this weekend.
You can probably find code if you do enough searching. try "MeshBuffer"
P.S.
The first thing to do is look-up interpolation on Wikipedia if you don't already understand it.
Thank you for your tips!
I'll try to interpolate between two meshes. At the moment I'm trying to find out how to manipulate the mesh used by the skinned animation. Apparently I'm missunderstanding something important when doing this, since my manipulations do not show. I guess I need to study the SSkinnedMeshBuffer and CSkinnedMesh code more...
This is what I've tried so far (I'm only trying to see if I can deform the mesh in some way).
It looks like CAnimatedMeshMD3::buildVertexArray() contain code for interpolation.
I'll try to interpolate between two meshes. At the moment I'm trying to find out how to manipulate the mesh used by the skinned animation. Apparently I'm missunderstanding something important when doing this, since my manipulations do not show. I guess I need to study the SSkinnedMeshBuffer and CSkinnedMesh code more...
This is what I've tried so far (I'm only trying to see if I can deform the mesh in some way).
Code: Select all
// m_mesh is a IAnimatedMeshSceneNode
void deform( IAnimatedMeshSceneNode* aMesh)
{
ISkinnedMesh* mesh =
static_cast< ISkinnedMesh*>( aMesh->getMesh( ));
array<SSkinMeshBuffer*>& skin = mesh->getMeshBuffers( );
SSkinMeshBuffer* skinBuffer = skin[ 0];
const u32 vertexCount = skinBuffer->getVertexCount( );
S3DVertex* vertices = static_cast< S3DVertex*>(
skinBuffer->getVertices( ));
// try to move half of the vertices to deform the skinned mesh
for( u32 i = 0; i < vertexCount / 2; i++)
{
vertices[ i].Pos += 1000.0f;
}// for i
}// deform()
OK here is a quick and dirty example. It should at least get you started. I'll try to cleanup/finish it when I get time.
There are 3 meshes used. The visible one and 2 morhped ones.You will need to supply your own meshes. It should be possible to do it with only 2.
edit:
Sorry for the lack of comments. Don't hesitate to ask questions.
Note that normally I would use an enum to list different animation states and set the poses with it and if I used a class like this it would be customized to fit the situation.
There are 3 meshes used. The visible one and 2 morhped ones.You will need to supply your own meshes. It should be possible to do it with only 2.
Code: Select all
/** Example 004 Movement
modified by KH. June 13, 2009
*/
#include <irrlicht.h>
#include <iostream>
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
class CMorphTargetAnimation
{
public:
scene::ISceneManager* smgr;
scene::IMeshSceneNode * node;
s32 poseCount;
s32 vertexCount;
///Here I'm using a vector3df array .You could also use a Vertex or float array.
array<vector3df> VectorArray;
vector3df* pSourceVerts;
vector3df* pDestinationVerts;
video::S3DVertex* pFinalVerts;
s32 src,dest;
f32 time;
CMorphTargetAnimation(scene::ISceneManager* smgr_,scene::IMeshSceneNode * node_):smgr(smgr_),node(node_),poseCount(0),vertexCount(0)
,pSourceVerts(0),pDestinationVerts(0),pFinalVerts(0),src(0),dest(0),time(0)
{
_IRR_DEBUG_BREAK_IF(smgr)
_IRR_DEBUG_BREAK_IF(node)
node->grab();
smgr->grab();
IMesh* msh = node->getMesh();
msh->grab();
//from what I've seen in the forum if you don't remove it from the cache
//any other nodes that use the same mesh will also get morphed
smgr->getMeshCache()->removeMesh (msh);
pFinalVerts = (S3DVertex*)msh->getMeshBuffer(0)->getVertices();
vertexCount = msh->getMeshBuffer(0)->getVertexCount();
///scene::IMeshCache::removeMesh ( const IMesh *const mesh )
}
s32 addPose(IMesh* mesh,bool remove = 1)
{
if(!mesh) return 0;
IMeshBuffer* meshBuff = mesh->getMeshBuffer(0);
video::S3DVertex* pvertBuff = (video::S3DVertex*) meshBuff->getVertices();
if(vertexCount != meshBuff->getVertexCount())
return 0;
for (u32 i=0; i <vertexCount; i++)
{
VectorArray.push_back(pvertBuff[i].Pos);
///VertexArray.push_back(pvertBuff[i]);
}
poseCount++;
dest=poseCount-1;
pSourceVerts = VectorArray.pointer();
pDestinationVerts = VectorArray.pointer() + (dest*vertexCount);
if(remove)
{
smgr->getMeshCache()->removeMesh (mesh);
}
return poseCount;
}
void destroy()
{
if(node)
{
node->getMesh()->drop();
node->removeAll();
node->remove();
}
node=0;
if(smgr) smgr->drop();
}
~CMorphTargetAnimation()
{
destroy();
}
void swapPoses()
{
vector3df* tmp = pSourceVerts;
pSourceVerts = pDestinationVerts;
pDestinationVerts = tmp;
}
void animatePoses(f32 speed)
{
time+=speed;
if(time>1.f)
{
///to do this as simply as I can,I'll just swap poses
///another way is to mult. time by a + or - direction
swapPoses();
time=0;
}
if (poseCount>1)
{
for (int i=0; i< vertexCount; i++)
{
pFinalVerts[i].Pos = pSourceVerts[i].getInterpolated(pDestinationVerts[i],time);
}
}
}
};
class MyEventReceiver : public IEventReceiver
{
public:
// This is the one method that we have to implement
virtual bool OnEvent(const SEvent& event)
{
// Remember whether each key is down or up
if (event.EventType == irr::EET_KEY_INPUT_EVENT)
KeyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
return false;
}
// This is used to check whether a key is being held down
virtual bool IsKeyDown(EKEY_CODE keyCode) const
{
return KeyIsDown[keyCode];
}
MyEventReceiver()
{
for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
KeyIsDown[i] = false;
}
private:
// We use this array to store the current state of each key
bool KeyIsDown[KEY_KEY_CODES_COUNT];
};
CMorphTargetAnimation* Morph;
int main()
{
// let user select driver type
video::E_DRIVER_TYPE driverType = video::EDT_DIRECT3D9;
printf("Please select the driver you want for this example:\n"\
" (a) Direct3D 9.0c\n (b) Direct3D 8.1\n (c) OpenGL 1.5\n"\
" (d) Software Renderer\n (e) Burning's Software Renderer\n"\
" (f) NullDevice\n (otherKey) exit\n\n");
char i;
std::cin >> i;
switch(i)
{
case 'a': driverType = video::EDT_DIRECT3D9;break;
case 'b': driverType = video::EDT_DIRECT3D8;break;
case 'c': driverType = video::EDT_OPENGL; break;
case 'd': driverType = video::EDT_SOFTWARE; break;
case 'e': driverType = video::EDT_BURNINGSVIDEO;break;
case 'f': driverType = video::EDT_NULL; break;
default: return 0;
}
// create device
MyEventReceiver receiver;
IrrlichtDevice* device = createDevice(driverType,
core::dimension2d<u32>(640, 480), 16, false, false, false, &receiver);
if (device == 0)
return 1; // could not create selected driver.
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager();
/*
Create the node which will be moved with the WSAD keys. We create a
sphere node, which is a built-in geometry primitive. We place the node
at (0,0,30) and assign a texture to it to let it look a little bit more
interesting. Because we have no dynamic lights in this scene we disable
lighting for each model (otherwise the models would be black).
*/
///this is the mesh you will see
scene::IMeshSceneNode * node = smgr->addMeshSceneNode(smgr->getMesh("../../media/sphere_0.ms3d"));
if (node)
{
//node->setPosition(core::vector3df(0,0,0));
node->setMaterialTexture(0, driver->getTexture("../../media/wall.bmp"));
node->setMaterialFlag(video::EMF_LIGHTING, false);
Morph = new CMorphTargetAnimation(smgr,node);
///these 2 are just temporary to get vertex positions from
IAnimatedMesh* mesh = smgr->getMesh("../../media/sphere_1.ms3d");
Morph->addPose(mesh->getMesh(0));
mesh = smgr->getMesh("../../media/sphere_2.ms3d");
Morph->addPose(mesh->getMesh(0));
}
scene::ISceneNode * PlaneMesh = smgr->addMeshSceneNode(smgr->addHillPlaneMesh("ground",core::dimension2d<f32>(64,64),core::dimension2d<u32>(8,8))->getMesh(0));
PlaneMesh->setMaterialTexture(0, driver->getTexture("../../media/stones.jpg"));
PlaneMesh->setMaterialFlag(EMF_LIGHTING, false);
/*
To be able to look at and move around in this scene, we create a first
person shooter style camera and make the mouse cursor invisible.
*/
smgr->addCameraSceneNodeFPS();
smgr->getActiveCamera()->setPosition(core::vector3df(0,20,-50));
device->getCursorControl()->setVisible(false);
// gui::IGUIStaticText* help = device->getGUIEnvironment()->addStaticText(
// L"Press UP/down left/right keys to start morph", core::rect<s32>(10, 10, 400, 20));
// diagnostics->setOverrideColor(video::SColor(255, 255, 255, 0));
/*
We have done everything, so lets draw it. We also write the current
frames per second and the name of the driver to the caption of the
window.
*/
int lastFPS = -1;
// In order to do framerate independent movement, we have to know
// how long it was since the last frame
u32 then = device->getTimer()->getTime();
// This is the movemen speed in units per second.
const f32 MOVEMENT_SPEED = 0.1f;
while(device->run())
{
// Work out a frame delta time.
const u32 now = device->getTimer()->getTime();
const f32 frameDeltaTime = (f32)(now - then) / 1000.f; // Time in seconds
then = now;
/* Check if keys W, S, A or D are being held down, and move the
sphere node around respectively. */
core::vector3df nodePosition = node->getPosition();
//
// if(receiver.IsKeyDown(irr::KEY_KEY_W))
// nodePosition.Y += MOVEMENT_SPEED * frameDeltaTime;
// else if(receiver.IsKeyDown(irr::KEY_KEY_S))
// nodePosition.Y -= MOVEMENT_SPEED * frameDeltaTime;
//
// if(receiver.IsKeyDown(irr::KEY_KEY_A))
// nodePosition.X -= MOVEMENT_SPEED * frameDeltaTime;
// else if(receiver.IsKeyDown(irr::KEY_KEY_D))
// nodePosition.X += MOVEMENT_SPEED * frameDeltaTime;
// node->setPosition(nodePosition);
Morph->animatePoses(MOVEMENT_SPEED * frameDeltaTime);
driver->beginScene(true, true, video::SColor(255,113,113,133));
smgr->drawAll(); // draw the 3d scene
device->getGUIEnvironment()->drawAll(); // draw the gui environment (the logo)
driver->endScene();
int fps = driver->getFPS();
if (lastFPS != fps)
{
core::stringw tmp(L"Movement Example - Irrlicht Engine [");
tmp += driver->getName();
tmp += L"] fps: ";
tmp += fps;
device->setWindowCaption(tmp.c_str());
lastFPS = fps;
}
}
/*
In the end, delete the Irrlicht device.
*/
delete Morph;
device->drop();
return 0;
}
/*
That's it. Compile and play around with the program.
**/
Sorry for the lack of comments. Don't hesitate to ask questions.
Note that normally I would use an enum to list different animation states and set the poses with it and if I used a class like this it would be customized to fit the situation.
-
- Posts: 1638
- Joined: Mon Apr 30, 2007 3:24 am
- Location: Montreal, CANADA
- Contact:
pir, If you want to do this directly in IRRlicht (Wow! Thats impressive!), your mesh need to be the same as:
The mesh need the same numbers of vertices and the same index for thoses vertices.
You should take an object, save it, then modify it then save the other version (not removing any vertices while modifiying the object). Then after that you load the 2 objects and interpollate the vertices position from one to the other. As an example, you take a character in the 3D application, use a magnet tool to modify the shape, to make him fatter. Then save the result as another mesh.
If your 2 objects don't share the same vertices, then I don't think it would work. (Would morph but it would look horrible)
The mesh need the same numbers of vertices and the same index for thoses vertices.
You should take an object, save it, then modify it then save the other version (not removing any vertices while modifiying the object). Then after that you load the 2 objects and interpollate the vertices position from one to the other. As an example, you take a character in the 3D application, use a magnet tool to modify the shape, to make him fatter. Then save the result as another mesh.
If your 2 objects don't share the same vertices, then I don't think it would work. (Would morph but it would look horrible)
Ok, I finally understood how to morph the SkinnedMesh. The solution is not good it is very primitive, but does what I want to do. I'm posting it here because I'm tired reading posts from people asking for help and ending with "it works now", without any hint on how to solve the topic of the thread.
The code morphs two meshes that have the same mesh as base. So the vertices are one to one mapped by index values.
BR
pir
The code morphs two meshes that have the same mesh as base. So the vertices are one to one mapped by index values.
Code: Select all
void Player::morph( AnimatedMeshSceneNodeExt* aMeshBase,
AnimatedMeshSceneNodeExt* aMeshMorph,
AnimatedMeshSceneNodeExt* aMeshTarget,
f32 aInterpolation)
{
ISkinnedMesh* skinBase = static_cast< ISkinnedMesh*>( aMeshBase->getMesh( ));
ISkinnedMesh* skinMorph = static_cast< ISkinnedMesh*>( aMeshMorph->getMesh( ));
ISkinnedMesh* skinTarget = static_cast< ISkinnedMesh*>( aMeshTarget->getMesh( ));
array< ISkinnedMesh::SJoint*> jointsBase = skinBase->getAllJoints( );
array< ISkinnedMesh::SJoint*> jointsMorph = skinMorph->getAllJoints( );
array< ISkinnedMesh::SJoint*> jointsTarget = skinTarget->getAllJoints( );
for( u32 j = 0; j < jointsBase.size( ); j++)
{
for( u32 w = 0; w < jointsBase[ j]->Weights.size( ); w++)
{
ISkinnedMesh::SWeight& weightBase = jointsBase[ j]->Weights[ w];
ISkinnedMesh::SWeight& weightMorph = jointsMorph[ j]->Weights[ w];
ISkinnedMesh::SWeight& weightTarget = jointsTarget[ j]->Weights[ w];
weightTarget.StaticPos =
weightBase.StaticPos
+ aInterpolation
* ( weightMorph.StaticPos - weightBase.StaticPos);
weightTarget.StaticNormal =
weightBase.StaticNormal
+ aInterpolation
* ( weightMorph.StaticNormal - weightBase.StaticNormal);
}// for w
}// for j
}// morph()
pir
Re: Is it possible to morph a mesh in Irrlicht?
StaticPos is a private member of class SWeight. how its possible to access it?
Re: Is it possible to morph a mesh in Irrlicht?
i used this method to morph my mesh to a target mesh. but this just works with one target.and always i have just the last target applied to base mesh.
how can i apply multiple targets to one base mesh?
how can i apply multiple targets to one base mesh?
-
- Posts: 91
- Joined: Mon Mar 05, 2012 4:51 pm
- Location: Bristol, UK
- Contact:
Re: Is it possible to morph a mesh in Irrlicht?
Dont bump old topics!
This topic is 4 years old!
This topic is 4 years old!
-
- Competition winner
- Posts: 688
- Joined: Mon Sep 10, 2012 8:51 am
Re: Is it possible to morph a mesh in Irrlicht?
@rubenwardy - It's fine. It can be annoying at times, but his question seems to be on topic.
@khatarat -
First question - I imagine the implementation of SWeight may have changed over the course of irrlicht's development.
Second question - I'm not sure what you mean by "always i have just the last target applied to base mesh" and "multiple targets". Are you looking to have several transitions? (For example, as if you had a person you wanted to scale - One mesh target makes them fat and the next one makes them tall.) Or are you looking for a cross between (or blending of) two or more target meshes?
@khatarat -
First question - I imagine the implementation of SWeight may have changed over the course of irrlicht's development.
Second question - I'm not sure what you mean by "always i have just the last target applied to base mesh" and "multiple targets". Are you looking to have several transitions? (For example, as if you had a person you wanted to scale - One mesh target makes them fat and the next one makes them tall.) Or are you looking for a cross between (or blending of) two or more target meshes?