diff --git a/src/fgt/filter_physics/DynamicMeshSubFilter.cpp b/src/fgt/filter_physics/DynamicMeshSubFilter.cpp index 9ddaab1d6..453756c91 100644 --- a/src/fgt/filter_physics/DynamicMeshSubFilter.cpp +++ b/src/fgt/filter_physics/DynamicMeshSubFilter.cpp @@ -2,17 +2,18 @@ #include -DynamicMeshSubFilter::DynamicMeshSubFilter() : m_fps(-1), m_iterations(-1), m_contacts(-1), m_steps(-1), m_seconds(-1), m_bounciness(-1) { +DynamicMeshSubFilter::DynamicMeshSubFilter() : m_fps(-1), m_iterations(-1), m_contacts(-1), m_steps(-1), m_seconds(-1), m_bounciness(-1), m_gravity(-1) { } void DynamicMeshSubFilter::initParameterSet(QAction* action,MeshDocument& md, RichParameterSet & par){ MeshSubFilter::initParameterSet(action, md, par); + par.addParam(new RichFloat("gravity", -1.f, "Gravity", "Gravity force value")); par.addParam(new RichInt("seconds", 10, "Simulation interval (sec)", "Physics simulation interval in seconds")); par.addParam(new RichDynamicFloat("timeline", 0, 0, 100, "Timeline %", "Controls the point in timeline of the simulation")); } bool DynamicMeshSubFilter::applyFilter(QAction* filter, MeshDocument &md, RichParameterSet& par, vcg::CallBackPos* cb){ - if(par.getInt("seconds") < 0 || par.getInt("fps") <= 0 || par.getInt("iterations") <= 0 || par.getInt("contacts") <= 0 || par.getFloat("bounciness") < 0 || par.getFloat("bounciness") > 1) + if(parametersAreNotCorrect(md, par)) return false; if(configurationHasChanged(md, par)) @@ -21,29 +22,18 @@ bool DynamicMeshSubFilter::applyFilter(QAction* filter, MeshDocument &md, RichPa return true; } +bool DynamicMeshSubFilter::parametersAreNotCorrect(MeshDocument& md, RichParameterSet& par){ + return par.getInt("seconds") < 0 || par.getInt("fps") <= 0 || par.getInt("iterations") <= 0 || par.getInt("contacts") <= 0 || par.getFloat("bounciness") < 0 || par.getFloat("bounciness") > 1; +} + bool DynamicMeshSubFilter::configurationHasChanged(MeshDocument& md, RichParameterSet& par){ bool changed = m_seconds != par.getInt("seconds"); changed |= m_fps != par.getInt("fps"); changed |= m_iterations != par.getInt("iterations"); changed |= m_contacts != par.getInt("contacts"); changed |= m_bounciness != par.getFloat("bounciness"); - - // Does not work because meshlab fails at restoring the original translation matrix in the preview checkbox logic - /* Dim: the transformation matrices should not change - for(int i = 0; i < md.size(); i++){ - for(int j = 0; j < 16; j++) std::cout << md.getMesh(i)->cm.Tr[j/4][j%4] <<" "; - std::cout<fileName == m2->fileName && m1->cm.Tr == m2->cm.Tr; -} - -void DynamicMeshSubFilter::saveMeshState(MeshDocument& md){ - m_state.clear(); - for(int i = 0; i < md.size(); i++){ - MeshModel* meshCopy = new MeshModel(); - vcg::tri::Append::Mesh(meshCopy->cm, md.getMesh(i)->cm, false, true); - meshCopy->fileName = md.getMesh(i)->fileName; - meshCopy->cm.Tr = md.getMesh(i)->cm.Tr; - m_state.push_back(meshCopy); - } -} - -void DynamicMeshSubFilter::initialize(MeshDocument&, RichParameterSet&, vcg::CallBackPos* cb){ +void DynamicMeshSubFilter::initialize(MeshDocument& md, RichParameterSet& par, vcg::CallBackPos* cb){ + MeshSubFilter::initialize(md, par, cb); m_steps = m_seconds * m_fps; } diff --git a/src/fgt/filter_physics/DynamicMeshSubFilter.h b/src/fgt/filter_physics/DynamicMeshSubFilter.h index a5c08d25e..ad9713b29 100644 --- a/src/fgt/filter_physics/DynamicMeshSubFilter.h +++ b/src/fgt/filter_physics/DynamicMeshSubFilter.h @@ -13,6 +13,7 @@ public: DynamicMeshSubFilter(); virtual void initParameterSet(QAction* action,MeshDocument& md, RichParameterSet & par); virtual bool applyFilter(QAction* filter, MeshDocument &md, RichParameterSet& par, vcg::CallBackPos* cb); + virtual bool parametersAreNotCorrect(MeshDocument& md, RichParameterSet& par); protected: typedef std::vector LayerTransformations; @@ -20,7 +21,6 @@ protected: virtual void initialize(MeshDocument&, RichParameterSet&, vcg::CallBackPos* cb); virtual bool configurationHasChanged(MeshDocument& md, RichParameterSet& par); - void saveMeshState(MeshDocument& md); int m_fps; int m_iterations; @@ -28,13 +28,12 @@ protected: int m_steps; int m_seconds; float m_bounciness; + float m_gravity; + float m_friction; LayersTransformations m_layersTrans; std::vector m_files; std::vector m_state; - -private: - bool compareMesh(MeshModel* m1, MeshModel* m2); }; #endif // DYNAMICMESHSUBFILTER_H diff --git a/src/fgt/filter_physics/GravitySubFilter.cpp b/src/fgt/filter_physics/GravitySubFilter.cpp index e11289699..01341b0d7 100644 --- a/src/fgt/filter_physics/GravitySubFilter.cpp +++ b/src/fgt/filter_physics/GravitySubFilter.cpp @@ -13,9 +13,6 @@ void GravitySubFilter::initParameterSet(QAction* action,MeshDocument& md, RichPa } bool GravitySubFilter::applyFilter(QAction* filter, MeshDocument &md, RichParameterSet& par, vcg::CallBackPos* cb){ - if(md.size() < 2 || par.getMesh("scenery") == 0) - return false; - if(!DynamicMeshSubFilter::applyFilter(filter, md, par, cb)) return false; @@ -27,6 +24,10 @@ bool GravitySubFilter::applyFilter(QAction* filter, MeshDocument &md, RichParame return true; } +bool GravitySubFilter::parametersAreNotCorrect(MeshDocument& md, RichParameterSet& par){ + return DynamicMeshSubFilter::parametersAreNotCorrect(md, par) || md.size() < 2 || par.getMesh("scenery") == 0; +} + bool GravitySubFilter::configurationHasChanged(MeshDocument& md, RichParameterSet& par){ bool changed = DynamicMeshSubFilter::configurationHasChanged(md, par) || m_scenery != par.getMesh("scenery") || m_currentFilterType != m_filterType; m_scenery = par.getMesh("scenery"); @@ -39,12 +40,13 @@ void GravitySubFilter::initialize(MeshDocument& md, RichParameterSet& par, vcg:: if(cb != 0) (*cb)(0, "Physics pre-renderization of the scene started..."); - static float gravity[3] = {0.0f, -9.8f, 0.0f}; + float gravity[3] = {0.0f, m_gravity, 0.0f}; m_engine.clear(md); m_engine.setGlobalForce(gravity); m_engine.setIterations(m_iterations); m_engine.setMaxContacts(m_contacts); m_engine.setBounciness(m_bounciness); + m_engine.setFriction(m_friction); for(int i = 0; i < md.size(); i++) m_engine.registerTriMesh(*md.getMesh(i), m_scenery == md.getMesh(i) ? true : false); @@ -62,6 +64,7 @@ void GravitySubFilter::initialize(MeshDocument& md, RichParameterSet& par, vcg:: for(int j = 0; j < md.size(); j++){ m_layersTrans[j].push_back(m_engine.getTransformationMatrix(*md.getMesh(j))); } + m_engine.integrate(1.0f / m_fps); } diff --git a/src/fgt/filter_physics/GravitySubFilter.h b/src/fgt/filter_physics/GravitySubFilter.h index c00349666..2fb7bbdfd 100644 --- a/src/fgt/filter_physics/GravitySubFilter.h +++ b/src/fgt/filter_physics/GravitySubFilter.h @@ -14,6 +14,7 @@ public: virtual void initParameterSet(QAction* action,MeshDocument& md, RichParameterSet & par); virtual bool applyFilter(QAction* filter, MeshDocument &md, RichParameterSet& par, vcg::CallBackPos* cb); + virtual bool parametersAreNotCorrect(MeshDocument& md, RichParameterSet& par); protected: virtual void initialize(MeshDocument&, RichParameterSet&, vcg::CallBackPos* cb); diff --git a/src/fgt/filter_physics/MeshSubFilter.cpp b/src/fgt/filter_physics/MeshSubFilter.cpp index dfcd5d8ba..ad405b3b2 100644 --- a/src/fgt/filter_physics/MeshSubFilter.cpp +++ b/src/fgt/filter_physics/MeshSubFilter.cpp @@ -10,4 +10,14 @@ void MeshSubFilter::initParameterSet(QAction* action,MeshDocument& md, RichParam par.addParam(new RichInt("iterations", 20, "Physics method iterations", "Number of iterations of the physics iterative method for equation solving")); par.addParam(new RichInt("contacts", 20, "Max contacts", "Maximum number of contact points to generate per object pair")); par.addParam(new RichFloat("bounciness", 0.1f, "Bounciness", "The amount of bounciness of a collision: 0 means the surfaces are not bouncy at all, 1 is the maximum bounciness")); + par.addParam(new RichFloat("friction", 10, "Friction", "The coulomb friction coefficient of a collision")); +} + +void MeshSubFilter::initialize(MeshDocument& md, RichParameterSet&, vcg::CallBackPos* cb){ + /*if(md.mm()->cm.Tr != vcg::Matrix44f::Identity()) + throw MatrixNotFreezedException();*/ +} + +void MeshSubFilter::clearLastAppliedFilter(){ + m_currentFilterType = 0; } diff --git a/src/fgt/filter_physics/MeshSubFilter.h b/src/fgt/filter_physics/MeshSubFilter.h index c6374e68e..b2898fade 100644 --- a/src/fgt/filter_physics/MeshSubFilter.h +++ b/src/fgt/filter_physics/MeshSubFilter.h @@ -8,12 +8,24 @@ #include #include +#include + +class MatrixNotFreezedException : public std::exception{ +public: + const char* what() const throw(){ + return "The matrix of the current selected mesh has to be frozen"; + } +}; + class MeshSubFilter{ public: MeshSubFilter(); virtual void initParameterSet(QAction* action,MeshDocument& md, RichParameterSet & par); virtual bool applyFilter(QAction* filter, MeshDocument &md, RichParameterSet& par, vcg::CallBackPos* cb) = 0; + virtual void initialize(MeshDocument&, RichParameterSet&, vcg::CallBackPos* cb); + + static void clearLastAppliedFilter(); protected: static int m_currentFilterType; diff --git a/src/fgt/filter_physics/ODEFacade.cpp b/src/fgt/filter_physics/ODEFacade.cpp index 366d7a332..5d277919f 100644 --- a/src/fgt/filter_physics/ODEFacade.cpp +++ b/src/fgt/filter_physics/ODEFacade.cpp @@ -10,6 +10,7 @@ using namespace vcg; bool ODEFacade::m_initialized; float ODEFacade::m_bounciness = 0.1f; +float ODEFacade::m_friction = 10.f; dSpaceID ODEFacade::m_space; std::vector ODEFacade::m_contacts(20); @@ -31,14 +32,9 @@ void ODEFacade::initialize(){ } void ODEFacade::clear(MeshDocument& md){ - for(MeshContainer::iterator i = m_registeredMeshes.begin(); i != m_registeredMeshes.end(); i++){ - /*if(tri::HasPerMeshAttribute(i->first->cm, "physicsID")) - tri::Allocator::DeletePerMeshAttribute(i->first->cm, "physicsID");*/ - + for(MeshIterator i = m_registeredMeshes.begin(); i != m_registeredMeshes.end(); i++){ dGeomDestroy(i->second->geom); - if(i->second->body) - dBodyDestroy(i->second->body); - + if(i->second->body) dBodyDestroy(i->second->body); delete i->second; } m_registeredMeshes.clear(); @@ -49,8 +45,12 @@ void ODEFacade::setGlobalForce(float force[3]){ } void ODEFacade::registerTriMesh(MeshModel& mesh, bool scenery){ - if(tri::HasPerMeshAttribute(mesh.cm, "physicsID")) - tri::Allocator::DeletePerMeshAttribute(mesh.cm, "physicsID"); + MeshIterator entry = m_registeredMeshes.find(&mesh); + if(entry != m_registeredMeshes.end()) + return; + + if(!scenery && hasBorders(mesh)) + throw ODEInvalidMeshException(); if(mesh.cm.Tr != vcg::Matrix44f::Identity()){ vcg::tri::UpdatePosition::Matrix(mesh.cm, mesh.cm.Tr); @@ -86,52 +86,51 @@ void ODEFacade::registerTriMesh(MeshModel& mesh, bool scenery){ odeMesh->data = dGeomTriMeshDataCreate(); dGeomTriMeshDataBuildSingle1(odeMesh->data, odeMesh->vertices, 3*sizeof(dReal), mesh.cm.vert.size(), odeMesh->indices, 3*mesh.cm.face.size(), 3*sizeof(dTriIndex), odeMesh->normals); - odeMesh->geom = dCreateTriMesh(m_space, odeMesh->data, 0, 0, 0); - - MeshIndex index = tri::Allocator::AddPerMeshAttribute(mesh.cm, "physicsID"); - index() = m_registeredMeshes.size(); - m_registeredMeshes.push_back(make_pair(&mesh, odeMesh)); - + m_registeredMeshes.insert(make_pair(&mesh, odeMesh)); setAsRigidBody(mesh, !scenery); } +bool ODEFacade::hasBorders(MeshModel& mesh){ + for(CMeshO::FaceIterator i = mesh.cm.face.begin(); i != mesh.cm.face.end(); i++) + for(int j = 0; j < 3; j++) + if(vcg::face::IsBorder(*i, j)) return true; + + return false; +} + + void ODEFacade::setAsRigidBody(MeshModel& mesh, bool isRigidBody){ - if(!tri::HasPerMeshAttribute(mesh.cm, "physicsID")) + MeshIterator entry = m_registeredMeshes.find(&mesh); + if(entry == m_registeredMeshes.end()) return; - MeshIndex index = tri::Allocator::GetPerMeshAttribute(mesh.cm, "physicsID"); + if(!isRigidBody && entry->second->body != 0){ // This mesh was registered as a rigid body + dBodyDestroy(entry->second->body); + entry->second->body = 0; + }else if(isRigidBody && entry->second->body == 0){ // This mesh was registered as part of the scenery + entry->second->body = dBodyCreate(m_world); + dGeomSetBody(entry->second->geom, entry->second->body); - if(!isRigidBody && m_registeredMeshes[index()].second->body != 0){ // This mesh was registered as a rigid body - dBodyDestroy(m_registeredMeshes[index()].second->body); - m_registeredMeshes[index()].second->body = 0; - }else if(isRigidBody && m_registeredMeshes[index()].second->body == 0){ // This mesh was registered as part of the scenery - m_registeredMeshes[index()].second->body = dBodyCreate(m_world); - dGeomSetBody(m_registeredMeshes[index()].second->geom, m_registeredMeshes[index()].second->body); - - dMass* mass = &m_registeredMeshes[index()].second->mass; - dMassSetTrimesh(mass, 1.0f, m_registeredMeshes[index()].second->geom); + dMass* mass = &entry->second->mass; + dMassSetTrimesh(mass, 1.0f, entry->second->geom); + if(mass->mass < 0) throw ODEInvalidMeshException(); mass->mass = 5; dReal centerOfMass[3] = { mass->c[0], mass->c[1], mass->c[2] }; - dMassTranslate(&m_registeredMeshes[index()].second->mass, -centerOfMass[0], -centerOfMass[1], -centerOfMass[2]); - dBodySetMass(m_registeredMeshes[index()].second->body, &m_registeredMeshes[index()].second->mass); - dBodySetPosition(m_registeredMeshes[index()].second->body, m_registeredMeshes[index()].second->centerOfMass[0], m_registeredMeshes[index()].second->centerOfMass[1], m_registeredMeshes[index()].second->centerOfMass[2]); + dMassTranslate(&entry->second->mass, -centerOfMass[0], -centerOfMass[1], -centerOfMass[2]); + dBodySetMass(entry->second->body, &entry->second->mass); + dBodySetPosition(entry->second->body, entry->second->centerOfMass[0], entry->second->centerOfMass[1], entry->second->centerOfMass[2]); } } void ODEFacade::updateTransform(){ for(MeshContainer::iterator i = m_registeredMeshes.begin(); i != m_registeredMeshes.end(); i++){ - const dReal* position = 0; - const dReal* rotation = 0; + if(i->second->body == 0) + return; - if(i->second->body != 0){ - position = dBodyGetPosition(i->second->body); - rotation = dBodyGetRotation(i->second->body); - }else{ - position = dGeomGetPosition(i->second->geom); - rotation = dGeomGetRotation(i->second->geom); - } + const dReal* position = dBodyGetPosition(i->second->body); + const dReal* rotation = dBodyGetRotation(i->second->body); i->first->cm.Tr[0][0] = rotation[0]; i->first->cm.Tr[0][1] = rotation[1]; @@ -173,7 +172,7 @@ void ODEFacade::collisionCallback(dGeomID o1, dGeomID o2){ for(int i = 0; i < m_contacts.size(); i++){ m_contacts[i].surface.mode = dContactBounce; - m_contacts[i].surface.mu = 0.5; + m_contacts[i].surface.mu = m_friction; m_contacts[i].surface.bounce = m_bounciness; m_contacts[i].surface.bounce_vel = 0.1; } @@ -186,22 +185,13 @@ void ODEFacade::collisionCallback(dGeomID o1, dGeomID o2){ } vcg::Matrix44f ODEFacade::getTransformationMatrix(MeshModel& mesh){ - if(!tri::HasPerMeshAttribute(mesh.cm, "physicsID")) + MeshIterator entry = m_registeredMeshes.find(&mesh); + if(entry == m_registeredMeshes.end() || entry->second->body == 0) return mesh.cm.Tr; - MeshIndex index = tri::Allocator::GetPerMeshAttribute(mesh.cm, "physicsID"); - vcg::Matrix44f matrix; - const dReal* position = 0; - const dReal* rotation = 0; - - if(m_registeredMeshes[index()].second->body != 0){ - position = dBodyGetPosition(m_registeredMeshes[index()].second->body); - rotation = dBodyGetRotation(m_registeredMeshes[index()].second->body); - }else{ - position = dGeomGetPosition(m_registeredMeshes[index()].second->geom); - rotation = dGeomGetRotation(m_registeredMeshes[index()].second->geom); - } + const dReal* position = dBodyGetPosition(entry->second->body); + const dReal* rotation = dBodyGetRotation(entry->second->body); matrix[0][0] = rotation[0]; matrix[0][1] = rotation[1]; @@ -221,7 +211,7 @@ vcg::Matrix44f ODEFacade::getTransformationMatrix(MeshModel& mesh){ matrix[3][3] = 1.f; vcg::Matrix44f m; - m.SetTranslate(-m_registeredMeshes[index()].second->centerOfMass[0], -m_registeredMeshes[index()].second->centerOfMass[1], -m_registeredMeshes[index()].second->centerOfMass[2]); + m.SetTranslate(-entry->second->centerOfMass[0], -entry->second->centerOfMass[1], -entry->second->centerOfMass[2]); return matrix * m; } @@ -237,3 +227,7 @@ void ODEFacade::setMaxContacts(int contacts){ void ODEFacade::setBounciness(float bounciness){ m_bounciness = bounciness; } + +void ODEFacade::setFriction(float friction){ + m_friction = friction; +} diff --git a/src/fgt/filter_physics/ODEFacade.h b/src/fgt/filter_physics/ODEFacade.h index f2a6505d5..34084efbb 100644 --- a/src/fgt/filter_physics/ODEFacade.h +++ b/src/fgt/filter_physics/ODEFacade.h @@ -5,15 +5,22 @@ #ifndef ODE_FACADE_H #define ODE_FACADE_H -#include "PhysicsEngineFacade.h" - -#include +#include +#include #include -#include +#include +#include -class ODEFacade : public PhysicsEngineFacade{ +class ODEInvalidMeshException : public std::exception{ +public: + const char* what() const throw(){ + return "Incorrect mesh: dynamic meshes can't have borders or a negative mass (check normals)!"; + } +}; + +class ODEFacade{ public: ODEFacade(); @@ -27,6 +34,7 @@ public: virtual void setIterations(int iterations); virtual void setMaxContacts(int contacts); virtual void setBounciness(float bounciness); + virtual void setFriction(float friction); virtual void integrate(float step); virtual void clear(MeshDocument& md); @@ -56,15 +64,17 @@ protected: dTriIndex (*indices)[3]; }; - typedef std::vector > MeshContainer; - typedef CMeshO::PerMeshAttributeHandle MeshIndex; + typedef std::map MeshContainer; + typedef MeshContainer::iterator MeshIterator; static void collisionCallback(void* data, dGeomID o1, dGeomID o2); void collisionCallback(dGeomID o1, dGeomID o2); + bool hasBorders(MeshModel& mesh); //This class is a monostate static bool m_initialized; static float m_bounciness; + static float m_friction; static dWorldID m_world; static dSpaceID m_space; @@ -72,6 +82,6 @@ protected: static dJointGroupID m_contactGroup; static MeshContainer m_registeredMeshes; -}; + }; #endif diff --git a/src/fgt/filter_physics/RandomDropFilter.cpp b/src/fgt/filter_physics/RandomDropFilter.cpp index 88f2e1e97..290c6207d 100644 --- a/src/fgt/filter_physics/RandomDropFilter.cpp +++ b/src/fgt/filter_physics/RandomDropFilter.cpp @@ -10,7 +10,7 @@ using namespace std; int RandomDropFilter::m_filterType = -1; -RandomDropFilter::RandomDropFilter() : m_randomMesh(0), m_dropRate(-1), m_distance(-1), m_bboxUnit(false){ +RandomDropFilter::RandomDropFilter() : m_randomMesh(0), m_dropRate(-1), m_distance(-1){ MeshSubFilter::m_currentFilterType += 1; m_filterType = MeshSubFilter::m_currentFilterType; } @@ -18,20 +18,17 @@ RandomDropFilter::RandomDropFilter() : m_randomMesh(0), m_dropRate(-1), m_distan void RandomDropFilter::initParameterSet(QAction* action,MeshDocument& md, RichParameterSet & par){ DynamicMeshSubFilter::initParameterSet(action, md, par); par.addParam(new RichMesh("randomMesh", 0, &md, "Random mesh", "This mesh will be randomly spawned in the scene")); - par.addParam(new RichFloat("distance", 1.0f, "Random spawn radius", "The object will spawn in a random position contained in the specified radius")); - par.addParam(new RichBool("bboxUnit", true, "Use bounding box units", "If true the random spawn radius will be computed in bounding box units")); par.addParam(new RichFloat("dropRate", 0.5, "Drop rate (sec)", "The drop rate of the filler mesh in seconds")); + par.addParam(new RichAbsPerc("distance",md.bbox().Diag()*0.1,0,md.bbox().Diag(),"Random spawn radius", "The object will spawn in a random position contained in the specified radius")); + } bool RandomDropFilter::applyFilter(QAction* filter, MeshDocument &md, RichParameterSet& par, vcg::CallBackPos* cb){ - if(md.size() < 2 || par.getMesh("randomMesh") == 0 || par.getMesh("randomMesh")->fileName.find("randomDropMesh") == 0 || par.getFloat("dropRate") < 0) - return false; + DynamicMeshSubFilter::applyFilter(filter, md, par, cb); if(par.getFloat("dropRate") == 0) return true; - DynamicMeshSubFilter::applyFilter(filter, md, par, cb); - int currentStep = par.getDynamicFloat("timeline") / 100 * m_steps; int randomObjects = m_seconds*m_dropRate; int currentRandomObject = md.size() - randomObjects + currentStep/(m_fps/m_dropRate); @@ -48,20 +45,26 @@ bool RandomDropFilter::applyFilter(QAction* filter, MeshDocument &md, RichParame return true; } +bool RandomDropFilter::parametersAreNotCorrect(MeshDocument& md, RichParameterSet& par){ + return DynamicMeshSubFilter::parametersAreNotCorrect(md, par) || md.size() < 2 || par.getMesh("randomMesh") == 0 || par.getMesh("randomMesh")->fileName.find("randomDropMesh") == 0 || par.getFloat("dropRate") < 0; +} + void RandomDropFilter::initialize(MeshDocument& md, RichParameterSet& par, vcg::CallBackPos* cb){ DynamicMeshSubFilter::initialize(md, par, cb); if(cb != 0) (*cb)(0, "Physics pre-renderization of the scene started..."); - static float gravity[3] = {0.0f, -9.8f, 0.0f}; + float gravity[3] = {0.0f, m_gravity, 0.0f}; m_engine.clear(md); m_engine.setGlobalForce(gravity); m_engine.setIterations(m_iterations); m_engine.setMaxContacts(m_contacts); m_engine.setBounciness(m_bounciness); + m_engine.setFriction(m_friction); srand((unsigned)time(0)); int randomObjects = m_seconds*m_dropRate; + int spawnInterval = m_fps / m_dropRate; m_steps = m_seconds * m_fps; foreach(MeshModel *mesh, md.meshList){ @@ -69,14 +72,15 @@ void RandomDropFilter::initialize(MeshDocument& md, RichParameterSet& par, vcg:: md.delMesh(mesh); } - for(int i = 0; i < randomObjects; i++) - addRandomObject(md, i); - - for(int i = 0; i < md.size() - randomObjects; i++){ + int meshes = md.size(); + for(int i = 0; i < md.size(); i++){ if(m_randomMesh != md.getMesh(i)) m_engine.registerTriMesh(*md.getMesh(i), true); } + for(int i = 0; i < randomObjects; i++) + addRandomObject(md, i); + m_layersTrans.clear(); m_layersTrans.resize(md.size()); @@ -87,10 +91,8 @@ void RandomDropFilter::initialize(MeshDocument& md, RichParameterSet& par, vcg:: for(int i = 0; i <= m_steps; i++){ if(cb != 0) (*cb)(98.f*i/m_steps, "Computing..."); - int currentRndObject = md.size() - randomObjects + i/(m_fps/m_dropRate); - - if(i != 0 && i % int(m_fps/m_dropRate) == 0) - m_engine.registerTriMesh(*md.getMesh(currentRndObject - 1), false); + if(i != 0 && i % spawnInterval == 0) + m_engine.registerTriMesh(*md.getMesh(meshes++), false); for(int j = 0; j < md.size(); j++){ m_layersTrans[j].push_back(m_engine.getTransformationMatrix(*md.getMesh(j))); @@ -107,18 +109,15 @@ void RandomDropFilter::initialize(MeshDocument& md, RichParameterSet& par, vcg:: } void RandomDropFilter::addRandomObject(MeshDocument& md, int meshID){ - float multiplier = m_bboxUnit ? m_randomMesh->cm.bbox.Diag() : 1.0f; - float distance = m_distance*multiplier; - ostringstream meshName; meshName << "randomDropMesh" << meshID; MeshModel* meshCopy = md.addNewMesh(meshName.str().c_str()); vcg::tri::Append::Mesh(meshCopy->cm, m_randomMesh->cm, false, true); meshCopy->cm.Tr = m_randomMesh->cm.Tr; - float x = meshCopy->cm.Tr.GetColumn3(3).X() + distance/2.0f - static_cast(rand())/RAND_MAX*distance; - float y = meshCopy->cm.Tr.GetColumn3(3).Y() + distance/2.0f - static_cast(rand())/RAND_MAX*distance; - float z = meshCopy->cm.Tr.GetColumn3(3).Z() + distance/2.0f - static_cast(rand())/RAND_MAX*distance; + float x = meshCopy->cm.Tr.GetColumn3(3).X() + m_distance/2.0f - static_cast(rand())/RAND_MAX*m_distance; + float y = meshCopy->cm.Tr.GetColumn3(3).Y() + m_distance/2.0f - static_cast(rand())/RAND_MAX*m_distance; + float z = meshCopy->cm.Tr.GetColumn3(3).Z() + m_distance/2.0f - static_cast(rand())/RAND_MAX*m_distance; meshCopy->cm.Tr.SetColumn(3, vcg::Point3f(x, y, z)); meshCopy->visible = false; @@ -127,15 +126,13 @@ void RandomDropFilter::addRandomObject(MeshDocument& md, int meshID){ bool RandomDropFilter::configurationHasChanged(MeshDocument& md, RichParameterSet& par){ bool changed = DynamicMeshSubFilter::configurationHasChanged(md, par) || m_randomMesh != par.getMesh("randomMesh") || - m_distance != par.getFloat("distance") || + m_distance != par.getAbsPerc("distance") || m_dropRate != par.getFloat("dropRate") || - m_bboxUnit != par.getBool("bboxUnit") || m_currentFilterType != m_filterType; m_randomMesh = par.getMesh("randomMesh"); - m_distance = par.getFloat("distance"); + m_distance = par.getAbsPerc("distance"); m_dropRate = par.getFloat("dropRate"); - m_bboxUnit = par.getBool("bboxUnit"); m_currentFilterType = m_filterType; return changed; diff --git a/src/fgt/filter_physics/RandomDropFilter.h b/src/fgt/filter_physics/RandomDropFilter.h index ff4d41574..0d4946dfd 100644 --- a/src/fgt/filter_physics/RandomDropFilter.h +++ b/src/fgt/filter_physics/RandomDropFilter.h @@ -14,6 +14,7 @@ public: virtual void initParameterSet(QAction* action,MeshDocument& md, RichParameterSet & par); virtual bool applyFilter(QAction* filter, MeshDocument &md, RichParameterSet& par, vcg::CallBackPos* cb); + virtual bool parametersAreNotCorrect(MeshDocument& md, RichParameterSet& par); protected: virtual void initialize(MeshDocument&, RichParameterSet&, vcg::CallBackPos* cb); @@ -24,7 +25,6 @@ private: MeshModel* m_randomMesh; float m_dropRate; float m_distance; - bool m_bboxUnit; ODEFacade m_engine; static int m_filterType; diff --git a/src/fgt/filter_physics/RandomFillFilter.cpp b/src/fgt/filter_physics/RandomFillFilter.cpp index 3c7e47f0c..f1332fa94 100644 --- a/src/fgt/filter_physics/RandomFillFilter.cpp +++ b/src/fgt/filter_physics/RandomFillFilter.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -23,25 +24,35 @@ void RandomFillFilter::initParameterSet(QAction* action,MeshDocument& md, RichPa par.addParam(new RichMesh("container", 0, &md, "Container mesh", "This mesh will act as a container for the filling mesh")); par.addParam(new RichMesh("filler", 0, &md, "Filler mesh", "The container mesh will be filled with this mesh")); par.addParam(new RichFloat("factor", 0.5, "Volume ratio factor", "The ratio between the container and the filler object will be multiplied by this factor. The volume ratio determines the number of filling objects to be spawn.")); - par.addParam(new RichBool("useRandomVertices", true, "Random spawn points", "If true the filling objects will spawn at random positions in the container mesh instead of being spawn at the center of mass")); + par.addParam(new RichFloat("seconds", 1, "Simulation interval (sec)", "Physics simulation interval in seconds")); + par.addParam(new RichBool("flipNormal", false, "Flip container normals", "If true the container normals will be flipped.")); } bool RandomFillFilter::applyFilter(QAction* filter, MeshDocument &md, RichParameterSet& par, vcg::CallBackPos* cb){ - if(md.size() < 2 || par.getMesh("container") == 0 || par.getMesh("container") == par.getMesh("filler") || par.getInt("fps") <= 0 || par.getInt("iterations") <= 0 || par.getInt("contacts") <= 0 || par.getFloat("bounciness") < 0.f || par.getFloat("bounciness") > 1.f || par.getFloat("factor") < 0.f || par.getFloat("factor") > 1.f) + if(parametersAreNotCorrect(md, par)) return false; + MeshSubFilter::initialize(md, par, cb); if(cb != 0) (*cb)(0, "Physics renderization of the scene started..."); MeshModel* container = par.getMesh("container"); MeshModel* filler = par.getMesh("filler"); int fillOffset = md.size(); + float gravity[3] = {0.0f, 0.0f, 0.0f}; + + if(par.getBool("flipNormal")){ + vcg::tri::Clean::FlipMesh(container->cm); + tri::UpdateNormals::PerVertexNormalizedPerFace(container->cm); + container->clearDataMask(MeshModel::MM_FACEFACETOPO | MeshModel::MM_FACEFLAGBORDER); + par.setValue("flipNormal", BoolValue(false)); // Why does it not work?? + } - static float gravity[3] = {0.0f, -9.8f, 0.0f}; m_engine.clear(md); m_engine.setGlobalForce(gravity); m_engine.setIterations(par.getInt("iterations")); m_engine.setMaxContacts(par.getInt("contacts")); m_engine.setBounciness(par.getFloat("bounciness")); + m_engine.setFriction(par.getFloat("friction")); m_engine.registerTriMesh(*container, true); srand((unsigned)time(0)); @@ -56,26 +67,26 @@ bool RandomFillFilter::applyFilter(QAction* filter, MeshDocument &md, RichParame int objects = abs(inertiaContainer.Mass()/inertiaFiller.Mass())*par.getFloat("factor"); filler->cm.Tr.SetColumn(3, - inertiaFiller.CenterOfMass()); - if(par.getBool("useRandomVertices")){ - for(int i = 0; i < objects; i++){ - if(cb != 0) (*cb)(50.f*i/objects, "Computing..."); - addRandomObject(md, filler, getRandomOrigin(par), i); - m_engine.registerTriMesh(*md.getMesh(fillOffset++)); - } - - for(int j = 0; j < par.getInt("fps"); j++){ - if(cb != 0) (*cb)(50 + 48.f*j/par.getInt("fps"), "Computing..."); + //Restore old generated meshes + int restoredMeshes = 0; + for(int i = 0; i < md.size(); i++){ + if(md.getMesh(i)->fileName.find("randomFillMesh") == 0){ + m_engine.registerTriMesh(*md.getMesh(i)); + restoredMeshes++; m_engine.integrate(1.0f/par.getInt("fps")); } - }else{ - for(int i = 0; i < objects; i++){ - if(cb != 0) (*cb)(98.f*i/objects, "Computing..."); - addRandomObject(md, filler, inertiaContainer.CenterOfMass(), i); - m_engine.registerTriMesh(*md.getMesh(fillOffset++)); + } - for(int j = 0; j < par.getInt("fps")/2; j++) - m_engine.integrate(1.0f/par.getInt("fps")); - } + for(int i = 0; i < objects; i++){ + if(cb != 0) (*cb)(50.f*i/objects, "Computing..."); + addRandomObject(md, filler, getRandomOrigin(par), restoredMeshes + i); + m_engine.registerTriMesh(*md.getMesh(fillOffset++)); + m_engine.integrate(1.0f/par.getInt("fps")); + } + + for(int j = 0; j < par.getFloat("seconds") * par.getInt("fps"); j++){ + if(cb != 0) (*cb)(50 + 48.f*j/par.getInt("fps"), "Computing..."); + m_engine.integrate(1.0f/par.getInt("fps")); } m_engine.updateTransform(); @@ -85,8 +96,12 @@ bool RandomFillFilter::applyFilter(QAction* filter, MeshDocument &md, RichParame return true; } +bool RandomFillFilter::parametersAreNotCorrect(MeshDocument& md, RichParameterSet& par){ + return md.size() < 2 || par.getMesh("container") == 0 || par.getMesh("container") == par.getMesh("filler") || par.getInt("fps") <= 0 || par.getInt("iterations") <= 0 || par.getInt("contacts") <= 0 || par.getFloat("bounciness") < 0.f || par.getFloat("bounciness") > 1.f || par.getFloat("factor") < 0.f || par.getFloat("factor") > 1.f; +} + vcg::Point3 RandomFillFilter::getRandomOrigin(RichParameterSet& par){ - int randomVertex = static_cast(rand())/RAND_MAX*(par.getMesh("container")->cm.vert.size() - 1); + int randomVertex = float(rand())/RAND_MAX*(par.getMesh("container")->cm.vert.size() - 1); CVertexO& vertex = par.getMesh("container")->cm.vert[randomVertex]; return vertex.P() + (vertex.N() * par.getMesh("filler")->cm.bbox.Diag()); } diff --git a/src/fgt/filter_physics/RandomFillFilter.h b/src/fgt/filter_physics/RandomFillFilter.h index 39a25b0a1..82b41930d 100644 --- a/src/fgt/filter_physics/RandomFillFilter.h +++ b/src/fgt/filter_physics/RandomFillFilter.h @@ -12,6 +12,7 @@ public: virtual void initParameterSet(QAction* action,MeshDocument& md, RichParameterSet & par); virtual bool applyFilter(QAction* filter, MeshDocument &md, RichParameterSet& par, vcg::CallBackPos* cb); + virtual bool parametersAreNotCorrect(MeshDocument& md, RichParameterSet& par); protected: virtual void addRandomObject(MeshDocument& md, MeshModel* filler, const vcg::Point3& origin, int meshID); diff --git a/src/fgt/filter_physics/filter_physics.cpp b/src/fgt/filter_physics/filter_physics.cpp index c823df4aa..84b45d005 100644 --- a/src/fgt/filter_physics/filter_physics.cpp +++ b/src/fgt/filter_physics/filter_physics.cpp @@ -4,6 +4,8 @@ #include #include +#include + using namespace std; using namespace vcg; @@ -63,25 +65,33 @@ void FilterPhysics::initParameterSet(QAction* action,MeshDocument& md, RichParam } bool FilterPhysics::applyFilter(QAction* action, MeshDocument &md, RichParameterSet& par, vcg::CallBackPos* cb){ - bool ret = true; + try{ + bool ret = true; - switch(ID(action)){ - case FP_PHYSICS_GRAVITY: - ret = m_gravityFilter.applyFilter(action, md, par, cb); - break; - case FP_PHYSICS_RNDDROP: - ret = m_rndDropFilter.applyFilter(action, md, par, cb); - break; - case FP_PHYSICS_RNDFILL: - ret = m_rndFillFilter.applyFilter(action, md, par, cb); - break; - default: - break; + switch(ID(action)){ + case FP_PHYSICS_GRAVITY: + ret = m_gravityFilter.applyFilter(action, md, par, cb); + break; + case FP_PHYSICS_RNDDROP: + ret = m_rndDropFilter.applyFilter(action, md, par, cb); + break; + case FP_PHYSICS_RNDFILL: + ret = m_rndFillFilter.applyFilter(action, md, par, cb); + break; + default: + break; + } + + if(!ret){ + QMessageBox::critical(0, QString("Error"), QString("Invalid filter configuration: check your parameters")); + return false; + } + }catch(std::exception& ex){ + MeshSubFilter::clearLastAppliedFilter(); + QMessageBox::critical(0, QString("Error"), ex.what()); + return false; } - if(!ret) - QMessageBox::critical(0, QString("Error"), QString("Invalid filter configuration parameters")); - return true; } diff --git a/src/fgt/filter_physics/filter_physics.h b/src/fgt/filter_physics/filter_physics.h index c677da399..829c31196 100644 --- a/src/fgt/filter_physics/filter_physics.h +++ b/src/fgt/filter_physics/filter_physics.h @@ -31,8 +31,8 @@ class FilterPhysics : public QObject, public MeshFilterInterface virtual QString filterName(FilterIDType filter) const; virtual QString filterInfo(FilterIDType filter) const; - virtual int getRequirements(QAction*){return MeshModel::MM_FACEVERT | MeshModel::MM_FACENORMAL | MeshModel::MM_VERTNORMAL; } - virtual int postCondition( QAction* ) const{return MeshModel::MM_FACENORMAL; /*MeshModel::MM_TRANSFMATRIX; */} + virtual int getRequirements(QAction*){return MeshModel::MM_FACEVERT | MeshModel::MM_FACENORMAL | MeshModel::MM_VERTNORMAL | MeshModel::MM_FACEFACETOPO; } + virtual int postCondition( QAction* ) const{return MeshModel::MM_FACENORMAL | MeshModel::MM_TRANSFMATRIX;} virtual bool autoDialog(QAction*) {return true;} virtual void initParameterSet(QAction*, MeshDocument&, RichParameterSet&); diff --git a/src/fgt/filter_physics/filter_physics.pro b/src/fgt/filter_physics/filter_physics.pro index f85146cae..798a33dd3 100644 --- a/src/fgt/filter_physics/filter_physics.pro +++ b/src/fgt/filter_physics/filter_physics.pro @@ -2,7 +2,6 @@ include (../../shared.pri) DEFINES += dSINGLE HEADERS = filter_physics.h \ ODEFacade.h \ - PhysicsEngineFacade.h \ MeshSubFilter.h \ GravitySubFilter.h \ RandomDropFilter.h \