float EmberEntity::getHeight(const WFMath::Point<2>& localPosition) const { if (mHeightProvider) { float height = 0; if (mHeightProvider->getHeight(WFMath::Point<2>(localPosition.x(), localPosition.y()), height)) { return height; } } //A normal EmberEntity shouldn't know anything about the terrain, so we can't handle the area here. //Instead we just pass it on to the parent until we get to someone who knows how to handle this (preferably the terrain). if (getEmberLocation()) { WFMath::Point<2> adjustedLocalPosition(getPredictedPos().x(), getPredictedPos().y()); WFMath::Vector<3> xVec = WFMath::Vector<3>(1.0, 0.0, 0.0).rotate(getOrientation()); double theta = atan2(xVec.y(), xVec.x()); // rotation about Z WFMath::RotMatrix<2> rm; WFMath::Vector<2> adjustment(localPosition.x(), localPosition.y()); adjustment.rotate(rm.rotation(theta)); adjustedLocalPosition += adjustment; return getEmberLocation()->getHeight(adjustedLocalPosition) - getPredictedPos().z(); } WFMath::Point<3> predictedPos = getPredictedPos(); if (predictedPos.isValid()) { return predictedPos.z(); } else { return 0.0f; } }
void NodeController::updatePosition() { WFMath::Point<3> pos = mAttachment.getAttachedEntity().getPredictedPos(); WFMath::Quaternion orientation = mAttachment.getAttachedEntity().getOrientation(); WFMath::Vector<3> velocity = mAttachment.getAttachedEntity().getPredictedVelocity(); mAttachment.setPosition(pos.isValid() ? pos : WFMath::Point<3>::ZERO(), orientation.isValid() ? orientation : orientation.identity(), velocity.isValid() ? velocity : WFMath::Vector<3>::ZERO()); }
void EmberEntityLoader::EmberEntity_VisibilityChanged(bool visible, EmberEntity* entity) { WFMath::Point<3> viewPos = entity->getViewPosition(); if (viewPos.isValid()) { //When the visibility changes, we only need to reload the page the entity is on. mGeom.reloadGeometryPage(Convert::toOgre(viewPos)); } }
void WorldAttachment::getOffsetForContainedNode(const IEntityAttachment& attachment, const WFMath::Point<3>& localPosition, WFMath::Vector<3>& offset) { assert(localPosition.isValid()); assert(offset.isValid()); float height = 0; if (mTerrainManager.getHeight(WFMath::Point<2>(localPosition.x(), localPosition.y()), height)) { offset.z() = height - localPosition.z(); } }
void EntityEditor::addMarker(const std::string& entityId, const WFMath::Point<3>& point) { delete mMarker; mMarker = 0; if (point.isValid()) { Eris::Entity* entity = mWorld.getView().getEntity(entityId); if (entity) { const WFMath::Point<3> worldPosition = entity->getViewPosition() + WFMath::Vector<3>(point); delete mMarker; mMarker = new EntityPointMarker(mEntity, mWorld.getSceneManager(), mWorld.getTerrainManager(), worldPosition); mMarker->updateMarker(); } } }
void PolygonPointMover::setPosition(const WFMath::Point<3>& position) { if (position.isValid()) { //We need to offset into local space. Ogre::Vector3 posOffset = Ogre::Vector3::ZERO; if (getActivePoint()->getNode()->getParent()) { posOffset = getActivePoint()->getNode()->getParent()->_getDerivedPosition(); } Ogre::Vector3 newPos = Convert::toOgre(position) - posOffset; newPos = getActivePoint()->getNode()->getParent()->_getDerivedOrientation().Inverse() * newPos; WFMath::Vector<3> translation = Convert::toWF<WFMath::Vector<3>>(newPos - getActivePoint()->getNode()->getPosition()); //adjust it so that it moves according to the ground for example getActivePoint()->translate(WFMath::Vector<2>(translation.x(), translation.y())); mPolygon.updateRender(); } }
void EmberEntityLoader::removeEmberEntity(EmberEntity* entity) { if (!entity) { S_LOG_WARNING("Tried to remove a null ref entity from the paged geometry."); return; } #if EMBERENTITYLOADER_USEBATCH EntityLookup::iterator I = mEntityLookup.find(entity); if (I != mEntityLookup.end()) { EntityStore::iterator J = mEntities.find(I->second.first); if (J != mEntities.end()) { EntityColumn& column(J->second); EntityColumn::iterator K = column.find(I->second.second); if (K != column.end()) { EntityMap& entityMap(K->second); EntityMap::iterator L = entityMap.find(entity->getId()); if (L != entityMap.end()) { L->second.movedConnection.disconnect(); L->second.beingDeletedConnection.disconnect(); entityMap.erase(L); mEntityLookup.erase(I); } } } } #else EntityMap::iterator I = mEntities.find(entity->getId()); if (I != mEntities.end()) { ModelRepresentationInstance& instance(I->second); Model::ModelRepresentation* modelRepresentation(instance.modelRepresentation); instance.movedConnection.disconnect(); instance.beingDeletedConnection.disconnect(); //Reset the rendering distance to the one set by the model def. modelRepresentation->getModel().setRenderingDistance(modelRepresentation->getModel().getDefinition()->getRenderingDistance()); mEntities.erase(I); } #endif WFMath::Point<3> pos = entity->getViewPosition(); if (pos.isValid()) { //Rebuild geometry if necessary. mGeom.reloadGeometryPage(Convert::toOgre(pos)); } }
void EmberEntityLoader::EmberEntity_Moved(EmberEntity* entity) { EntityMap* entityMap(getStoreForEntity(entity)); if (entityMap) { EntityMap::iterator I = entityMap->find(entity->getId()); if (I != entityMap->end()) { ModelRepresentationInstance& instance(I->second); if (!instance.lastPosition.isNaN()) { mGeom.reloadGeometryPage(instance.lastPosition); } WFMath::Point<3> viewPos = entity->getViewPosition(); if (viewPos.isValid()) { mGeom.reloadGeometryPage(Convert::toOgre(viewPos)); instance.lastPosition = Convert::toOgre(viewPos); } } } }
void EntityMoverBase::setPosition(const WFMath::Point<3>& position) { WFMath::Point<3> finalPosition(position); if (position.isValid()) { WFMath::Vector<3> adjustment; EmberEntity* entity = nullptr; if (mSnapping.get() && mSnapping->testSnapTo(position, getOrientation(), adjustment, &entity)) { finalPosition = finalPosition.shift(adjustment); } //We need to offset into local space. Ogre::Vector3 posOffset = Ogre::Vector3::ZERO; if (mNode->getParent()) { posOffset = mNode->getParent()->_getDerivedPosition(); } mNode->setPosition(Convert::toOgre(finalPosition) - posOffset); newEntityPosition(mNode->getPosition()); Moved.emit(); } }
void EmberEntityLoader::loadPage(::Forests::PageInfo & page) { static Ogre::ColourValue colour(1, 1, 1, 1); #if EMBERENTITYLOADER_USEBATCH const int batchX = static_cast<int>(Ogre::Math::Floor(page.bounds.left/ mBatchSize)); const int batchY = static_cast<int>(Ogre::Math::Floor(page.bounds.top / mBatchSize)); EntityMap& entities(mEntities[batchX][batchY]); #else EntityMap& entities(mEntities); #endif for (EntityMap::iterator I = entities.begin(); I != entities.end(); ++I) { ModelRepresentationInstance& instance(I->second); Model::ModelRepresentation* modelRepresentation(instance.modelRepresentation); EmberEntity& emberEntity = modelRepresentation->getEntity(); if (emberEntity.isVisible()) { WFMath::Point<3> viewPos = emberEntity.getViewPosition(); if (viewPos.isValid()) { Ogre::Vector3 pos(Convert::toOgre(viewPos)); Model::Model& model(modelRepresentation->getModel()); Ogre::Node* node = model.getParentNode(); if (node) { const Ogre::Vector3& pos = node->_getDerivedPosition(); if (pos.x > page.bounds.left && pos.x < page.bounds.right && pos.z > page.bounds.top && pos.z < page.bounds.bottom) { for (Model::Model::SubModelSet::const_iterator J = model.getSubmodels().begin(); J != model.getSubmodels().end(); ++J) { // if (!(*J)->getEntity()->getParentSceneNode()) { // model->getParentSceneNode()->attachObject((*J)->getEntity()); // } // if ((*J)->getEntity()->isVisible()) { addEntity((*J)->getEntity(), pos, node->_getDerivedOrientation(), modelRepresentation->getScale(), colour); // (*J)->getEntity()->setVisible(false); // } } } } } } } }
void EmberEntityLoader::addEmberEntity(Model::ModelRepresentation* modelRepresentation) { if (!modelRepresentation) { S_LOG_WARNING("Tried to add a null ref entity to the paged geometry."); return; } EmberEntity& entity = modelRepresentation->getEntity(); ModelRepresentationInstance instance; instance.movedConnection = entity.Moved.connect(sigc::bind(sigc::mem_fun(*this, &EmberEntityLoader::EmberEntity_Moved), &entity)); instance.beingDeletedConnection = entity.BeingDeleted.connect(sigc::bind(sigc::mem_fun(*this, &EmberEntityLoader::EmberEntity_BeingDeleted), &entity)); instance.visibilityChangedConnection = entity.VisibilityChanged.connect(sigc::bind(sigc::mem_fun(*this, &EmberEntityLoader::EmberEntity_VisibilityChanged), &entity)); instance.modelRepresentation = modelRepresentation; WFMath::Point<3> viewPosition = entity.getViewPosition(); Ogre::Vector3 position(std::numeric_limits<Ogre::Real>::quiet_NaN(), std::numeric_limits<Ogre::Real>::quiet_NaN(), std::numeric_limits<Ogre::Real>::quiet_NaN()); bool isValidPos = false; if (viewPosition.isValid()) { isValidPos = true; position = Convert::toOgre(viewPosition); } instance.lastPosition = position; #if EMBERENTITYLOADER_USEBATCH const int batchX = Ogre::Math::Floor(position.x / mBatchSize); const int batchY = Ogre::Math::Floor(position.y / mBatchSize); mEntityLookup[entity] = std::pair<int, int>(batchX, batchY); EntityMap& entities(mEntities[batchX][batchY]); #else EntityMap& entities(mEntities); #endif entities[entity.getId()] = instance; if (isValidPos) { //Rebuild geometry if necessary mGeom.reloadGeometryPage(position); } }
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 SoundSource::setPosition(const WFMath::Point<3>& pos) { assert(pos.isValid()); alSource3f(mALSource, AL_POSITION, pos.x(), pos.y(), pos.z()); SoundGeneral::checkAlError("Setting sound source position."); }
void Steering::update() { if (mSteeringEnabled) { if (mUpdateNeeded) { updatePath(); } auto entity = mAvatar.getEntity(); if (!mPath.empty()) { const auto& finalDestination = mPath.back(); auto entity3dPosition = entity->getViewPosition(); const WFMath::Point<2> entityPosition(entity3dPosition.x(), entity3dPosition.z()); //First check if we've arrived at our actual destination. if (WFMath::Distance(WFMath::Point<2>(finalDestination.x(), finalDestination.z()), entityPosition) < 0.1f) { //We've arrived at our destination. If we're moving we should stop. if (mLastSentVelocity.isValid() && mLastSentVelocity != WFMath::Vector<2>::ZERO()) { moveInDirection(WFMath::Vector<2>::ZERO()); } stopSteering(); } else { //We should send a move op if we're either not moving, or we've reached a waypoint, or we need to divert a lot. WFMath::Point<2> nextWaypoint(mPath.front().x(), mPath.front().z()); if (WFMath::Distance(nextWaypoint, entityPosition) < 0.1f) { mPath.pop_front(); nextWaypoint = WFMath::Point<2>(mPath.front().x(), mPath.front().z()); } WFMath::Vector<2> velocity = nextWaypoint - entityPosition; WFMath::Point<2> destination; velocity.normalize(); if (mPath.size() == 1) { //if the next waypoint is the destination we should send a "move to position" update to the server, to make sure that we stop when we've arrived. //otherwise, if there's too much lag, we might end up overshooting our destination and will have to double back destination = nextWaypoint; } //Check if we need to divert in order to avoid colliding. WFMath::Vector<2> newVelocity; bool avoiding = mAwareness.avoidObstacles(entityPosition, velocity * mSpeed, newVelocity); if (avoiding) { auto newMag = newVelocity.mag(); auto relativeMag = mSpeed / newMag; velocity = newVelocity; velocity.normalize(); velocity *= relativeMag; mUpdateNeeded = true; } bool shouldSend = false; if (velocity.isValid()) { if (mLastSentVelocity.isValid()) { //If the entity has stopped, and we're not waiting for confirmation to a movement request we've made, we need to start moving. if (!entity->isMoving() && !mExpectingServerMovement) { shouldSend = true; } else { auto currentTheta = std::atan2(mLastSentVelocity.y(), mLastSentVelocity.x()); auto newTheta = std::atan2(velocity.y(), velocity.x()); //If we divert too much from where we need to go we must adjust. if (std::abs(currentTheta - newTheta) > WFMath::numeric_constants<double>::pi() / 20) { shouldSend = true; } } } else { //If we've never sent a movement update before we should do that now. shouldSend = true; } } if (shouldSend) { //If we're moving to a certain destination and aren't avoiding anything we should tell the server to move to the destination. if (destination.isValid() && !avoiding) { moveToPoint(WFMath::Point<3>(destination.x(), entity3dPosition.y(), destination.y())); } else { moveInDirection(velocity); } } } } else { //We are steering, but the path is empty, which means we can't find any path. If we're moving we should stop movement. //But we won't stop steering; perhaps we'll find a path later. if (mLastSentVelocity.isValid() && mLastSentVelocity != WFMath::Vector<2>::ZERO()) { moveInDirection(WFMath::Vector<2>::ZERO()); } } } }
WFMath::Point<3> SoundEntity::getPosition() const { WFMath::Point<3> pos = mParentEntity.getViewPosition(); return pos.isValid() ? pos : WFMath::Point<3>::ZERO(); }