Logo Search packages:      
Sourcecode: earth3d version File versions  Download package

navigator.cpp

#include "navigator.h"
#include "quaternion.h"
#include "matrix.h"
#include <assert.h>
#include "globalsettings.h"

#define min(a,b) ((a)<(b)?(a):(b))

Navigator::Navigator() {
  speed = 0;
  up = Point3D(0,1,0);
  autoNavigation = false;
}

Navigator::~Navigator() {
}

void Navigator::accelerate(float factor) {
  speed += factor;
}

void Navigator::stop() {
  speed = 0;
}

void Navigator::setViewer(Point3D newViewer) {
  viewer = newViewer;
#ifdef DEBUG
  printf("viewer=%f %f %f\n", newViewer.x, newViewer.y, newViewer.z);
#endif
  needRedraw = true;
}

Point3D Navigator::getViewer() {
  return(viewer);
}

void Navigator::setDirection(Point3D newDirection) {
#ifdef DEBUG
  printf("setDirection(%f, %f, %f)\n", newDirection.x, newDirection.y, newDirection.z);
#endif
  direction = newDirection;
}

Point3D Navigator::getDirection() {
  return(direction);
}

void Navigator::setUp(Point3D newUp) {
#ifdef DEBUG
  printf("setUp(%f, %f, %f)\n", newUp.x, newUp.y, newUp.z);
#endif
  up = newUp;
}

Point3D Navigator::getUp() {
  return(up);
}

void Navigator::rotate(float angle) {
  stopAutoNavigation();

  /* rotate the up vector around the direction vector */
  float matrix[16];
  Quaternion quat;
  quat.createFromAxisAngle(direction.x, direction.y, direction.z, angle);
  quat.createMatrix(matrix);
  up = Matrix::multvector(matrix, up);
}

void Navigator::rotateLeftRight(float angle) {
  stopAutoNavigation();

  /* rotate the direction vector around the up vector */
  float matrix[16];
  Quaternion quat;
  quat.createFromAxisAngle(up.x, up.y, up.z, angle);
  quat.createMatrix(matrix);
  direction = Matrix::multvector(matrix, direction);
}

void Navigator::rotateOnSurface(float angle) {
  stopAutoNavigation();

  /* rotate the direction vector around the up vector */
  float matrix[16];
  Quaternion quat;
  Point3D viewnorm = viewer.normalize();

  quat.createFromAxisAngle(viewnorm.x, viewnorm.y, viewnorm.z, angle);
  quat.createMatrix(matrix);
  direction = Matrix::multvector(matrix, direction);
  up = Matrix::multvector(matrix, up);
}

void Navigator::elevate(float angle) {
  stopAutoNavigation();

  /* rotate the direction vector around the upXdirection vector */
  Point3D upxdir = Matrix::DotP3D(&up, &direction);
  Matrix::normalize(&upxdir);

  float matrix[16];
  Quaternion quat;
  quat.createFromAxisAngle(upxdir.x, upxdir.y, upxdir.z, angle);
  quat.createMatrix(matrix);
  direction = Matrix::multvector(matrix, direction);
  up = Matrix::multvector(matrix, up);
}

void Navigator::elevateUp(float factor) {
  stopAutoNavigation();

  float surfacedist = viewer.length()-1;
  setViewer(viewer+(viewer.normalize()/60.)*factor*surfacedist);
}

void Navigator::step(float steps) {
  if (autoNavigation) {
    QTime currentTime = QTime::currentTime();    
    int timestep = autoNavTimeLastFrame.msecsTo(currentTime);
    if (timestep>1000) timestep = 1000;
    
    autoNavPos += autoNavStep*(float(timestep)/(1000./50.));
    if (autoNavPos>1) autoNavPos=1;

    autoNavTimeLastFrame = currentTime;

    Point3D newMark2D = sourceMark*(1.-autoNavPos) + targetMark*autoNavPos;
    double height = newMark2D.z;
    height += autoNavDistance*(-pow(fabs(0.5-autoNavPos),2)+0.25)*10;
    Point3D newMark3D = gSphere.getPoint(Point2D(newMark2D.x, newMark2D.y), height);
    setViewer(newMark3D);
    setDirection((sourceDir*(1.-autoNavPos) + targetDir*autoNavPos-getViewer()).normalize());
    setUp((sourceUp*(1.-autoNavPos) + targetUp*autoNavPos).normalize());

    if (autoNavPos>=1.) stopAutoNavigation();
  }
  else {
    /* reduce speed when the surface is near */
    float surfacedist = viewer.length()-1;
    viewer += direction*(speed*surfacedist)*steps;
  }
}

void Navigator::forward(float factor) {
  viewer += direction*factor*(viewer.length()-1);
}

void Navigator::strafe(float factor) {
  /* rotate the direction by 90 degrees */
  float matrix[16];
  Quaternion quat;
  Point3D norm = viewer.normalize();

  quat.createFromAxisAngle(norm.x, norm.y, norm.z, 90);
  quat.createMatrix(matrix);
  Point3D strafedirection = Matrix::multvector(matrix, direction);

  viewer += strafedirection*factor*(viewer.length()-1);
}

void Navigator::forwardOnSurface(float factor) {
  double viewerdistance = viewer.length();

  Point3D v1 = viewer;
  forward(factor);

  // keep distance
  Point3D v2 = viewer;
  viewer = viewer.normalize() * viewerdistance;

  // rotate forward and up
//   double alpha = acos((v2-v1).length()/(viewer-v1).length());
  Point3D v1n = v1.normalize();
  double alpha = acos(viewer.normalize().skalarprodukt(v1n));

  Point3D r = (v2-v1).crossproduct(v2);

  float matrix[16];
  Quaternion quat;
  Point3D norm = r.normalize();

  quat.createFromAxisAngle(norm.x, norm.y, norm.z, alpha);
  quat.createMatrix(matrix);
  direction = Matrix::multvector(matrix, direction);
  up = Matrix::multvector(matrix, up);
}

void Navigator::strafeOnSurface(float factor) {
  double viewerdistance = viewer.length();

  Point3D v1 = viewer;
  strafe(factor);

  // keep distance
  Point3D v2 = viewer;
  viewer = viewer.normalize() * viewerdistance;

  // rotate forward and up
  Point3D v1n = v1.normalize();
  double alpha = acos(viewer.normalize().skalarprodukt(v1n));
  Point3D r = (v2-v1).crossproduct(v2);

  float matrix[16];
  Quaternion quat;
  Point3D norm = r.normalize();

  quat.createFromAxisAngle(norm.x, norm.y, norm.z, alpha);
  quat.createMatrix(matrix);
  direction = Matrix::multvector(matrix, direction);
  up = Matrix::multvector(matrix, up);
}

void Navigator::strafeUpDown(float factor) {
  /* reduce speed when the surface is near */
  float surfacedist = viewer.length()-1;
  viewer += (viewer.normalize()*surfacedist*factor*0.01);

  if (viewer.length()<1) viewer = viewer.normalize();
}

void Navigator::moveToPosition(Point3D mark, Point3D dir, Point3D up, bool lowerquality) {
  targetMark = mark;
  targetDir = (dir-gSphere.getPoint(Point2D(mark.x, mark.y), mark.z)).normalize();
  
  /* get intersection point with sphere */
  targetDir = getEarthIntersection(targetDir, gSphere.getPoint(Point2D(mark.x, mark.y), mark.z));

  targetUp = up;

#ifdef DEBUG
  printf("target viewer=%f %f %f\n", targetMark.x, targetMark.y, targetMark.z);
#endif

  sourceMark = getViewer();
  printf("source viewer=%f %f %f\n", sourceMark.x, sourceMark.y, sourceMark.z);

  Point2D sourceMark2D = gSphere.inverse(sourceMark);
  sourceMark.x = sourceMark2D.x;
  sourceMark.y = sourceMark2D.y;
  sourceMark.z = getViewer().length();
  sourceDir = getDirection();
  sourceDir = getEarthIntersection(sourceDir, getViewer());
  printf("=================> target length: %f source length: %f\n", targetDir.length(), sourceDir.length());

  sourceUp = getUp();

  /* the outer borders of the map are connected. The way over the border could be shorter */
  if (fabs(sourceMark.x-targetMark.x)>0.5) {
    if (sourceMark.x>targetMark.x) {
      targetMark.x+=1;
    }
    else {
      targetMark.x-=1;
    }      
  }

  printf("source viewer 2D=%f %f %f\n", sourceMark.x, sourceMark.y, sourceMark.z);

  autoNavDistance = sqrt(pow(sourceMark.x-targetMark.x,2)+pow(sourceMark.y-targetMark.y,2));

  if (autoNavDistance<=0) autoNavDistance = 0.1;

  autoNavStep = (1./autoNavDistance)/1000.;
  if (autoNavStep>0.04) autoNavStep=0.04;

  autoNavigation = true;
  savedMAXTILESIZE = MAXTILESIZE;
  savedCENTERWEIGHT = CENTERWEIGHT;

  if (lowerquality) {
    MAXTILESIZE = 1.6;
    CENTERWEIGHT = 1;
  }

  autoNavPos = 0;
  autoNavTimeLastFrame = QTime::currentTime();
}

void Navigator::stopAutoNavigation() {
  if (autoNavigation) {
    autoNavigation = false;
    
    MAXTILESIZE=savedMAXTILESIZE;
    CENTERWEIGHT=savedCENTERWEIGHT;
  }
}

Point3D Navigator::getEarthIntersection(Point3D d, Point3D v) {
  double w;

  double a = d*d;
  double b = 2*(d*v);
  double c = v*v-1;

  w = b*b - 4*a*c;

  if (w<0) {
    /* no intersection */
    /* return intersection with larger sphere */
    return(v+d);
  }
  else {
    double a1 = -(v*d)+sqrt(w);
    double a2 = -(v*d)-sqrt(w);

    a1 = (-b+sqrt(w))/(2*a);
    a2 = (-b-sqrt(w))/(2*a);
    if (a1<0) a1=a2;
    if (a2<0) a2=a1;
    double a=min(a1,a2);
//     printf("Intersect: %f, %f, d=%f, w=%f, a1=%f, a2=%f\n", (v+d*a1).length(), (v+d*a2).length(), d.length(), w, a1, a2);
    return(v+d*a);
  }
}

Generated by  Doxygen 1.6.0   Back to index