Create a gist now

Instantly share code, notes, and snippets.

Ogre 3D Listener that I used to program multi-touch interactions
#include "listener.h"
//--------------------------------
// GLOBAL FOR LEVMAR
//--------------------------------
std::map<int,Vector3> mapPoint3DToMatch;
std::map<int,Vector2> mapPoint2D;
Vector3 vCentreObject = Vector3::ZERO; // Center of rotation ?
Vector3 vCentreRotation = Vector3::ZERO;
Camera * gmCamera; // Camera use to project
OgreListener::OgreListener(OIS::Keyboard *keyboard, Camera* cam, SceneManager *sceneMgr, RenderWindow* Window){
mContinue = true;
//--------------------------
// INIT MOUSE KEYBOARD & STUFF
//--------------------------
keyboard->setEventCallback(this);
mKeyboard = keyboard;
mSceneMgr = sceneMgr;
mCamNode = cam->getParentSceneNode();
mCamera = cam;
gmCamera = cam;
mWindow = Window;
//-----------------------------------
// INIT INTERACTION
//-----------------------------------
iNbFinger = 0;
iCurrentMode = INIT_STATE;
iCurrentTechnique = INTERACTION_DS3;
selectedNode = NULL;
lastSelectedNode = NULL;
//------------------------------
// FINGERPRINT
//------------------------------
/*
fingerprint=new Ogre2dManager;
fingerprint->init(mSceneMgr, Ogre::RENDER_QUEUE_OVERLAY, true);
Ogre::TextureManager::getSingleton().load("doigt.png",ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
*/
printFinger = false;
//-----------------------------
// TOUCH STUFF
//-----------------------------
mtlib_register_listener(this);
mtlib_activate_provider(MTLIB_TOUCHPROVIDER_TUIO);
//----------------------------
// INIT PICKING
//----------------------------
mRaySceneQuery = mSceneMgr->createRayQuery(Ray(),OK_MASK);
//------------------------------
// INTERACTION POINTER & VARIABLE
//------------------------------
fZ = NULL;
fRoll = NULL;
vMovePrev = Vector2::ZERO;
vYawPrev = Vector2::ZERO;
vRollPrev = Vector2::ZERO;
vZPrev = Vector2::ZERO;
vectorTranslation = Vector3::ZERO;
quatRotation = Quaternion::IDENTITY;
//-----------------------------
// INTERACTION CONSTANT
//-----------------------------
v3K1 = 10;
v3K2 = 10;
factorZ = 10;
//-----------------------------
// CEGUI CONSTANT
//-----------------------------
createCEGUIevent();
}
OgreListener::~OgreListener(){
}
//---------------------------------
// CEGUI EVENT
//---------------------------------
void OgreListener::createCEGUIevent(){
CEGUI::WindowManager &wmgr = CEGUI::WindowManager::getSingleton();
CEGUI::Window *button = wmgr.getWindow("mainMenu/bQuit");
button->subscribeEvent(CEGUI::PushButton::EventClicked, CEGUI::Event::Subscriber(&OgreListener::quit, this));
}
bool OgreListener::quit(const CEGUI::EventArgs &e){
mContinue = false;
return true;
}
//---------------------------------
// KEYBOARD
//---------------------------------
bool OgreListener::keyPressed(const OIS::KeyEvent &e){
CEGUI::System &sys = CEGUI::System::getSingleton();
sys.injectKeyDown(e.key);
sys.injectChar(e.text);
switch (e.key)
{
case OIS::KC_ESCAPE:
mContinue = false;
break;
default:
break;
}
return mContinue;
}
bool OgreListener::keyReleased(const OIS::KeyEvent &e){
CEGUI::System::getSingleton().injectKeyUp(e.key);
return true;
}
//---------------------------------
// RENDER LOOP
//---------------------------------
bool OgreListener::frameStarted(const FrameEvent& evt){
// Inputs
mtlib_process_message_queue();
mKeyboard->capture();
// We are using information which is share with TouchListener (tuio ...) so we need a mutex !
multiTouchLock.LockWriter();
do_debuginfo();
if(iCurrentTechnique==INTERACTION_DS3){
ds3_interaction();
}else if(iCurrentTechnique==INTERACTION_STICKY){
sticky_interaction();
}else if(iCurrentTechnique==INTERACTION_SCREENSPACE){
nx_interaction();
}
multiTouchLock.UnlockWriter();
if(!mWindow->isActive()) mWindow->setActive(true); // Force ogre to refresh when not in focus (tuio simulator ...)
return mContinue;
}
void OgreListener::do_debuginfo(){
/*
if(printFinger)
{
// print finger
std::map<int,Finger*>::iterator it;
real x1,y1,x2,y2;
for( it = fingerMap.begin(); it != fingerMap.end(); ++it )
{
Vector2 current(it->second->cvec.x,it->second->cvec.y);
x1 = ((current.x*2)-1) - 0.05;
x2 = ((current.x*2)-1) + 0.05;
y1 = -((current.y*2)-1) - 0.05;
y2 = -((current.y*2)-1) + 0.05;
fingerprint->spriteBltFull("doigt.png", x1, y1, x2, y2);
}
}
*/
}
//---------------------------------
// TOUCH EVENT
//---------------------------------
void OgreListener::on_touch_detect(TOUCH& args){
int id = args.rid; // id of the finger
// test if CEGUI overlay and button to process
CEGUI::WindowManager &wmgr = CEGUI::WindowManager::getSingleton();
CEGUI::Window *current = CEGUI::System::getSingleton().getGUISheet();
CEGUI::Vector2 touchpos(args.x*mWindow->getWidth(),args.y*mWindow->getHeight());
CEGUI::EventArgs e;
bool handle = false;
if(current != NULL){
CEGUI::Window *button = current->getChildAtPosition(touchpos);
(button!=NULL) ? button->fireEvent(CEGUI::PushButton::EventClicked, e) : handle = true;
}
if(handle){
multiTouchLock.LockWriter();
Finger *temp = new Finger(id,args.x,args.y,0,0);
updateInteraction(PRESS,id,temp); // We call this function every time, it will take care of everything !
delete temp;
multiTouchLock.UnlockWriter();
}
}
void OgreListener::on_touch_update(TOUCH& args){
//printf("ON TOUCH UPDATe [%d, %f, %f]\n", args.rid, args.x, args.y);
int id = args.rid;
multiTouchLock.LockWriter();
Finger *temp = new Finger(id,args.x,args.y,0,0);
updateInteraction(MOVE,id,temp); // We call this function every time, it will take care of everything !
delete temp;
multiTouchLock.UnlockWriter();
}
void OgreListener::on_touch_release(TOUCH& args){
int id = args.rid;
multiTouchLock.LockWriter();
Finger *temp = new Finger(id,args.x,args.y,0,0);
updateInteraction(RELEASE,id,temp); // We call this function every time, it will take care of everything !
delete temp;
multiTouchLock.UnlockWriter();
}
//-------------------------------
// PICKING FUNCTION
//-------------------------------
bool OgreListener::IsNamePickedOK(String name){
return name.substr(0,5)=="ObjOK";
}
int OgreListener::basic_picking(Finger *fPick, Vector3 &Result){
Entity *ent=NULL;
MovableObject * mo;
Vector3 hitposition;
Ray mouseRay = mCamera->getCameraToViewportRay(fPick->cvec.x,fPick->cvec.y);
Plane test;
PickEntity(mouseRay,&ent,hitposition,test);
Result = NULL;
mo=ent;
if (mo!= NULL) // On a pické un truc
{
if(IsNamePickedOK(mo->getName()))
{
if(fPick!=NULL){
fPick->picked = true;
fPick->picking = mo->getName();
}
if(iCurrentScene==SCENE_PEG)
{
// Si aucune sélection
if(selectedNode==NULL){
selectedNode = mo->getParentSceneNode();
//selectedNode->showBoundingBox(true);
Result = hitposition;
return PICKING_OK;
// Sinon on vérifie qu'on est sur le même objet
}else if(selectedNode==mo->getParentSceneNode()){
Result = hitposition;
return PICKING_OK;
}
}else{
selectedNode = mSceneMgr->getSceneNode("Source");
Result = hitposition;
return PICKING_OK;
}
}
}
return NO_PICK;
}
int OgreListener::mini_picking(Vector2 pos, Vector3 &Result){
Entity *ent=NULL;
MovableObject * mo;
Vector3 hitposition;
Ray mouseRay = mCamera->getCameraToViewportRay(pos.x,pos.y);
Plane test;
PickEntity(mouseRay,&ent,hitposition,test);
Result = NULL;
mo=ent;
if (mo!= NULL) // On a pické un truc
{
if(IsNamePickedOK(mo->getName()))
{
Result = hitposition;
return PICKING_OK;
}
}
return NO_PICK;
}
bool OgreListener::plane_picking(Vector2 pos, Plane plan, Vector3 &Result){
Ray mouseRay = mCamera->getCameraToViewportRay(pos.x,pos.y);
std::pair<bool, Real> res = mouseRay.intersects(plan);
if(res.first)
{
Result = mouseRay.getPoint(res.second);
return true;
}
return false;
}
//-------------------------------
// ADVANCE PICKING FUNCTION
//-------------------------------
bool OgreListener::PickEntity(Ogre::Ray &ray, Ogre::Entity ** result, Ogre::Vector3 &hitpoint, Ogre::Plane &hitplane, Ogre::uint32 masque){
mRaySceneQuery->setQueryMask(masque);
mRaySceneQuery->setRay(ray);
mRaySceneQuery->setSortByDistance(true);
// Execute query
RaySceneQueryResult &query = mRaySceneQuery->execute();
RaySceneQueryResult::iterator itr;
if (query.size() <= 0) return (false);
// at this point we have raycast to a series of different objects bounding boxes.
// we need to test these different objects to see which is the first polygon hit.
// there are some minor optimizations (distance based) that mean we wont have to
// check all of the objects most of the time, but the worst case scenario is that
// we need to test every triangle of every object.
Ogre::Real closest_distance = -1.0f;
Ogre::Vector3 closest_result;
Ogre::RaySceneQueryResult &query_result = mRaySceneQuery->getLastResults();
for (size_t qr_idx = 0; qr_idx < query_result.size(); qr_idx++)
{
// stop checking if we have found a raycast hit that is closer
// than all remaining entities
if ((closest_distance >= 0.0f) && (closest_distance < query_result[qr_idx].distance))
{
break;
}
// only check this result if its a hit against an entity
if ((query_result[qr_idx].movable != NULL) && (query_result[qr_idx].movable->getMovableType().compare("Entity") == 0))
{
// get the entity to check
Ogre::Entity *pentity = static_cast<Ogre::Entity*>(query_result[qr_idx].movable);
if(!pentity->getVisible()) continue;
// mesh data to retrieve
size_t vertex_count;
size_t index_count;
Ogre::Vector3 *vertices;
unsigned long *indices;
// get the mesh information
GetMeshInformationEx(pentity->getMesh(), vertex_count, vertices, index_count, indices,
pentity->getParentNode()->_getDerivedPosition(),
pentity->getParentNode()->_getDerivedOrientation(),
pentity->getParentNode()->_getDerivedScale());
// test for hitting individual triangles on the mesh
bool new_closest_found = false;
for (int i = 0; i < static_cast<int>(index_count); i += 3)
{
// check for a hit against this triangle
std::pair<bool, Ogre::Real> hit = Ogre::Math::intersects(ray, vertices[indices[i]],
vertices[indices[i+1]], vertices[indices[i+2]], true, false);
// if it was a hit check if its the closest
if (hit.first)
{
if ((closest_distance < 0.0f) || (hit.second < closest_distance))
{
// this is the closest so far, save it off
closest_distance = hit.second;
new_closest_found = true;
hitplane = Plane(vertices[indices[i]],vertices[indices[i+1]],vertices[indices[i+2]]);
}
}
}
// free the verticies and indicies memory
delete[] vertices;
delete[] indices;
// if we found a new closest raycast for this object, update the
// closest_result before moving on to the next object.
if (new_closest_found)
{
closest_result = ray.getPoint(closest_distance);
(*result) = pentity;
}
}
}
// return the result
if (closest_distance >= 0.0f)
{
hitpoint = closest_result;
return (true);
}
else
{
// raycast failed
return (false);
}
}
void OgreListener::GetMeshInformationEx(const Ogre::MeshPtr mesh,size_t &vertex_count,Ogre::Vector3* &vertices,size_t &index_count,unsigned long* &indices,const Ogre::Vector3 &position,const Ogre::Quaternion &orient,const Ogre::Vector3 &scale){
bool added_shared = false;
size_t current_offset = 0;
size_t shared_offset = 0;
size_t next_offset = 0;
size_t index_offset = 0;
vertex_count = index_count = 0;
// Calculate how many vertices and indices we're going to need
for (unsigned short i = 0; i < mesh->getNumSubMeshes(); ++i)
{
Ogre::SubMesh* submesh = mesh->getSubMesh( i );
// We only need to add the shared vertices once
if(submesh->useSharedVertices)
{
if( !added_shared )
{
vertex_count += mesh->sharedVertexData->vertexCount;
added_shared = true;
}
}
else
{
vertex_count += submesh->vertexData->vertexCount;
}
// Add the indices
index_count += submesh->indexData->indexCount;
}
// Allocate space for the vertices and indices
vertices = new Ogre::Vector3[vertex_count];
indices = new unsigned long[index_count];
added_shared = false;
// Run through the submeshes again, adding the data into the arrays
for ( unsigned short i = 0; i < mesh->getNumSubMeshes(); ++i)
{
Ogre::SubMesh* submesh = mesh->getSubMesh(i);
Ogre::VertexData* vertex_data = submesh->useSharedVertices ? mesh->sharedVertexData : submesh->vertexData;
if((!submesh->useSharedVertices)||(submesh->useSharedVertices && !added_shared))
{
if(submesh->useSharedVertices)
{
added_shared = true;
shared_offset = current_offset;
}
const Ogre::VertexElement* posElem = vertex_data->vertexDeclaration->findElementBySemantic(Ogre::VES_POSITION);
Ogre::HardwareVertexBufferSharedPtr vbuf = vertex_data->vertexBufferBinding->getBuffer(posElem->getSource());
unsigned char* vertex = static_cast<unsigned char*>(vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
// There is _no_ baseVertexPointerToElement() which takes an Ogre::Real or a double
// as second argument. So make it float, to avoid trouble when Ogre::Real will
// be comiled/typedefed as double:
// Ogre::Real* pReal;
float* pReal;
for( size_t j = 0; j < vertex_data->vertexCount; ++j, vertex += vbuf->getVertexSize())
{
posElem->baseVertexPointerToElement(vertex, &pReal);
Ogre::Vector3 pt(pReal[0], pReal[1], pReal[2]);
vertices[current_offset + j] = (orient * (pt * scale)) + position;
}
vbuf->unlock();
next_offset += vertex_data->vertexCount;
}
Ogre::IndexData* index_data = submesh->indexData;
size_t numTris = index_data->indexCount / 3;
Ogre::HardwareIndexBufferSharedPtr ibuf = index_data->indexBuffer;
bool use32bitindexes = (ibuf->getType() == Ogre::HardwareIndexBuffer::IT_32BIT);
unsigned long* pLong = static_cast<unsigned long*>(ibuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
unsigned short* pShort = reinterpret_cast<unsigned short*>(pLong);
size_t offset = (submesh->useSharedVertices)? shared_offset : current_offset;
if ( use32bitindexes )
{
for ( size_t k = 0; k < numTris*3; ++k)
{
indices[index_offset++] = pLong[k] + static_cast<unsigned long>(offset);
}
}
else
{
for ( size_t k = 0; k < numTris*3; ++k)
{
indices[index_offset++] = static_cast<unsigned long>(pShort[k]) + static_cast<unsigned long>(offset);
}
}
ibuf->unlock();
current_offset = next_offset;
}
}
//------------------------------
// INTERACTION FUNCTION
//------------------------------
void OgreListener::updateInteraction(int iLastTouch, int iLastFinger, Finger *f){
// According to last touch
if(iLastTouch==PRESS)
{
// insert in the map
Finger *temp = new Finger(f);
fingerMap.insert(pair<int,Finger*>(f->id,temp));
}else if(iLastTouch==MOVE){
// Update the map
std::map<int,Finger*>::iterator it;
it=fingerMap.find(f->id);
if(it!=fingerMap.end()) it->second->update(f);
}else if(iLastTouch==RELEASE){
// Remove from the map
int id = f->id;
std::map<int,Finger*>::iterator it;
it=fingerMap.find(id);
if(it!=fingerMap.end()){
delete fingerMap.find(id)->second;
fingerMap.erase(id);
}
}
iNbFinger = fingerMap.size();
// The map is used for general purpose, the interaction technique does not use this.
// for every interaction technique, we process first the mode, then we extract the data we need !
if(iCurrentTechnique==INTERACTION_DS3){
ds3_mode(iLastTouch,iLastFinger,f);
ds3_gesture(iLastTouch,iLastFinger, f);
}else if(iCurrentTechnique==INTERACTION_STICKY){
sticky_mode(iLastTouch,iLastFinger,f);
sticky_gesture(iLastTouch,iLastFinger, f);
}else if(iCurrentTechnique==INTERACTION_SCREENSPACE){
nx_mode(iLastTouch,iLastFinger,f);
nx_gesture(iLastTouch,iLastFinger, f);
}
}
void OgreListener::updateSelected(){
lastSelectedNode = selectedNode;
//selectedNode->showBoundingBox(false);
selectedNode = NULL;
}
//------------------------------
// DS3 FUNCTION
//------------------------------
void OgreListener::ds3_mode(int iLastTouch, int iLastFinger, Finger *f){
// According to the mode and picking we switch mode, build the objectfingers map !
switch(iLastTouch)
{
case PRESS:
if (iCurrentMode == INIT_STATE)
{
Vector3 vPicking;
int picked = basic_picking(f,vPicking);
if(picked==PICKING_OK) {
ObjectFingers.insert(pair<int,Finger> (f->id,Finger(f)));
iCurrentMode = TRANSLATION;
break;
}
break;
}else if(iCurrentMode == TRANSLATION){
Vector3 vPicking;
int picked = basic_picking(f,vPicking);
if(picked==PICKING_OK) {
ObjectFingers.insert(pair<int,Finger> (f->id,Finger(f)));
iCurrentMode = ROTATION;
break;
}else{
fZ = new Finger(f);
iCurrentMode = Z_TRANSLATION;
break;
}
}else if(iCurrentMode == Z_TRANSLATION){
Vector3 vPicking;
int picked = basic_picking(f,vPicking);
if(picked==PICKING_OK) {
ObjectFingers.insert(pair<int,Finger> (f->id,Finger(f)));
iCurrentMode = COMBINED;
break;
}
break;
}else if(iCurrentMode == ROTATION){
Vector3 vPicking;
int picked = basic_picking(f,vPicking);
if(picked==NO_PICK) {
fZ = new Finger(f);
iCurrentMode = COMBINED;
break;
}else if(picked==PICKING_OK) {
ObjectFingers.insert(pair<int,Finger> (f->id,Finger(f)));
break;
}
}
break;
case MOVE: // Simple update of the finger
if(fZ!=NULL && iLastFinger == fZ->id){
fZ->update(f);
break;
}else if(ObjectFingers.find(f->id)!=ObjectFingers.end()){
ObjectFingers.find(f->id)->second.update(f);
break;
}
break;
case RELEASE: // A bit more tricky
if(iCurrentMode == TRANSLATION){
if(ObjectFingers.find(f->id)!=ObjectFingers.end()){
ObjectFingers.erase(f->id);
iCurrentMode = INIT_STATE;
updateSelected();
break;
}
break;
}else if(iCurrentMode == ROTATION){
if(ObjectFingers.find(f->id)!=ObjectFingers.end()){
ObjectFingers.erase(f->id);
if(ObjectFingers.size()==1)
{
Vecteur3D temp = ObjectFingers.begin()->second.cvec;
Vector3 temp2;
// with two fingers, sometimes, when releasing, we are not on the object anymore ...
if(mini_picking(Vector2(temp.x,temp.y),temp2)==PICKING_OK){
iCurrentMode = TRANSLATION;
}else{
iCurrentMode = NOTHING;
}
}
break;
}
break;
}else if(iCurrentMode == Z_TRANSLATION){
if(fZ!=NULL && iLastFinger == fZ->id){
delete fZ;
fZ = NULL;
iCurrentMode = TRANSLATION;
break;
}else if(ObjectFingers.find(f->id)!=ObjectFingers.end()){
ObjectFingers.erase(f->id);
if(ObjectFingers.size()==0)
{
iCurrentMode = NOTHING;
updateSelected();
}
break;
}
break;
}else if(iCurrentMode == COMBINED){
if(ObjectFingers.find(f->id)!=ObjectFingers.end()){
ObjectFingers.erase(f->id);
int size = ObjectFingers.size();
if(size==1)
{
Vector2 temp = Vector2(ObjectFingers.begin()->second.cvec.x,ObjectFingers.begin()->second.cvec.y);
Vector3 temp2;
int picking = mini_picking(temp,temp2);
if(picking==PICKING_OK)
{
iCurrentMode = Z_TRANSLATION;
vMovePrev = Vector2::ZERO;
}else{
iCurrentMode = NOTHING;
updateSelected();
}
}else if(size==0)
{
iCurrentMode = NOTHING;
updateSelected();
}
break;
}else if(fZ!=NULL && iLastFinger == fZ->id){
delete fZ;
fZ = NULL;
iCurrentMode = ROTATION;
break;
}
break;
}else if(iCurrentMode == NOTHING){
if(fZ!=NULL && iLastFinger == fZ->id){
delete fZ;
fZ = NULL;
}else if(ObjectFingers.find(f->id)!=ObjectFingers.end()){
ObjectFingers.erase(f->id);
}
if(ObjectFingers.empty() && fZ==NULL) iCurrentMode = INIT_STATE;
break;
}
}
}
void OgreListener::ds3_gesture(int iLastTouch, int iLastFinger, Finger *f){
if(iCurrentMode == INIT_STATE){
}else if(iCurrentMode == TRANSLATION){
// Only one finger, we use its position to move the object
if(ObjectFingers.begin()!=ObjectFingers.end())
{
vMoveCur = Vector2(ObjectFingers.begin()->second.cvec.x,ObjectFingers.begin()->second.cvec.y);
}
}else if(iCurrentMode == Z_TRANSLATION){
// two fingers : direct and indirect
// tz
vZCur = Vector2(fZ->cvec.x,fZ->cvec.y);
//tx,ty
if(ObjectFingers.begin()!=ObjectFingers.end())
{
vMoveCur = Vector2(ObjectFingers.begin()->second.cvec.x,ObjectFingers.begin()->second.cvec.y);
}
}else if(iCurrentMode == ROTATION){
// We create a map with vector3 :
// .x & .y = position
// .z = id
std::map<int,Finger>::iterator it = ObjectFingers.begin();
vRotationCur.clear();
for(it; it!=ObjectFingers.end();it++)
{
vRotationCur.push_back(Vector3(it->second.cvec.x,it->second.cvec.y,it->second.id));
}
}else if(iCurrentMode == COMBINED){
// a mix of above
std::map<int,Finger>::iterator it = ObjectFingers.begin();
vRotationCur.clear();
for(it; it!=ObjectFingers.end();it++)
{
vRotationCur.push_back(Vector3(it->second.cvec.x,it->second.cvec.y,it->second.id));
}
vZCur = Vector2(fZ->cvec.x,fZ->cvec.y);
}
}
void OgreListener::ds3_interaction(){
if(iCurrentMode==INIT_STATE){
// reset everything
vRotationCur.clear();
vZCur = Vector2::ZERO;
vMoveCur = Vector2::ZERO;
vMovePrev = Vector2::ZERO;
vZPrev = Vector2::ZERO;
vRotationPrev.clear();
}else if(iCurrentMode==TRANSLATION){
// tx, ty avec vMoveCur
translate();
//update
vMovePrev = vMoveCur;
// reset other
vZPrev = Vector2::ZERO;
vRotationPrev.clear();
}else if(iCurrentMode==Z_TRANSLATION){
// tx, ty avec vMoveCur
translate();
// tz avec vZCur
z_translate();
// update
vMovePrev = vMoveCur;
vZPrev = vZCur;
// reset other
vRotationPrev.clear();
}else if(iCurrentMode==ROTATION){
// Handle rx,ry,rz
if(vRotationPrev.size() == vRotationCur.size())
{
direct3D_rotation();
}else{
init_direct3D();
}
// update
vRotationPrev = vRotationCur;
//reset other
vZPrev = Vector2::ZERO;
vMovePrev = Vector2::ZERO;
}else if(iCurrentMode==COMBINED){
// Handle rx,ry,rz
if(vRotationPrev.size() == vRotationCur.size())
{
direct3D_rotation();
}else{
init_direct3D();
}
// tz
z_translate();
// update
vZPrev = vZCur;
vRotationPrev = vRotationCur;
// reset other
vMovePrev = Vector2::ZERO;
}
moveObject();
}
//------------------------------
// STICKY TOOLS FUNCTION
//------------------------------
void OgreListener::sticky_mode(int iLastTouch, int iLastFinger, Finger *f){
switch(iLastTouch)
{
case PRESS:
if (iCurrentMode == INIT_STATE)
{
Vector3 vPicking;
int picked = basic_picking(f,vPicking);
if(picked==PICKING_OK) {
ObjectFingers.insert(pair<int,Finger> (f->id,Finger(f)));
iCurrentMode = TRANSLATION;
break;
}
break;
}else if(iCurrentMode == TRANSLATION){
Vector3 vPicking;
int picked = basic_picking(f,vPicking);
if(picked==PICKING_OK) {
ObjectFingers.insert(pair<int,Finger> (f->id,Finger(f)));
iCurrentMode = TOUCH_2;
break;
}else{
fRoll = new Finger(f);
iCurrentMode = TOUCH_3;
break;
}
}else if(iCurrentMode == TOUCH_3){
Vector3 vPicking;
int picked = basic_picking(f,vPicking);
if(picked==PICKING_OK) {
ObjectFingers.insert(pair<int,Finger> (f->id,Finger(f)));
iCurrentMode = COMBINED;
break;
}
break;
}else if(iCurrentMode == TOUCH_2){
Vector3 vPicking;
int picked = basic_picking(f,vPicking);
if(picked==NO_PICK) {
fRoll = new Finger(f);
iCurrentMode = COMBINED;
break;
}
}
break;
case MOVE:
if(fRoll!=NULL && iLastFinger == fRoll->id){
fRoll->update(f);
break;
}else if(ObjectFingers.find(f->id)!=ObjectFingers.end()){
ObjectFingers.find(f->id)->second.update(f);
break;
}
break;
case RELEASE:
if(iCurrentMode == TRANSLATION){
if(ObjectFingers.find(f->id)!=ObjectFingers.end()){
ObjectFingers.erase(f->id);
iCurrentMode = INIT_STATE;
updateSelected();
break;
}
break;
}else if(iCurrentMode == TOUCH_2){
if(ObjectFingers.find(f->id)!=ObjectFingers.end()){
ObjectFingers.erase(f->id);
if(ObjectFingers.size()==1)
{
Vecteur3D temp = ObjectFingers.begin()->second.cvec;
Vector3 temp2;
if(mini_picking(Vector2(temp.x,temp.y),temp2)==PICKING_OK){
iCurrentMode = TRANSLATION;
}else{
iCurrentMode = NOTHING;
}
}
break;
}
break;
}else if(iCurrentMode == TOUCH_3){
if(fRoll!=NULL && iLastFinger == fRoll->id){
delete fRoll;
fRoll = NULL;
iCurrentMode = TRANSLATION;
break;
}else if(ObjectFingers.find(f->id)!=ObjectFingers.end()){
ObjectFingers.erase(f->id);
if(ObjectFingers.size()==0)
{
iCurrentMode = NOTHING;
updateSelected();
}
break;
}
break;
}else if(iCurrentMode == COMBINED){
if(ObjectFingers.find(f->id)!=ObjectFingers.end()){
ObjectFingers.erase(f->id);
int size = ObjectFingers.size();
if(size==1)
{
Vector2 temp = Vector2(ObjectFingers.begin()->second.cvec.x,ObjectFingers.begin()->second.cvec.y);
Vector3 temp2;
int picking = mini_picking(temp,temp2);
if(picking==PICKING_OK)
{
iCurrentMode = TOUCH_3;
vMovePrev = Vector2::ZERO;
}else{
iCurrentMode = NOTHING;
updateSelected();
}
}else if(size==0)
{
iCurrentMode = NOTHING;
updateSelected();
}
break;
}else if(fRoll!=NULL && iLastFinger == fRoll->id){
delete fRoll;
fRoll = NULL;
iCurrentMode = TOUCH_2;
break;
}
break;
}else if(iCurrentMode == NOTHING){
if(fRoll!=NULL && iLastFinger == fRoll->id){
delete fRoll;
fRoll = NULL;
}else if(ObjectFingers.find(f->id)!=ObjectFingers.end()){
ObjectFingers.erase(f->id);
}
if(ObjectFingers.empty() && fRoll==NULL) iCurrentMode = INIT_STATE;
break;
}
}
}
void OgreListener::sticky_gesture(int iLastTouch, int iLastFinger, Finger *f){
if(iCurrentMode == INIT_STATE){
}else if(iCurrentMode == TRANSLATION){
//tx,ty
if(ObjectFingers.begin()!=ObjectFingers.end())
{
vMoveCur = Vector2(ObjectFingers.begin()->second.cvec.x,ObjectFingers.begin()->second.cvec.y);
}
}else if(iCurrentMode == TOUCH_3){
//rx, ry
vRollCur = Vector2(fRoll->cvec.x,fRoll->cvec.y);
//tx,ty
if(ObjectFingers.begin()!=ObjectFingers.end())
{
vMoveCur = Vector2(ObjectFingers.begin()->second.cvec.x,ObjectFingers.begin()->second.cvec.y);
}
}else if(iCurrentMode == TOUCH_2){
// tx, ty, tz, rz
std::map<int,Finger>::iterator it = ObjectFingers.begin();
vRotationCur.clear();
for(it; it!=ObjectFingers.end();it++)
{
vRotationCur.push_back(Vector3(it->second.cvec.x,it->second.cvec.y,it->second.id));
}
}else if(iCurrentMode == COMBINED){
// tx, ty, tz, rz
std::map<int,Finger>::iterator it = ObjectFingers.begin();
vRotationCur.clear();
for(it; it!=ObjectFingers.end();it++)
{
vRotationCur.push_back(Vector3(it->second.cvec.x,it->second.cvec.y,it->second.id));
}
// rx, ry
vRollCur = Vector2(fRoll->cvec.x,fRoll->cvec.y);
}
}
void OgreListener::sticky_interaction(){
if(iCurrentMode==INIT_STATE){
vMovePrev = Vector2::ZERO;
vRotationPrev.clear();
vRollPrev = Vector2::ZERO;
vMoveCur = Vector2::ZERO;
vRotationCur.clear();
vRollCur = Vector2::ZERO;
}else if(iCurrentMode==TRANSLATION){
// tx, ty
translate();
// update
vMovePrev = vMoveCur;
// reset other
vRotationPrev.clear();
vRollPrev = Vector2::ZERO;
}else if(iCurrentMode==TOUCH_2){
// tx, ty, tz, rz
if(vRotationPrev.size() == vRotationCur.size())
{
sticky_4dof();
}else{
init_direct3D();
}
// update
vRotationPrev = vRotationCur;
// reset other
vRollPrev = Vector2::ZERO;
vMovePrev = Vector2::ZERO;
}else if(iCurrentMode==TOUCH_3){
// tx, ty
translate();
// rx, ry
shallow_pitch();
// update
vMovePrev = vMoveCur;
vRollPrev = vRollCur;
// reset other
vRotationPrev.clear();
}else if(iCurrentMode==COMBINED){
// tx, ty, tz, rz
if(vRotationPrev.size() == vRotationCur.size())
{
sticky_4dof();
}else{
init_direct3D();
}
// rx,ry
shallow_pitch();
// update
vRotationPrev = vRotationCur;
vRollPrev = vRollCur;
// reset other
vMovePrev = Vector2::ZERO;
}
moveObject();
}
//------------------------------
// SCREENSPACE FUNCTION
//------------------------------
void OgreListener::nx_mode(int iLastTouch, int iLastFinger, Finger *f){
switch(iLastTouch)
{
case PRESS:
if (iCurrentMode == INIT_STATE)
{
Vector3 vPicking;
int picked = basic_picking(f,vPicking);
if(picked==PICKING_OK) {
ObjectFingers.insert(pair<int,Finger> (f->id,Finger(f)));
iCurrentMode = TRANSLATION;
break;
}
break;
}else if(iCurrentMode == TRANSLATION){
Vector3 vPicking;
int picked = basic_picking(f,vPicking);
if(picked==PICKING_OK) {
ObjectFingers.insert(pair<int,Finger> (f->id,Finger(f)));
iCurrentMode = TOUCH_2;
break;
}
}else if(iCurrentMode == TOUCH_2){
Vector3 vPicking;
int picked = basic_picking(f,vPicking);
if(picked==PICKING_OK) {
ObjectFingers.insert(pair<int,Finger> (f->id,Finger(f)));
iCurrentMode = TOUCH_3;
break;
}
}else if(iCurrentMode == TOUCH_3){
Vector3 vPicking;
int picked = basic_picking(f,vPicking);
if(picked==PICKING_OK) {
ObjectFingers.insert(pair<int,Finger> (f->id,Finger(f)));
break;
}
}
break;
case MOVE:
if(ObjectFingers.find(f->id)!=ObjectFingers.end()){
ObjectFingers.find(f->id)->second.update(f);
break;
}
break;
case RELEASE:
if(iCurrentMode == TRANSLATION){
if(ObjectFingers.find(f->id)!=ObjectFingers.end())
{
ObjectFingers.erase(f->id);
iCurrentMode = INIT_STATE;
updateSelected();
break;
}
break;
}else if(iCurrentMode == TOUCH_2){
if(ObjectFingers.find(f->id)!=ObjectFingers.end()){
ObjectFingers.erase(f->id);
Vecteur3D temp = ObjectFingers.begin()->second.cvec;
Vector3 temp2;
if(mini_picking(Vector2(temp.x,temp.y),temp2)==PICKING_OK){
iCurrentMode = TRANSLATION;
}else{
iCurrentMode = NOTHING;
}
break;
}
break;
}else if(iCurrentMode == TOUCH_3){
if(ObjectFingers.find(f->id)!=ObjectFingers.end()){
ObjectFingers.erase(f->id);
int size = ObjectFingers.size();
if(size==2)
{
iCurrentMode = TOUCH_2;
break;
}
}
break;
}else if(iCurrentMode == NOTHING){
if(ObjectFingers.find(f->id)!=ObjectFingers.end()){
ObjectFingers.erase(f->id);
}
if(ObjectFingers.empty()) iCurrentMode = INIT_STATE;
break;
}
}
}
void OgreListener::nx_gesture(int iLastTouch, int iLastFinger, Finger *f){
if(iCurrentMode == INIT_STATE){
}else if(iCurrentMode == TRANSLATION){
if(ObjectFingers.begin()!=ObjectFingers.end())
{
vMoveCur = Vector2(ObjectFingers.begin()->second.cvec.x,ObjectFingers.begin()->second.cvec.y);
}
}else if(iCurrentMode == TOUCH_2 || iCurrentMode == TOUCH_3){
std::map<int,Finger>::iterator it = ObjectFingers.begin();
vRotationCur.clear();
for(it; it!=ObjectFingers.end();it++)
{
vRotationCur.push_back(Vector3(it->second.cvec.x,it->second.cvec.y,it->second.id));
}
}
}
void OgreListener::nx_interaction(){
if(iCurrentMode==INIT_STATE){
vRotationCur.clear();
vMoveCur = Vector2::ZERO;
vMovePrev = Vector2::ZERO;
vRotationPrev.clear();
}else if(iCurrentMode==TRANSLATION){
// tx, ty avec vMoveCur
translate();
// update
vMovePrev = vMoveCur;
// reset
vRotationPrev.clear();
}else if(iCurrentMode==TOUCH_2){
// tz, rx, ry, rz
if(vRotationPrev.size() == vRotationCur.size())
{
direct3D_rotation_z();
}else{
init_direct3D();
}
// update
vRotationPrev = vRotationCur;
// reset
vMovePrev = Vector2::ZERO;
}else if(iCurrentMode==TOUCH_3){
// tx, ty, tz, rx, ry, rz
if(vRotationPrev.size() == vRotationCur.size())
{
direct3D_full();
}else{
init_direct3D();
}
// update
vRotationPrev = vRotationCur;
// reset
vMovePrev = Vector2::ZERO;
}
moveObject();
}
//------------------------------
// INTERACTION SUB-FUNCTION
//------------------------------
void OgreListener::init_offset(){
Vector3 vectorResult;
int picking = mini_picking(vMoveCur,vectorResult);
if(picking==PICKING_OK)
{
vOffset = vectorResult; // Store the exact point picked !
}
}
// handle 2DOF translation
void OgreListener::translate(){
if(vMovePrev==Vector2::ZERO)
{
init_offset(); // if we just start interaction, init !
}else if(vMoveCur != vMovePrev){
// I used a planed defined with a Vector3
Vector3 vPlanCamera = mCamNode->getOrientation()*Vector3(0,0,-1);
// and a point (where we touched the object in init_offset)
Plane plan(vPlanCamera,vOffset);
Vector3 vCur;
if(plane_picking(vMoveCur,plan,vCur))
{
// translation with 2DOF
vectorTranslation = (vCur-vOffset);
// updating the offset
vOffset = vCur;
}
}
}
// handle pitch and roll for sticky tools
void OgreListener::shallow_pitch(){
if(vRollPrev!=Vector2::ZERO && vRollCur != vRollPrev)
{
real roll = (vRollCur.x - vRollPrev.x)*v3K1;
real pitch = (vRollCur.y - vRollPrev.y)*v3K2;
Quaternion qY,qX;
if(roll!=0.0) qX = Quaternion(Radian(roll),mCamNode->getOrientation()*Vector3(0,1,0));
if(pitch!=0.0) qY = Quaternion(Radian(pitch),mCamNode->getOrientation()*Vector3(1,0,0));
quatRotation = qX * qY * quatRotation;
//quatRotation = (quatRotation.operator +(qX)).operator +(qY);
}
}
// handle translation among the third dimension
void OgreListener::z_translate(){
if(vZPrev!=Vector2::ZERO && vZCur!=vZPrev){
Vector2 vDiff = vZCur - vZPrev;
real interpolate_z = itp_z.Interpolate(vDiff.y) * vDiff.y; // we used an optional interpolation
// We want to object to stay under the finger, we thus will move it among the ray defined by vMoveCur (position of the translation finger)
Real impact = 0;
Ray mouseRay = mSceneMgr->getCamera("Camera")->getCameraToViewportRay(vMoveCur.x,vMoveCur.y);
mRaySceneQuery->setQueryMask(OK_MASK);
mRaySceneQuery->setRay(mouseRay);
mRaySceneQuery->setSortByDistance(true);
RaySceneQueryResult &result = mRaySceneQuery->execute();
RaySceneQueryResult::iterator itr = result.begin();
while (itr != result.end() && !(IsNamePickedOK(itr->movable->getName())))
{
itr++;
}
if (itr != result.end() && itr->movable)
{
impact = itr->distance;
}
if (impact!=0)
{
// To have our translation vector, we proceed to a difference
Vector3 oldpos = mouseRay.getPoint(impact); // Current point
Vector3 newpos = mouseRay.getPoint(impact + interpolate_z); // Current point + z_translation
Vector3 diff = oldpos-newpos; // we obtain a distance among the ray
vectorTranslation += diff; // that we add to vectorTranslation
}
}
}
// init the solver
void OgreListener::init_direct3D(){
mapPoint3DToMatch.clear();
mapPoint2D.clear();
// We will pick every finger so as to match 2D point with 3D points
int nbFingerOK = 0;
int picking;
Vector3 vPicked;
Vector3 vFingerCur;
std::list<Vector3>::iterator itcur = vRotationCur.begin();
for(itcur; itcur!=vRotationCur.end();itcur++)
{
vFingerCur = (*itcur);
picking = mini_picking(Vector2(vFingerCur.x,vFingerCur.y),vPicked);
if(picking == PICKING_OK)
{
mapPoint3DToMatch.insert(pair<int,Vector3>(vFingerCur.z,vPicked)); // id of the finger, with 3D position of the object in the 3D scene
mapPoint2D.insert(pair<int,Vector2>(vFingerCur.z,Vector2(vFingerCur.x,vFingerCur.y))); // id of the finger, with 2D position on the screen
nbFingerOK++;
}
}
iNbFingerPicked = nbFingerOK;
}
// Use of the solver for 4 DOF
void OgreListener::sticky_4dof(){
// They are the same size (cf _interaction) proceed if one has moved
if(vRotationCur != vRotationPrev)
{
int nbFingerOK = 0;
// First, update the map2D et map3D ...
Vector3 vPicked;
Vector2 vFingerCur;
Vector2 vFingerPrev;
Vector2 vSommeFingerRotationPrev = Vector2::ZERO;
std::list<Vector3>::iterator itprev = vRotationPrev.begin();
std::list<Vector3>::iterator itcur = vRotationCur.begin();
// for every finger
for(itprev; itprev!=vRotationPrev.end();itprev++)
{
vFingerPrev = Vector2((*itprev).x,(*itprev).y);
vFingerCur = Vector2((*itcur).x,(*itcur).y);
int fID = (*itprev).z;
// We will save the total of movement for later
vSommeFingerRotationPrev = vSommeFingerRotationPrev + vFingerPrev;
// update of the map2D and map3D if picking OK
std::map<int,Vector2>::iterator it2D = mapPoint2D.find(fID);
std::map<int,Vector3>::iterator it3D = mapPoint3DToMatch.find(fID);
if(it2D != mapPoint2D.end()){
// if picking OK
if(it3D != mapPoint3DToMatch.end() && mini_picking(it2D->second,vPicked)==PICKING_OK )
{
// then update map3D
it3D->second = vPicked;
}
// update map2D
it2D->second = vFingerCur;
}
itcur++;
}
// If we have at least two fingers
if (mapPoint2D.size()>1)
{
vCentreObject = selectedNode->getPosition();
// problem size
int n = mapPoint2D.size();
// 1 - Compute the center of the previous fingers.
Vector2 vPrevCentre2D = (vSommeFingerRotationPrev)/n;
// 2 - Compute the sum of the displacements from the center.
Vector2 displacement = Vector2::ZERO;
std::map<int,Finger>::iterator it = ObjectFingers.begin();
for(it; it!=ObjectFingers.end();it++)
{
Finger *temp = &(it->second);
displacement = displacement + (Vector2(temp->cvec.x,temp->cvec.y)-vPrevCentre2D);
}
// will store the minimization via extern function
double *x = (double *)malloc(n*2*sizeof(double));
for (int i = 0; i < n*2; i++)
x[i] = 0.0;
// number of DOF to solve, here tx, ty, tz, rz
int m = 4;
// initial value of our parameters, to guide the solver in the right way
double p[4] = { 0.0, 0.0, 0.0, 0.0};
if (displacement.x < 0.0)
{
p[0] = -0.01;
}else{
p[0] = 0.01;
}
if (displacement.y < 0.0)
{
p[1] = -0.01;
}else{
p[1] = 0.01;
}
// Allocate some data structure for levenberg.
double opts[LM_OPTS_SZ];
opts[0] = LM_INIT_MU;
opts[1] = 1E-15;
opts[2] = 1E-15;
opts[3] = 1E-20;
opts[4] = LM_DIFF_DELTA;
double info[LM_INFO_SZ];
// main call for the solver, it will use the diff2D_4DOF function defined globally
int ret = dlevmar_dif(diff2D_4DOF, p, x, m, n*2, 1000, opts, info, NULL, NULL, NULL);
// if OK
if ((fabs(info[6] -1.0) < 0.00001) || (fabs(info[6] - 2.0) < 0.00001))
{
// retry value from solver and adjust vectorTranslation and quatRotation
Vector3 txy = Vector3(p[0],p[1],0);
Vector3 scale = vCentreObject- mCamera->getPositionForViewUpdate();
scale = scale * p[2];
vectorTranslation = txy + scale;
quatRotation = Quaternion(Radian(p[3]),mCamera->getOrientationForViewUpdate()*Vector3 (0,0,1));
// Update the map3D point with the rotation : necessary when overconstrained and finger not on the object anymore
std::map<int,Vector3>::iterator it3D = mapPoint3DToMatch.begin();
for(it3D;it3D!=mapPoint3DToMatch.end();it3D++){
Vector3 vPoint3D = it3D->second;
it3D->second = quatRotation*(vPoint3D-vCentreObject) + vCentreObject + vectorTranslation;
}
}
// Cleaning stuff
free(x);
}
}
}
// Cf sticky_4dof, exact same structure
void OgreListener::direct3D_rotation(){
if(vRotationCur != vRotationPrev)
{
int nbFingerOK = 0;
// On va mettre à jour les positions 2D si nécessaire et calculer le déplacement avec prev !
Vector3 vPicked;
Vector2 vFingerCur;
Vector2 vFingerPrev;
Vector2 vSommeFingerRotationPrev = Vector2::ZERO;
std::list<Vector3>::iterator itprev = vRotationPrev.begin();
std::list<Vector3>::iterator itcur = vRotationCur.begin();
for(itprev; itprev!=vRotationPrev.end();itprev++)
{
vFingerPrev = Vector2((*itprev).x,(*itprev).y);
vFingerCur = Vector2((*itcur).x,(*itcur).y);
int fID = (*itprev).z;
// Calcul prev
vSommeFingerRotationPrev = vSommeFingerRotationPrev + vFingerPrev;
// Mise à jour coordonnée 2D et si picking hop on met à jour
std::map<int,Vector2>::iterator it2D = mapPoint2D.find(fID);
std::map<int,Vector3>::iterator it3D = mapPoint3DToMatch.find(fID);
// Si la fonction find a bien finder et
if(it2D != mapPoint2D.end()){
// Si picking ok
if(it3D != mapPoint3DToMatch.end() && mini_picking(it2D->second,vPicked)==PICKING_OK )
{
// alors on met à jour les point3D associé avec le point2D prev!
it3D->second = vPicked;
}
// On met à jour le point 2D avec la valeur courante
it2D->second = vFingerCur;
}
itcur++;
}
// Si OK
if (mapPoint2D.size()>1)
{
// Centre de rotation : centre Objet
vCentreObject = selectedNode->getPosition();
// taille du problème : ici nombre de doigts
int n = mapPoint2D.size();
// 1 - Compute the center of the previous fingers.
Vector2 vPrevCentre2D = (vSommeFingerRotationPrev)/n;
// 2 - Compute the sum of the displacements from the center.
Vector2 displacement = Vector2::ZERO;
std::map<int,Finger>::iterator it = ObjectFingers.begin();
for(it; it!=ObjectFingers.end();it++)
{
Finger *temp = &(it->second);
displacement = displacement + (Vector2(temp->cvec.x,temp->cvec.y)-vPrevCentre2D);
}
// Tableau pour ce qu'on va calculer
double *x = (double *)malloc(n*2*sizeof(double));
for (int i = 0; i < n*2; i++)
x[i] = 0.0;
// (ex, ey, ez)
int m = 3;
double p[3] = { 0.0, 0.0, 0.0};
if (displacement.x < 0.0)
{
p[0] = -0.01;
}else{
p[0] = 0.01;
}
if (displacement.y < 0.0)
{
p[1] = -0.01;
}else{
p[1] = 0.01;
}
double opts[LM_OPTS_SZ];
opts[0] = LM_INIT_MU;
opts[1] = 1E-17;
opts[2] = 1E-17;
opts[3] = 1E-17;
opts[4] = LM_DIFF_DELTA;
double info[LM_INFO_SZ];
int ret = dlevmar_dif(diff2D_3DOF, p, x, m, n*2, 1000, opts, info, NULL, NULL, NULL);
// Si OK
if ((fabs(info[6] -1.0) < 0.00001) || (fabs(info[6] - 2.0) < 0.00001))
{
Quaternion qRot;
qRot.x = p[0];
qRot.y = p[1];
qRot.z = p[2];
qRot.normalise();
quatRotation = qRot;
// Mise à jour des points 3D en tenant compte de la rotation !
std::map<int,Vector3>::iterator it3D = mapPoint3DToMatch.begin();
for(it3D;it3D!=mapPoint3DToMatch.end();it3D++){
Vector3 vPoint3D = it3D->second;
it3D->second = qRot*(vPoint3D-vCentreObject) + vCentreObject;
}
}
// Cleaning stuff
free(x);
}
}
}
// Cf sticky_4dof, exact same structure
void OgreListener::direct3D_rotation_z(){
if(vRotationCur != vRotationPrev)
{
int nbFingerOK = 0;
// On va mettre à jour les positions 2D si nécessaire et calculer le déplacement avec prev !
Vector3 vPicked;
Vector2 vFingerCur;
Vector2 vFingerPrev;
Vector2 vSommeFingerRotationPrev = Vector2::ZERO;
std::list<Vector3>::iterator itprev = vRotationPrev.begin();
std::list<Vector3>::iterator itcur = vRotationCur.begin();
for(itprev; itprev!=vRotationPrev.end();itprev++)
{
vFingerPrev = Vector2((*itprev).x,(*itprev).y);
vFingerCur = Vector2((*itcur).x,(*itcur).y);
int fID = (*itprev).z;
// Calcul prev
vSommeFingerRotationPrev = vSommeFingerRotationPrev + vFingerPrev;
// Mise à jour coordonnée 2D et si picking hop on met à jour
std::map<int,Vector2>::iterator it2D = mapPoint2D.find(fID);
std::map<int,Vector3>::iterator it3D = mapPoint3DToMatch.find(fID);
// Si la fonction find a bien finder et
if(it2D != mapPoint2D.end()){
// Si picking ok
if(it3D != mapPoint3DToMatch.end() && mini_picking(it2D->second,vPicked)==PICKING_OK )
{
// alors on met à jour les point3D associé avec le point2D prev!
it3D->second = vPicked;
}
// On met à jour le point 2D avec la valeur courante
it2D->second = vFingerCur;
}
itcur++;
}
// Si OK
if (mapPoint2D.size()>1)
{
// Centre de rotation : centre Objet
vCentreObject = selectedNode->getPosition();
// taille du problème : ici nombre de doigts
int n = mapPoint2D.size();
// 1 - Compute the center of the previous fingers.
Vector2 vPrevCentre2D = (vSommeFingerRotationPrev)/n;
// 2 - Compute the sum of the displacements from the center.
Vector2 displacement = Vector2::ZERO;
std::map<int,Finger>::iterator it = ObjectFingers.begin();
for(it; it!=ObjectFingers.end();it++)
{
Finger *temp = &(it->second);
displacement = displacement + (Vector2(temp->cvec.x,temp->cvec.y)-vPrevCentre2D);
}
// Tableau pour ce qu'on va calculer
double *x = (double *)malloc(n*2*sizeof(double));
for (int i = 0; i < n*2; i++)
x[i] = 0.0;
// (tz, ex, ey, ez)
int m = 3;
double p[4] = { 0.0, 0.0, 0.0, 0.0};
if (displacement.x < 0.0)
{
p[1] = -0.01;
}else{
p[1] = 0.01;
}
if (displacement.y < 0.0)
{
p[2] = -0.01;
}else{
p[2] = 0.01;
}
double opts[LM_OPTS_SZ];
opts[0] = LM_INIT_MU;
opts[1] = 1E-15;
opts[2] = 1E-15;
opts[3] = 1E-20;
opts[4] = LM_DIFF_DELTA;
double info[LM_INFO_SZ];
int ret = dlevmar_dif(diff2D_4DOFz, p, x, m, n*2, 1000, opts, info, NULL, NULL, NULL);
// Si OK
if ((fabs(info[6] -1.0) < 0.00001) || (fabs(info[6] - 2.0) < 0.00001))
{
Vector3 deltaPos = vCentreObject- mCamera->getPositionForViewUpdate();
vectorTranslation = deltaPos * p[0];
Quaternion qRot;
qRot.x = p[1];
qRot.y = p[2];
qRot.z = p[3];
qRot.normalise();
quatRotation = qRot;
// Mise à jour des points 3D en tenant compte de la rotation !
std::map<int,Vector3>::iterator it3D = mapPoint3DToMatch.begin();
for(it3D;it3D!=mapPoint3DToMatch.end();it3D++){
Vector3 vPoint3D = it3D->second;
it3D->second = quatRotation*(vPoint3D-vCentreObject) + vCentreObject + vectorTranslation;
}
}
// Cleaning stuff
free(x);
}
}
}
// Cf sticky_4dof, exact same structure
void OgreListener::direct3D_full(){
if(vRotationCur != vRotationPrev)
{
int nbFingerOK = 0;
// On va mettre à jour les positions 2D si nécessaire et calculer le déplacement avec prev !
Vector3 vPicked;
Vector2 vFingerCur;
Vector2 vFingerPrev;
Vector2 vSommeFingerRotationPrev = Vector2::ZERO;
std::list<Vector3>::iterator itprev = vRotationPrev.begin();
std::list<Vector3>::iterator itcur = vRotationCur.begin();
for(itprev; itprev!=vRotationPrev.end();itprev++)
{
vFingerPrev = Vector2((*itprev).x,(*itprev).y);
vFingerCur = Vector2((*itcur).x,(*itcur).y);
int fID = (*itprev).z;
// Calcul prev
vSommeFingerRotationPrev = vSommeFingerRotationPrev + vFingerPrev;
// Mise à jour coordonnée 2D et si picking hop on met à jour
std::map<int,Vector2>::iterator it2D = mapPoint2D.find(fID);
std::map<int,Vector3>::iterator it3D = mapPoint3DToMatch.find(fID);
// Si la fonction find a bien finder et
if(it2D != mapPoint2D.end()){
// Si picking ok
if(it3D != mapPoint3DToMatch.end() && mini_picking(it2D->second,vPicked)==PICKING_OK )
{
// alors on met à jour les point3D associé avec le point2D prev!
it3D->second = vPicked;
}
// On met à jour le point 2D avec la valeur courante
it2D->second = vFingerCur;
}
itcur++;
}
// Si OK
if (mapPoint2D.size()>2)
{
// Centre de rotation : centre Objet
vCentreObject = selectedNode->getPosition();
// taille du problème : ici nombre de doigts
int n = mapPoint2D.size();
// 1 - Compute the center of the previous fingers.
Vector2 vPrevCentre2D = (vSommeFingerRotationPrev)/n;
// 2 - Compute the sum of the displacements from the center.
Vector2 displacement = Vector2::ZERO;
std::map<int,Finger>::iterator it = ObjectFingers.begin();
for(it; it!=ObjectFingers.end();it++)
{
Finger *temp = &(it->second);
displacement = displacement + (Vector2(temp->cvec.x,temp->cvec.y)-vPrevCentre2D);
}
// Tableau pour ce qu'on va calculer
double *x = (double *)malloc(n*2*sizeof(double));
for (int i = 0; i < n*2; i++)
x[i] = 0.0;
// (tx,ty,tz, ex, ey, ez)
int m = 6;
double p[6] = { 0.0, 0.0, 0.1, 0.1 , 0.1, 0.1};
if (displacement.x < 0.0)
{
p[0] = -0.01;
}else{
p[0] = 0.01;
}
if (displacement.y < 0.0)
{
p[1] = -0.01;
}else{
p[1] = 0.01;
}
double opts[LM_OPTS_SZ];
opts[0] = LM_INIT_MU;
opts[1] = 1E-15;
opts[2] = 1E-15;
opts[3] = 1E-20;
opts[4] = LM_DIFF_DELTA;
double info[LM_INFO_SZ];
int ret = dlevmar_dif(diff2D_6DOF, p, x, m, n*2, 1000, opts, info, NULL, NULL, NULL);
// Si OK
if ((fabs(info[6] -1.0) < 0.00001) || (fabs(info[6] - 2.0) < 0.00001))
{
Quaternion qRot;
qRot.x = p[3];
qRot.y = p[4];
qRot.z = p[5];
qRot.normalise();
quatRotation = qRot;
vectorTranslation = mCamera->getOrientationForViewUpdate() * Vector3 (p[0],p[1],p[2]);
// Mise à jour des points 3D en tenant compte de la rotation !
std::map<int,Vector3>::iterator it3D = mapPoint3DToMatch.begin();
for(it3D;it3D!=mapPoint3DToMatch.end();it3D++){
Vector3 vPoint3D = it3D->second;
it3D->second = quatRotation*(vPoint3D-vCentreObject) + vCentreObject + vectorTranslation;
}
}
// Cleaning stuff
free(x);
}
}
}
//------------------------------
// OBJECT DISPLACEMENT
//------------------------------
void OgreListener::moveObject(){
// TO DO, switch to 4*4 matrix
if(selectedNode != NULL)
{
Quaternion prevQuat = selectedNode->getOrientation();
Vector3 prevTrans = selectedNode->getPosition();
vCentreObject = selectedNode->getPosition();
if(quatRotation!=Quaternion::IDENTITY)
{
// handle if the center of rotation is different from center of the object
Vector3 vTranslation = vCentreRotation - vCentreObject;
if(vCentreRotation!=Vector3::ZERO)
{
// On sait qu'on dois tourner autour de centreRotation
// Calculons le déplacement que ce point fait
Vector3 newPos = quatRotation*vTranslation;
selectedNode->rotate(quatRotation,Node::TS_WORLD);
// maintenant le point de rotation à bouger, on le ramène à sa place en faisant
selectedNode->translate(vCentreRotation-(newPos+vCentreObject));
vCentreRotation = Vector3::ZERO;
}else{
selectedNode->rotate(quatRotation,Node::TS_WORLD);
}
quatRotation = Quaternion::IDENTITY;
}
if(vectorTranslation!=Vector3::ZERO)
{
selectedNode->translate(vectorTranslation);
Vector3 pos = selectedNode->getPosition();
if(vectorTranslation.z != 0.0) // if we change the depth, re init offset !
{
init_offset();
}
vectorTranslation = Vector3::ZERO;
}
}
}
//------------------------------
// SOLVER FUNCTION (EXTERN)
//------------------------------
/* The following function measures the error between the current 3D points and
the reprojected 2D points considering the p vetor. */
/* p is the std::vector of values we are looking for (translations and rotations).
hx is the result of the computation done by the function.
n is the number of values for hx. */
void diff2D_6DOF(double *p, double *hx, int m, int n, void *adata){
/* Retrieve the translation and the rotation from the p std::vector. */
/* Translation values are the first 3 values in the p std::vector. */
Vector3 deltaPos;
deltaPos.x = p[0];
deltaPos.y = p[1];
deltaPos.z = p[2];
deltaPos = gmCamera->getOrientationForViewUpdate() * deltaPos;
/* Quaternion x, y, z are the 3 last values of the p std::vector. */
Quaternion deltaQuat;
deltaQuat.x = p[3];
deltaQuat.y = p[4];
deltaQuat.z = p[5];
deltaQuat.normalise();
/* Save the current transformations, then apply the current transformations from the p std::vector,
project and finally restaure the initial transformations. */
/* For each world point, compute the projected point, depending on the input parameters p. */
std::map<int,Vector3>::iterator itV3;
std::map<int,Vector2>::iterator itV2;
int i=0;
for (itV3=mapPoint3DToMatch.begin();itV3!=mapPoint3DToMatch.end();itV3++)
{
Vector3 v3Prev = (*itV3).second;
itV2 = mapPoint2D.find((*itV3).first);
Vector2 v2DCur = Vector2::ZERO;
if(itV2 != mapPoint2D.end()){
v2DCur = (*itV2).second;
}else{
cout << "erreur diff2D_6DOF" << endl;
return;
}
/* Apply tr & rotate to V3Prev */
Matrix3 temp;
deltaQuat.ToRotationMatrix(temp);
Matrix4 mat(temp);
mat.setTrans(vCentreObject+deltaPos);
Vector3 V3DOK = mat*(v3Prev-vCentreObject);
/* Project the world points to the screen space, using transformations defined by the p std::vector. */
Vector2 v2DTest;
bool proj = get2DCoord(gmCamera,V3DOK,v2DTest);
/* Compute the result, ie the difference between fingers 2D points and
the computed 2D points considering the p std::vector. */
hx[i*2] = (v2DTest.x - v2DCur.x);
hx[i*2 + 1] = (v2DTest.y - v2DCur.y);
/* Update pointeurs */
i++;
itV2++;
}
}
void diff2D_4DOFz(double *p, double *hx, int m, int n, void *adata){
/* Retrieve the translation and the rotation from the p std::vector. */
Vector3 deltaPos = vCentreObject - gmCamera->getPositionForViewUpdate();
/* Translation values are the first 3 values in the p std::vector. */
deltaPos = p[0]*deltaPos;
/* Quaternion x, y, z are the 3 last values of the p std::vector. */
Quaternion deltaQuat;
deltaQuat.x = p[1];
deltaQuat.y = p[2];
deltaQuat.z = p[3];
deltaQuat.normalise();
/* Save the current transformations, then apply the current transformations from the p std::vector,
project and finally restaure the initial transformations. */
/* For each world point, compute the projected point, depending on the input parameters p. */
std::map<int,Vector3>::iterator itV3;
std::map<int,Vector2>::iterator itV2;
int i=0;
for (itV3=mapPoint3DToMatch.begin();itV3!=mapPoint3DToMatch.end();itV3++)
{
Vector3 v3Prev = (*itV3).second;
itV2 = mapPoint2D.find((*itV3).first);
Vector2 v2DCur = Vector2::ZERO;
if(itV2 != mapPoint2D.end()){
v2DCur = (*itV2).second;
}else{
cout << "erreur diff2D_4DOFz" << endl;
return;
}
/* Appliquer la transformation tr et rotate à V3Prev */
Matrix3 temp;
deltaQuat.ToRotationMatrix(temp);
Matrix4 mat(temp);
mat.setTrans(vCentreObject+deltaPos);
Vector3 V3DOK = mat*(v3Prev-vCentreObject);
/* Project the world points to the screen space, using transformations defined by the p std::vector. */
Vector2 v2DTest;
bool proj = get2DCoord(gmCamera,V3DOK,v2DTest);
/* Compute the result, ie the difference between fingers 2D points and
the computed 2D points considering the p std::vector. */
hx[i*2] = (v2DTest.x - v2DCur.x);
hx[i*2 + 1] = (v2DTest.y - v2DCur.y);
/* Update pointeurs */
i++;
itV2++;
}
}
void diff2D_4DOF(double *p, double *hx, int m, int n, void *adata){
/* Retrieve the translation and the rotation from the p std::vector. */
Vector3 deltaPos;
Vector3 txy = Vector3(p[0],p[1],0);
Vector3 scale = vCentreObject - gmCamera->getPositionForViewUpdate();
scale = scale * p[2];
deltaPos = txy + scale;
/* Translation values are the first 3 values in the p std::vector. */
/* Quaternion x, y, z are the 3 last values of the p std::vector. */
Quaternion deltaQuat(Radian(p[3]),gmCamera->getOrientationForViewUpdate()*Vector3(0,0,1));
/* Save the current transformations, then apply the current transformations from the p std::vector,
project and finally restaure the initial transformations. */
/* For each world point, compute the projected point, depending on the input parameters p. */
std::map<int,Vector3>::iterator itV3;
std::map<int,Vector2>::iterator itV2;
int i=0;
for (itV3=mapPoint3DToMatch.begin();itV3!=mapPoint3DToMatch.end();itV3++)
{
Vector3 v3Prev = (*itV3).second;
itV2 = mapPoint2D.find((*itV3).first);
Vector2 v2DCur = Vector2::ZERO;
if(itV2 != mapPoint2D.end()){
v2DCur = (*itV2).second;
}else{
cout << "erreur diff2D_4DOF" << endl;
return;
}
/* Appliquer la transformation tr et rotate à V3Prev */
Matrix3 temp;
deltaQuat.ToRotationMatrix(temp);
Matrix4 mat(temp);
mat.setTrans(vCentreObject+deltaPos);
Vector3 V3DOK = mat*(v3Prev-vCentreObject);
/* Project the world points to the screen space, using transformations defined by the p std::vector. */
Vector2 v2DTest;
bool proj = get2DCoord(gmCamera,V3DOK,v2DTest);
/* Compute the result, ie the difference between fingers 2D points and
the computed 2D points considering the p std::vector. */
hx[i*2] = (v2DTest.x - v2DCur.x);
hx[i*2 + 1] = (v2DTest.y - v2DCur.y);
/* Update pointeurs */
i++;
itV2++;
}
}
void diff2D_3DOF(double *p, double *hx, int m, int n, void *adata){
/* Retrieve the translation and the rotation from the p std::vector. */
/* Translation values are the first 3 values in the p std::vector. */
Quaternion deltaQuat;
deltaQuat.x = p[0];
deltaQuat.y = p[1];
deltaQuat.z = p[2];
deltaQuat.normalise();
/* Save the current transformations, then apply the current transformations from the p std::vector,
project and finally restaure the initial transformations. */
/* For each world point, compute the projected point, depending on the input parameters p. */
std::map<int,Vector3>::iterator itV3;
std::map<int,Vector2>::iterator itV2;
int i=0;
for (itV3=mapPoint3DToMatch.begin();itV3!=mapPoint3DToMatch.end();itV3++)
{
Vector3 v3Prev = (*itV3).second;
itV2 = mapPoint2D.find((*itV3).first);
Vector2 v2DCur = Vector2::ZERO;
if(itV2 != mapPoint2D.end()){
v2DCur = (*itV2).second;
}else{
cout << "erreur diff2D_3DOF" << endl;
return;
}
/* Appliquer la transformation tr et rotate à V3Prev */
/*
Matrix3 temp;
deltaQuat.ToRotationMatrix(temp);
Matrix4 mat(temp);
mat.setTrans(vCentreObject);
*/
Vector3 V3DOK = deltaQuat*(v3Prev-vCentreObject) + vCentreObject;
/* Project the world points to the screen space, using transformations defined by the p std::vector. */
Vector2 v2DTest;
bool proj = get2DCoord(gmCamera,V3DOK,v2DTest);
/* Compute the result, ie the difference between fingers 2D points and
the computed 2D points considering the p std::vector. */
hx[i*2] = (v2DTest.x - v2DCur.x);
hx[i*2 + 1] = (v2DTest.y - v2DCur.y);
/* Update pointeurs */
i++;
itV2++;
}
}
bool get2DCoordMatrix(Matrix4 view, Matrix4 proj, Vector3 pos3D, Vector2 &pos2D){
Vector3 eyeSpacePos = view * pos3D;
Real x,y;
// z < 0 means in front of cam
if (eyeSpacePos.z < 0) {
// calculate projected pos
Vector3 screenSpacePos = proj * eyeSpacePos;
x = screenSpacePos.x;
y = screenSpacePos.y;
// Coming back to [0,1] of the screen
x = (x+1)/2;
y = (-y+1)/2;
pos2D = Vector2(x,y);
return true;
} else {
x = (-eyeSpacePos.x > 0) ? -1 : 1;
y = (-eyeSpacePos.y > 0) ? -1 : 1;
return false;
}
}
bool get2DCoord (Camera* cam,Vector3 pos3D, Vector2 &pos2D){
Vector3 eyeSpacePos = cam->getViewMatrix(true) * pos3D;
Real x,y;
// z < 0 means in front of cam
if (eyeSpacePos.z < 0) {
// calculate projected pos
Vector3 screenSpacePos = cam->getProjectionMatrix() * eyeSpacePos;
x = screenSpacePos.x;
y = screenSpacePos.y;
// Coming back to [0,1] of the screen
x = (x+1)/2;
y = (-y+1)/2;
pos2D = Vector2(x,y);
return true;
} else {
x = (-eyeSpacePos.x > 0) ? -1 : 1;
y = (-eyeSpacePos.y > 0) ? -1 : 1;
return false;
}
}
#ifndef INCLUDED_LISTENER_H
#define INCLUDED_LISTENER_H
#pragma once
/******************
Basic Ogre
*******************/
#include <Ogre.h>
#include <OIS/OIS.h>
/******************
My files
*******************/
#include "lock.h" // Class to handle mutex
#include "finger.h" // To store finger information (basically current position as Vector2)
#include "lm.h" // this comes from http://www.ics.forth.gr/~lourakis/levmar/
#include "mtlib.hpp" // it's our own tuio client, cooked to also handled win7. Should be released very soon !
/******************
Basic CEGUI
*******************/
#include "CEGUI.h"
#include "RendererModules/Ogre/CEGUIOgreRenderer.h"
enum Mode // Mode used to handle interaction
{
INIT_STATE,
NOTHING,
TRANSLATION,
TOUCH_2,
TOUCH_3,
Z_TRANSLATION,
COMBINED,
ROTATION,
};
enum PickInfo // information returned with picking function
{
NO_PICK = 0,
PICKING_OK = 1,
};
enum Interaction // to have a more clear way of dealing with different techniques
{
INTERACTION_DS3,
INTERACTION_STICKY,
INTERACTION_SCREENSPACE,
};
enum QueryFlags // Specific to Ogre, to handle different group of picking
{
OK_MASK = 1<<0,
KO_MASK = 1<<1,
TRACKBALL_MASK = 1<<2,
};
using namespace Ogre;
/******************************************************************************************************************************
Very important, those function are needed for the constraint solver (levmar) and have to be global
*******************************************************************************************************************************/
bool get2DCoord (Camera* cam,Vector3 pos3D, Vector2 &pos2D);
void diff2D_6DOF(double *p, double *hx, int m, int n, void *adata);
void diff2D_4DOFz(double *p, double *hx, int m, int n, void *adata);
void diff2D_4DOF(double *p, double *hx, int m, int n, void *adata);
void diff2D_3DOF(double *p, double *hx, int m, int n, void *adata);
bool get2DCoordMatrix(Matrix4 view, Matrix4 proj, Vector3 pos3D, Vector2 &pos2D);
/**************************************************************
FrameListener : ogre specific
OIS::KeyListener : ogre specific
TouchListener : similar to TUIO client !
**************************************************************/
class OgreListener : public FrameListener, public OIS::KeyListener, public TouchListener
{
public:
OgreListener(OIS::Keyboard *keyboard, Camera* cam, SceneManager *sceneMgr, RenderWindow* Window);
~OgreListener();
//--------------------------------
// BASIC STUFF
//--------------------------------
bool keyPressed(const OIS::KeyEvent &e);
bool keyReleased(const OIS::KeyEvent &e);
bool frameStarted(const FrameEvent& evt);
private:
//-------------------------------
// OGRE
//-------------------------------
SceneManager *mSceneMgr; // The current SceneManager
SceneNode *mCamNode; // The SceneNode the camera is currently attached to
Camera *mCamera;
RenderWindow* mWindow;
OIS::Keyboard *mKeyboard;
bool mContinue;
//---------------------------
// OBJECT MANIPULATION
//--------------------------
SceneNode* selectedNode;
SceneNode* lastSelectedNode;
//-------------------------------
// TOUCH
//-------------------------------
std::map<int,Finger*> fingerMap;
CReadWriteLock multiTouchLock;
void on_touch_detect(TOUCH& args);
void on_touch_update(TOUCH& args);
void on_touch_release(TOUCH& args);
//-------------------------------
// CEGUI Event
//------------------------------
void createCEGUIevent();
bool quit(const CEGUI::EventArgs &e);
//-----------------------------
// INTERACTION MAIN FUNCTION
//-----------------------------
void updateInteraction(int iLastTouch, int iLastFinger, Finger *f);
void updateSelected();
void ds3_gesture(int iLastTouch, int iLastFinger, Finger *f);
void ds3_mode(int iLastTouch, int iLastFinger, Finger *f);
void ds3_interaction();
void sticky_gesture(int iLastTouch, int iLastFinger, Finger *f);
void sticky_mode(int iLastTouch, int iLastFinger, Finger *f);
void sticky_interaction();
void nx_gesture(int iLastTouch, int iLastFinger, Finger *f);
void nx_mode(int iLastTouch, int iLastFinger, Finger *f);
void nx_interaction();
void moveObject();
//-----------------------------
// INTERACTION SUB FUNCTION
//-----------------------------
void init_offset();
void translate();
void shallow_pitch();
void z_translate();
void sticky_4dof();
void init_direct3D();
void direct3D_rotation();
void direct3D_rotation_z();
void direct3D_full();
//----------------------------
// OVERLAYS & FINGERPRINT
//----------------------------
//Ogre2dManager* fingerprint;
void do_debuginfo();
bool printFinger;
//-------------------------------
// PICKING FUNCTION
//-------------------------------
RaySceneQuery *mRaySceneQuery;
bool IsNamePickedOK(String name);
int basic_picking(Finger *fPick, Vector3 &Result);
int mini_picking(Vector2 pos, Vector3 &Result);
bool plane_picking(Vector2 pos, Plane plan, Vector3 &Result);
bool PickEntity(Ray &ray,
Entity ** result,
Vector3 &hitpoint,
Plane &hitplane,
Ogre::uint32 masque = OK_MASK);
void GetMeshInformationEx( const MeshPtr mesh,
size_t &vertex_count,
Vector3* &vertices,
size_t &index_count,
unsigned long* &indices,
const Vector3 &position,
const Quaternion &orient,
const Vector3 &scale);
//-----------------------------
// INTERACTION VARIABLE
//-----------------------------
int iNbFinger; // Used to count all the fingers
int iCurrentMode; // To store the mode we are in
int iCurrentTechnique; // ...
Finger *fZ; // To store indirect finger of z technique
Finger* fRoll; // To store indirect finger of sticky tools
std::map<int,Finger> ObjectFingers; // To store other fingers
// Interaction managment : vector that we will manipulate
Vector2 vMovePrev;
Vector2 vMoveCur;
Vector2 vYawCur;
Vector2 vYawPrev;
Vector2 vRollCur;
Vector2 vRollPrev;
Vector2 vZCur;
Vector2 vZPrev;
std::list<Vector3> vRotationPrev;
std::list<Vector3> vRotationCur;
Vector3 vOffset; // Offset Pyramide
int iNbFingerPicked; // gestion Screenspace
//-----------------------------
// OBJECT MOVEMENT VARIABLE
//-----------------------------
Vector3 vectorTranslation; // will store the translation to apply every frame
Quaternion quatRotation; // will store the rotation to apply every frame
//-----------------------------
// TECHNIQUE SPECIFIC VARIABLE
//-----------------------------
Real v3K1; // Facteur K1 Shallow Technique
Real v3K2; // Facteur K2 Shallow Technique
Real factorZ;
};
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment