bool SnapToMovement::testSnapTo(const WFMath::Point<3>& position, const WFMath::Quaternion& orientation, WFMath::Vector<3>& adjustment, EmberEntity* snappedToEntity) { try { for (std::vector<Ogre::SceneNode*>::iterator I = mDebugNodes.begin(); I != mDebugNodes.end(); ++I) { Ogre::SceneNode* node = *I; node->setVisible(false); Ogre::Entity* sphereEntity = static_cast<Ogre::Entity*> (node->getAttachedObject(0)); sphereEntity->setMaterialName("/global/authoring/point"); } } catch (const std::exception& ex) { S_LOG_WARNING("Error when setting up debug nodes for snapping." << ex); } std::vector<Ogre::SceneNode*>::iterator nodeIterator = mDebugNodes.begin(); //Use an auto pointer to allow both for undefined values and automatic cleanup when exiting the method. std::auto_ptr<SnapPointCandidate> closestSnapping(0); WFMath::AxisBox<3> currentBbox = mEntity.getBBox(); //Translate the bbox into a rotbox WFMath::RotBox<3> currentRotbox; currentRotbox.size() = currentBbox.highCorner() - currentBbox.lowCorner(); currentRotbox.corner0() = currentBbox.lowCorner(); currentRotbox.orientation().identity(); currentRotbox.rotatePoint(orientation, WFMath::Point<3>(0, 0, 0)); currentRotbox.shift(WFMath::Vector<3>(position)); //See if we should visualize debug nodes for the moved entity for (size_t j = 0; j < currentRotbox.numCorners(); ++j) { WFMath::Point<3> currentPoint = currentRotbox.getCorner(j); if (currentPoint.isValid() && nodeIterator != mDebugNodes.end()) { Ogre::SceneNode* node = *nodeIterator; node->setPosition(Convert::toOgre(currentPoint)); node->setVisible(true); nodeIterator++; } } //First find all entities which are close enough //Then try to do a snap movement based on the points of the eris bounding boxes. I.e. we only provide support for snapping one corner of a bounding box to another corner (for now). WFMath::Ball<3> boundingSphere = mEntity.getBBox().boundingSphere(); Ogre::Sphere sphere(mNode._getDerivedPosition(), boundingSphere.radius() * 2); Ogre::SphereSceneQuery* query = mSceneManager.createSphereQuery(sphere); Ogre::SceneQueryResult& result = query->execute(); for (Ogre::SceneQueryResultMovableList::const_iterator I = result.movables.begin(); I != result.movables.end(); ++I) { Ogre::MovableObject* movable = *I; if (movable->getUserAny().getType() == typeid(EmberEntityUserObject::SharedPtr)) { EmberEntityUserObject* anUserObject = Ogre::any_cast<EmberEntityUserObject::SharedPtr>(movable->getUserAny()).get(); EmberEntity& entity = anUserObject->getEmberEntity(); if (&entity != &mEntity && entity.hasBBox()) { //Ok, we have an entity which is close to our entity. Now check if any of the points of the bounding box is close. WFMath::AxisBox<3> bbox = entity.getBBox(); if (bbox.isValid()) { WFMath::RotBox<3> rotbox; rotbox.size() = bbox.highCorner() - bbox.lowCorner(); rotbox.corner0() = bbox.lowCorner(); rotbox.orientation().identity(); rotbox.rotatePoint(entity.getViewOrientation(), WFMath::Point<3>(0, 0, 0)); rotbox.shift(WFMath::Vector<3>(entity.getViewPosition())); for (size_t i = 0; i < rotbox.numCorners(); ++i) { WFMath::Point<3> point = rotbox.getCorner(i); Ogre::SceneNode* currentNode(0); //If there is any unclaimed debug node left we'll use it to visualize the corner if (nodeIterator != mDebugNodes.end()) { currentNode = *nodeIterator; currentNode->setPosition(Convert::toOgre(point)); currentNode->setVisible(true); nodeIterator++; } point.z() = 0; for (size_t j = 0; j < currentRotbox.numCorners(); ++j) { WFMath::Point<3> currentPoint = currentRotbox.getCorner(j); currentPoint.z() = 0; WFMath::CoordType distance = WFMath::Distance(currentPoint, point); if (distance <= mSnapThreshold) { if (currentNode) { Ogre::Entity* sphereEntity = static_cast<Ogre::Entity*> (currentNode->getAttachedObject(0)); if (sphereEntity) { try { sphereEntity->setMaterialName("/global/authoring/point/moved"); } catch (const std::exception& ex) { S_LOG_WARNING("Error when setting material for point." << ex); } } } if (!closestSnapping.get()) { closestSnapping = std::auto_ptr<SnapPointCandidate>(new SnapPointCandidate()); closestSnapping->entity = &entity; closestSnapping->distance = distance; closestSnapping->adjustment = point - currentPoint; } else if (distance < closestSnapping->distance) { closestSnapping->entity = &entity; closestSnapping->distance = distance; closestSnapping->adjustment = point - currentPoint; } } } } } } } } mSceneManager.destroyQuery(query); if (closestSnapping.get()) { adjustment = closestSnapping->adjustment; snappedToEntity = closestSnapping->entity; return true; } 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); } } } } } }