bool CollisionTools::raycast(const Ogre::Ray &ray, Ogre::Vector3 &result,Ogre::MovableObject* &target,float &closest_distance, const Ogre::uint32 queryMask) { target = NULL; // check we are initialised if (mRaySceneQuery != NULL) { // create a query object mRaySceneQuery->setRay(ray); mRaySceneQuery->setSortByDistance(true); mRaySceneQuery->setQueryMask(queryMask); // execute the query, returns a vector of hits if (mRaySceneQuery->execute().size() <= 0) { // raycast did not hit an objects bounding box return (false); } } else { //LOG_ERROR << "Cannot raycast without RaySceneQuery instance" << ENDLOG; 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::Ogre::Real closest_distance = -1.0f; 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 or // ManualObject. if ((query_result[qr_idx].movable != NULL) && (query_result[qr_idx].movable->isVisible()) && ((query_result[qr_idx].movable->getMovableType().compare("Entity") == 0) || (query_result[qr_idx].movable->getMovableType().compare("ManualObject") == 0))) { // get the entity to check Ogre::MovableObject *pmovable = static_cast<Ogre::MovableObject*>(query_result[qr_idx].movable); Ogre::MeshPtr mesh_ptr; if (query_result[qr_idx].movable->getMovableType().compare("Entity") == 0) { mesh_ptr = ((Ogre::Entity*)pmovable)->getMesh(); } else { // XXX: Does "Mesh" get replaced so we can get away with not // XXX: deleting it for now? mesh_ptr = ((Ogre::ManualObject*)pmovable)->convertToMesh("Mesh", "General"); } // mesh data to retrieve size_t vertex_count; size_t index_count; Ogre::Vector3 *vertices; Ogre::uint32 *indices; // get the mesh information GetMeshInformation(mesh_ptr, vertex_count, vertices, index_count, indices, pmovable->getParentNode()->_getDerivedPosition(), pmovable->getParentNode()->_getDerivedOrientation(), pmovable->getParentNode()->_getDerivedScale()); // test for hitting individual triangles on the mesh bool new_closest_found = false; for (size_t i = 0; i < 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; } } } // 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) { target = pmovable; closest_result = ray.getPoint(closest_distance); } } } // return the result if (closest_distance >= 0.0f) { // raycast success result = closest_result; return (true); } else { // raycast failed return (false); } }
void System::makeObjectTransparent( const Ogre::Vector3 &start, const Ogre::Vector3 &end, float nNewTrans, float nDelay, IObjectFilter *pFilter ) { Ogre::Vector3 vDirction = (start-end); Ogre::Real fDistance = vDirction.length(); Ogre::Ray ray = Ogre::Ray(end,vDirction); Ogre::RaySceneQuery* raySceneQuery = getSceneManager()->createRayQuery(Ogre::Ray()); std::set<Fairy::Object*> setCurrentTransparentObjects; raySceneQuery->setRay(ray); const Ogre::RaySceneQueryResult& queryResult = raySceneQuery->execute(); for (Ogre::RaySceneQueryResult::const_iterator it = queryResult.begin(); it != queryResult.end(); ++it) { Ogre::MovableObject* pMovable = it->movable; Ogre::Vector3 vObjectPos = pMovable->getParentNode()->getPosition(); Ogre::Vector3 vObjectDir = (vObjectPos-end); Fairy::ObjectPtr pObject = Fairy::getObjectFromMovable(pMovable); if(pObject) { // 当满足以下条件时设置透明: 1.是StaticEntity类型; 2.相交; 3.允许透明 if(pObject->getType() == "StaticEntity" && vObjectDir.dotProduct(vDirction) > 0 && static_cast<Fairy::StaticEntityObject*>(pObject.get())->getEnableTransparency()) { Fairy::StaticEntityObject* pModel = static_cast<Fairy::StaticEntityObject*>(pObject.get()); pModel->setTransparency(0.5f); // 删除原有的Object,放到临时的Set中 m_setTransparencyObjects.erase(pObject.get()); setCurrentTransparentObjects.insert(pObject.get()); } } } std::set<Fairy::Object*>::iterator iter = m_setTransparencyObjects.begin(); std::set<Fairy::Object*>::iterator tempIter; while (iter != m_setTransparencyObjects.end()) { tempIter = iter; ++iter; // 恢复没有遮挡的Object的透明度为0 Fairy::StaticEntityObject* pModel = static_cast<Fairy::StaticEntityObject*>(*tempIter); pModel->setTransparency(0); // 删除透明度为0的Object if (pModel->getTransparency()==0) { m_setTransparencyObjects.erase(tempIter); } } // 将遮挡的Object放到m_setTransparencyObjects for (std::set<Fairy::Object*>::iterator it = setCurrentTransparentObjects.begin(); it != setCurrentTransparentObjects.end(); ++it) { m_setTransparencyObjects.insert(*it); } if(raySceneQuery) { getSceneManager()->destroyQuery(raySceneQuery); raySceneQuery = NULL; } }
bool CollisionTools::raycast(const Ogre::Ray &ray, Ogre::Vector3 &result,Ogre::MovableObject* &target,float &closest_distance, const Ogre::uint32 queryMask) { target = NULL; // check we are initialised if (mRaySceneQuery != NULL) { // create a query object mRaySceneQuery->setRay(ray); mRaySceneQuery->setSortByDistance(true); mRaySceneQuery->setQueryMask(queryMask); // execute the query, returns a vector of hits if (mRaySceneQuery->execute().size() <= 0) { // raycast did not hit an objects bounding box return (false); } } else { //LOG_ERROR << "Cannot raycast without RaySceneQuery instance" << ENDLOG; 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::Ogre::Real closest_distance = -1.0f; closest_distance = -1.0f; Ogre::Vector3 closest_result; Ogre::RaySceneQueryResult &query_result = mRaySceneQuery->getLastResults(); #if 0 for (size_t qr_idx = 0; qr_idx < query_result.size(); qr_idx++) { const Ogre::RaySceneQueryResultEntry& result = query_result[qr_idx]; Ogre::Real distance = result.distance; Ogre::MovableObject* movable = static_cast<Ogre::MovableObject*>(result.movable); Ogre::SceneQuery::WorldFragment* worldFragment = result.worldFragment; if (movable) { const Ogre::String& type = movable->getMovableType(); const Ogre::String& name = movable->getName(); const Ogre::String& parentName = movable->getParentNode()->getName(); Ogre::uint32 flag = movable->getQueryFlags(); if (type.compare("Entity") == 0) { Ogre::Entity* ent = (Ogre::Entity*)movable; } std::ostrstream oss; oss<<"name:"<<name<<" distance:"<< distance <<" type:"<<type<<" flag:"<<flag<<" parent:"<<parentName<<std::endl; OutputDebugString(oss.str() ); } } #endif 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::MovableObject *pentity = static_cast<Ogre::MovableObject*>(query_result[qr_idx].movable); // mesh data to retrieve size_t vertex_count; size_t index_count; Ogre::Vector3 *vertices = NULL; Ogre::uint32 *indices = NULL; // get the mesh information GetMeshInformation(((Ogre::Entity*)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 (size_t i = 0; i < 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; } } } // 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) { target = pentity; closest_result = ray.getPoint(closest_distance); } } } // return the result if (closest_distance >= 0.0f) { // raycast success result = closest_result; return (true); } else { // raycast failed return (false); } }
void EntityWorldPickListener::processPickResult(bool& continuePicking, Ogre::RaySceneQueryResultEntry& entry, Ogre::Ray& cameraRay, const MousePickerArgs& mousePickerArgs) { if (!mContinuePickingThisContext) { return; } if (entry.worldFragment) { //this is terrain //a position of -1, -1, -1 is not valid terrain Ogre::SceneQuery::WorldFragment* wf = entry.worldFragment; static const Ogre::Vector3 invalidPos(-1, -1, -1); if (wf->singleIntersection != invalidPos) { if (mFurthestPickingDistance == 0 || mResult.empty()) { EntityPickResult result; result.entity = findTerrainEntity(); result.position = wf->singleIntersection; result.distance = entry.distance; result.isTransparent = false; mResult.push_back(result); mContinuePickingThisContext = false; } else { if (entry.distance < mResult[mResult.size() - 1].distance) { //If the last result is transparent, add another result, but if it's not replace it. if (mResult.size() && !mResult[mResult.size() - 1].isTransparent) { mResult.pop_back(); } EntityPickResult result; result.entity = findTerrainEntity(); result.position = wf->singleIntersection; result.distance = entry.distance; result.isTransparent = false; mResult.push_back(result); mContinuePickingThisContext = false; } } } /* std::stringstream ss; ss << wf->singleIntersection; S_LOG_VERBOSE("Picked in terrain: " << ss.str() << " distance: " << mResult.distance);*/ } else if (entry.movable) { Ogre::MovableObject* pickedMovable = entry.movable; if (pickedMovable->isVisible() && pickedMovable->getUserObjectBindings().getUserAny().getType() == typeid(EmberEntityUserObject::SharedPtr)) { EmberEntityUserObject* anUserObject = Ogre::any_cast<EmberEntityUserObject::SharedPtr>(pickedMovable->getUserObjectBindings().getUserAny()).get(); //refit the opcode mesh to adjust for changes in the mesh (for example animations) anUserObject->refit(); ICollisionDetector* collisionDetector = anUserObject->getCollisionDetector(); if (collisionDetector) { CollisionResult collisionResult; collisionResult.collided = false; collisionResult.isTransparent = false; collisionDetector->testCollision(cameraRay, collisionResult); if (collisionResult.collided) { if (mFurthestPickingDistance == 0) { //If the current collision is transparent, also check for entities which are further away. if (!collisionResult.isTransparent) { //test all objects that fall into this distance mFurthestPickingDistance = (pickedMovable->getParentNode()->_getDerivedPosition() - cameraRay.getOrigin()).length() + pickedMovable->getBoundingRadius(); } } else { if (collisionResult.distance > mFurthestPickingDistance) { mContinuePickingThisContext = false; return; } else { if (!mResult.empty() && mResult[mResult.size() - 1].distance > collisionResult.distance) { //If the last result is transparent, add another result, but if it's not replace it. if (!mResult[mResult.size() - 1].isTransparent) { mResult.pop_back(); } } else { return; } } } EmberEntity& pickedEntity = anUserObject->getEmberEntity(); std::list<EmberEntity*> entities; entities.push_front(&pickedEntity); EmberEntity* entity = pickedEntity.getEmberLocation(); while (entity) { if (entity->getCompositionMode() == EmberEntity::CM_COMPOSITION) { entities.push_front(entity); } else if (entity->getCompositionMode() == EmberEntity::CM_COMPOSITION_EXCLUSIVE) { entities.clear(); entities.push_front(entity); } entity = entity->getEmberLocation(); } for (std::list<EmberEntity*>::const_iterator I = entities.begin(); I != entities.end(); ++I) { EntityPickResult result; result.entity = *I; result.position = collisionResult.position; result.distance = collisionResult.distance; result.isTransparent = collisionResult.isTransparent; mResult.push_back(result); } } } } } }