void PolygonPointMover::move(const WFMath::Vector<3>& directionVector) { if (directionVector.isValid()) { getActivePoint()->translate(WFMath::Vector<2>(directionVector.x(), directionVector.y())); mPolygon.updateRender(); } }
void HeightMapFlatSegment::getHeightAndNormal(float x, float y, float& height, WFMath::Vector<3>& normal) const { height = mHeight; normal.x() = 0; normal.y() = 0; normal.z() = 1; }
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; } }
bool Position2DAdapter::_hasChanges() { WFMath::Vector<2> originalValue; originalValue.fromAtlas(mOriginalElement); WFMath::Vector<2> newValue; newValue.fromAtlas(getValue()); return originalValue != newValue; }
void Position2DAdapter::fillElementFromGui() { WFMath::Vector<2> vector; if (mXWindow) { vector.x() = atof(mXWindow->getText().c_str()); } if (mYWindow) { vector.y() = atof(mYWindow->getText().c_str()); } mEditedElement = vector.toAtlas(); }
void Steering::moveInDirection(const WFMath::Vector<2>& direction) { WFMath::Vector<3> fullDirection(direction.x(), 0, direction.y()); WFMath::Quaternion orientation; if (direction != WFMath::Vector<2>::ZERO()) { orientation.rotation(WFMath::Vector<3>(0, 0, 1), WFMath::Vector<3>(fullDirection).normalize(), WFMath::Vector<3>(0, 1, 0)); } mAvatar.moveInDirection(fullDirection, orientation); mLastSentVelocity = direction; mExpectingServerMovement = true; }
void Steering::moveToPoint(const WFMath::Point<3>& point) { auto entity3dPosition = mAvatar.getEntity()->getViewPosition(); WFMath::Vector<3> vel = point - entity3dPosition; WFMath::Quaternion orientation; if (vel != WFMath::Vector<3>::ZERO()) { orientation.rotation(WFMath::Vector<3>(0, 0, 1), WFMath::Vector<3>(vel).normalize(), WFMath::Vector<3>(0, 1, 0)); } mAvatar.moveToPoint(point, orientation); mLastSentVelocity = WFMath::Vector<2>(vel.x(), vel.z()); mExpectingServerMovement = true; }
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 EntityMoverBase::move(const WFMath::Vector<3>& directionVector) { if (directionVector.isValid()) { mNode->translate(Convert::toOgre(directionVector)); newEntityPosition(mNode->getPosition()); Moved.emit(); } }
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 InspectWidget::showEntityInfo(EmberEntity* entity) { Eris::Entity* parent = entity->getLocation(); std::stringstream ss; ss.precision(4); ss << "Name: " << entity->getName() << "\n"; ss << "Id: " << entity->getId() << "\n"; ss << "Parent: "; if (parent) { ss << parent->getName() << " (Id: " << parent->getId() << ")"; } else { ss << "none"; } ss << "\n"; if (entity->getPredictedPos().isValid()) { ss << "PredPosition: " << entity->getPredictedPos() << "\n"; } if (entity->getPosition().isValid()) { ss << "Position: " << entity->getPosition() << "\n"; } WFMath::Vector<3> velocity = entity->getPredictedVelocity(); if (velocity.isValid()) { ss << "Velocity: " << velocity << ": " << sqrt(velocity.sqrMag()) << "\n"; } if (entity->getOrientation().isValid()) { ss << "Orientation: " << entity->getOrientation() << "\n"; } if (entity->getBBox().isValid()) { ss << "Boundingbox: " << entity->getBBox() << "\n"; } ss << "Type: " << entity->getType()->getName() << "\n"; ss << "Attributes:\n"; ss << mAttributesString; mInfo->setText(ss.str()); mChangedThisFrame = false; }
bool TerrainArea::parseArea() { if (!mEntity.hasAttr("area")) { S_LOG_FAILURE("TerrainArea created for entity with no area attribute"); return false; } const Atlas::Message::Element areaElem(mEntity.valueOfAttr("area")); if (!areaElem.isMap()) { S_LOG_FAILURE("TerrainArea element ('area') must be of map type."); return false; } const Atlas::Message::MapType& areaData(areaElem.asMap()); int layer = 0; WFMath::Polygon<2> poly; TerrainAreaParser parser; if (parser.parseArea(areaData, poly, layer)) { if (!mArea) { mArea = new Mercator::Area(layer, false); } else { //A bit of an ugly hack here since the Mercator system doesn't support changing the layer. We need to swap the old area for a new one if the layer has changed. if (mArea->getLayer() != layer) { mOldArea = mArea; mArea = new Mercator::Area(layer, false); } } // transform polygon into terrain coords WFMath::Vector<3> xVec = WFMath::Vector<3>(1.0, 0.0, 0.0).rotate(mEntity.getOrientation()); double theta = atan2(xVec.y(), xVec.x()); // rotation about Z WFMath::RotMatrix<2> rm; poly.rotatePoint(rm.rotation(theta), WFMath::Point<2>(0, 0)); poly.shift(WFMath::Vector<2>(mEntity.getPosition().x(), mEntity.getPosition().y())); mArea->setShape(poly); return true; } else { return false; } }
Mercator::TerrainMod* InnerTranslatorImpl<ModT, ShapeT>::createInstance(const WFMath::Point<3>& pos, const WFMath::Quaternion& orientation) { ShapeT<2> shape = this->mShape; if (!shape.isValid() || !pos.isValid()) { return nullptr; } if (orientation.isValid()) { /// rotation about Z axis WFMath::Vector<3> xVec = WFMath::Vector<3>(1.0, 0.0, 0.0).rotate(orientation); WFMath::CoordType theta = std::atan2(xVec.y(), xVec.x()); WFMath::RotMatrix<2> rm; shape.rotatePoint(rm.rotation(theta), WFMath::Point<2>(0, 0)); } shape.shift(WFMath::Vector<2>(pos.x(), pos.y())); float level = TerrainModTranslator::parsePosition(pos, this->mData); return new ModT<ShapeT>(level, shape); }
/// \brief Get an accurate height and normal vector at a given coordinate /// relative to this segment. /// /// The height and surface normal are determined by finding the four adjacent /// height points nearest to the coordinate, and interpolating between /// those height values. The square area defined by the 4 height points is /// considered as two triangles for the purposes of interpolation to ensure /// that the calculated height falls on the surface rendered by a 3D /// graphics engine from the same heightfield data. The line used to /// divide the area is defined by the gradient y = x, so the first /// triangle has relative vertex coordinates (0,0) (1,0) (1,1) and /// the second triangle has vertex coordinates (0,0) (0,1) (1,1). void Segment::getHeightAndNormal(float x, float y, float& h, WFMath::Vector<3> &normal) const { // FIXME this ignores edges and corners assert(x <= m_res); assert(x >= 0.0f); assert(y <= m_res); assert(y >= 0.0f); // get index of the actual tile in the segment int tile_x = I_ROUND(std::floor(x)); int tile_y = I_ROUND(std::floor(y)); // work out the offset into that tile float off_x = x - tile_x; float off_y = y - tile_y; float h1=get(tile_x, tile_y); float h2=get(tile_x, tile_y+1); float h3=get(tile_x+1, tile_y+1); float h4=get(tile_x+1, tile_y); // square is broken into two triangles // top triangle |/ if ((off_x - off_y) <= 0.f) { normal = WFMath::Vector<3>(h2-h3, h1-h2, 1.0f); //normal for intersection of both triangles if (off_x == off_y) { normal += WFMath::Vector<3>(h1-h4, h4-h3, 1.0f); } normal.normalize(); h = h1 + (h3-h2) * off_x + (h2-h1) * off_y; } // bottom triangle /| else { normal = WFMath::Vector<3>(h1-h4, h4-h3, 1.0f); normal.normalize(); h = h1 + (h4-h1) * off_x + (h3-h4) * off_y; } }
void TerrainPageShadow::updateShadow(const TerrainPageGeometry& geometry) { if (!mImage) { mImage = new OgreImage(new Image::ImageBuffer(mTerrainPage.getBlendMapSize(), 1)); } mImage->reset(); int pageSizeInVertices = mTerrainPage.getPageSize(); int pageSizeInMeters = pageSizeInVertices - 1; //since Ogre uses a different coord system than WF, we have to do some conversions here TerrainPosition origPosition(0, pageSizeInMeters - 1); WFMath::Vector<3> wfLightDirection = mLightDirection; wfLightDirection = wfLightDirection.normalize(1); TerrainPosition position(origPosition); auto data = mImage->getData(); for (int i = 0; i < pageSizeInMeters; ++i) { position = origPosition; position[1] = position[1] - i; for (int j = 0; j < pageSizeInMeters; ++j) { WFMath::Vector<3> normal; if (geometry.getNormal(position, normal)) { float dotProduct = WFMath::Dot(normal.normalize(1), wfLightDirection); // if the dotProduct is > 0, the face is looking away from the sun *data = static_cast<unsigned char>((1.0f - ((dotProduct + 1.0f) * 0.5f)) * 255); } else { *data = 0; } data++; position[0] = position[0] + 1; } } }
bool TerrainArea::placeArea(WFMath::Polygon<2>& poly) { //If the position if invalid we can't do anything with the area yet. if (!mEntity.getPosition().isValid()) { return false; } // transform polygon into terrain coords if (mEntity.getOrientation().isValid()) { WFMath::Vector<3> xVec = WFMath::Vector<3>(1.0, 0.0, 0.0).rotate(mEntity.getOrientation()); double theta = atan2(xVec.y(), xVec.x()); // rotation about Z WFMath::RotMatrix<2> rm; poly.rotatePoint(rm.rotation(theta), WFMath::Point<2>(0, 0)); } poly.shift(WFMath::Vector<2>(mEntity.getPosition().x(), mEntity.getPosition().y())); return true; }
void SoundService::updateListenerPosition(const WFMath::Point<3>& pos, const WFMath::Vector<3>& direction, const WFMath::Vector<3>& up) { if (!isEnabled()) { return; } alListener3f(AL_POSITION, pos.x(), pos.y(), pos.z()); SoundGeneral::checkAlError("Setting the listener position."); //Set the direction of the listener. ALfloat aluVectors[6]; aluVectors[0] = direction.x(); aluVectors[1] = direction.y(); aluVectors[2] = direction.z(); aluVectors[3] = up.x(); aluVectors[4] = up.y(); aluVectors[5] = up.z(); alListenerfv(AL_ORIENTATION, aluVectors); SoundGeneral::checkAlError("Setting the listener orientation."); }
int main() { Mercator::Terrain terrain; terrain.setBasePoint(0, 0, 2.8); terrain.setBasePoint(1, 0, 7.1); terrain.setBasePoint(0, 1, 0.2); terrain.setBasePoint(1, 1, 14.7); Mercator::Segment * segment = terrain.getSegment(0, 0); if (segment == 0) { std::cerr << "Segment not created by addition of required basepoints" << std::endl << std::flush; return 1; } segment->populate(); //test box definitely outside terrain WFMath::AxisBox<3> highab(WFMath::Point<3> (10.0, 10.0, segment->getMax() + 3.0), WFMath::Point<3> (20.0, 20.0, segment->getMax() + 6.1)); if (Mercator::Intersect(terrain, highab)) { std::cerr << "axisbox intersects with terrain even though it should be above it" << std::endl; return 1; } //test box definitely inside terrain WFMath::AxisBox<3> lowab(WFMath::Point<3> (10.0, 10.0, segment->getMin() - 6.1), WFMath::Point<3> (20.0, 20.0, segment->getMax() - 3.0)); if (!Mercator::Intersect(terrain, lowab)) { std::cerr << "axisbox does not intersect with terrain even though it should be below it" << std::endl; return 1; } //test axis box moved from above terrain to below it. bool inter=false; float dz = highab.highCorner()[2] - highab.lowCorner()[2] - 0.1; while (highab.highCorner()[2] > segment->getMin()) { highab.shift(WFMath::Vector<3>(0.0, 0.0, -dz)); if (Mercator::Intersect(terrain, highab)) { inter=true; break; } } if (!inter) { std::cerr << "axisbox passed through terrain with no intersection" << std::endl; return 1; } //test axisbox that spans two segments terrain.setBasePoint(0, 2, 4.8); terrain.setBasePoint(1, 2, 3.7); Mercator::Segment *segment2 = terrain.getSegment(0, 1); segment2->populate(); float segmax=std::max(segment->getMax(), segment2->getMax()); float segmin=std::min(segment->getMin(), segment2->getMin()); WFMath::AxisBox<3> ab(WFMath::Point<3> (50.0, 10.0, segmax + 3.0), WFMath::Point<3> (70.0, 20.0, segmax + 6.1)); if (Mercator::Intersect(terrain, ab)) { std::cerr << "axisbox2 intersects with terrain even though it should be above it" << std::endl; return 1; } WFMath::AxisBox<3> ab2(WFMath::Point<3> (50.0, 10.0, segmin - 6.1), WFMath::Point<3> (70.0, 20.0, segmin + 3.0)); if (!Mercator::Intersect(terrain, ab2)) { std::cerr << "axisbox2 does not intersect with terrain even though it should be below it" << std::endl; return 1; } WFMath::Point<3> intPoint; WFMath::Vector<3> intNorm; float par; //test vertical ray if (Mercator::Intersect(terrain, WFMath::Point<3>(20.1, 20.2, segmax + 3), WFMath::Vector<3>(0.0,0.0,50.0), intPoint, intNorm, par)) { std::cerr << "vertical ray intersected when it shouldnt" << std::endl; return 1; } if (!Mercator::Intersect(terrain, WFMath::Point<3>(20.1, 20.2, segmax + 3), WFMath::Vector<3>(0.0,0.0,-50.0), intPoint, intNorm, par)) { std::cerr << "vertical ray didnt intersect when it should" << std::endl; return 1; } //test each quadrant if (!Mercator::Intersect(terrain, WFMath::Point<3>(20.1, 20.2, segmax + 3), WFMath::Vector<3>(10.0,10.0,-100.0), intPoint, intNorm, par)) { std::cerr << "quad1 ray didnt intersect when it should" << std::endl; return 1; } if (!Mercator::Intersect(terrain, WFMath::Point<3>(20.1, 20.2, segmax + 3), WFMath::Vector<3>(10.0,-15.0,-50.0), intPoint, intNorm, par)) { std::cerr << "quad2 ray didnt intersect when it should" << std::endl; return 1; } if (!Mercator::Intersect(terrain, WFMath::Point<3>(20.1, 20.2, segmax + 3), WFMath::Vector<3>(-10.0,-10.0,-50.0), intPoint, intNorm, par)) { std::cerr << "quad3 ray didnt intersect when it should" << std::endl; return 1; } if (!Mercator::Intersect(terrain, WFMath::Point<3>(20.1, 20.2, segmax + 3), WFMath::Vector<3>(-10.0,10.0,-50.0), intPoint, intNorm, par)) { std::cerr << "quad4 ray didnt intersect when it should" << std::endl; return 1; } //test dx==0 and dy==0 if (!Mercator::Intersect(terrain, WFMath::Point<3>(20.1, 20.2, segmax + 3), WFMath::Vector<3>(0.0,10.0,-50.0), intPoint, intNorm, par)) { std::cerr << "y+ ray didnt intersect when it should" << std::endl; return 1; } if (!Mercator::Intersect(terrain, WFMath::Point<3>(20.1, 20.2, segmax + 3), WFMath::Vector<3>(0.0,-10.0,-50.0), intPoint, intNorm, par)) { std::cerr << "y- ray didnt intersect when it should" << std::endl; return 1; } if (!Mercator::Intersect(terrain, WFMath::Point<3>(20.1, 20.2, segmax + 3), WFMath::Vector<3>(-10.0,0.0,-50.0), intPoint, intNorm, par)) { std::cerr << "x- ray didnt intersect when it should" << std::endl; return 1; } if (!Mercator::Intersect(terrain, WFMath::Point<3>(20.1, 20.2, segmax + 3), WFMath::Vector<3>(10.0,0.0,-50.0), intPoint, intNorm, par)) { std::cerr << "x+ ray didnt intersect when it should" << std::endl; return 1; } //test a longer ray if (!Mercator::Intersect(terrain, WFMath::Point<3>(-10.08, -20.37, segmax + 3), WFMath::Vector<3>(100.0,183.0,-50.0), intPoint, intNorm, par)) { std::cerr << "long ray didnt intersect when it should" << std::endl; return 1; } //check the height value float h; WFMath::Vector<3> n; terrain.getHeightAndNormal(intPoint[0], intPoint[1], h, n); n.normalize(); if (n != intNorm) { std::cerr << "calculated normal is different from getHeightAndNormal" << std::endl; std::cerr << intPoint << std::endl; std::cerr << intNorm << "!=" << n << std::endl; // return 1; } // We can't check for equality here is it just doesn't work with // floats. Look it up in any programming book if you don't believe me. // - 20040721 <*****@*****.**> if (fabs(h - intPoint[2]) > 0.00001) { std::cerr << "calculated height is different from getHeightAndNormal" << std::endl; std::cerr << h << "!=" << intPoint[2] << std::endl; return 1; } return 0; }
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()); } } } }
void SoundSource::setVelocity(const WFMath::Vector<3>& vel) { assert(vel.isValid()); alSource3f(mALSource, AL_VELOCITY, vel.x(), vel.y(), vel.z()); SoundGeneral::checkAlError("Setting sound source velocity."); }
WFMath::Vector<3> SoundEntity::getVelocity() const { WFMath::Vector<3> velocity = mParentEntity.getPredictedVelocity(); return velocity.isValid() ? velocity : WFMath::Vector<3>::ZERO(); }