Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.