[comp.sys.sgi] Have a better lookat

Dan Karron@UCBVAX.BERKELEY.EDU (03/24/91)

Has anyone have an algoritm for removing the annoying twist when you
use lookat() to view an object from arbitrary locations ?

I need the formulas given two points that define an eye vector will
return the euler angles (Z,Y,X is my preferred order) that will
(when inverted) give a view of an object with the world space +Z vector
always pointing up (+y in screen space) as much as possible. I am hard coding
values for the 24 cardinal points of a cube surrounding an object for
eye positions looking at the origin, and there must be a formula to calculate
the roll (Z euler angle) to keep the object right side up.

Cheers! (I will post the values that look good, but what is the formula
for arbitrary locations ?)

| karron@nyu.edu (e-mail alias )         Dan Karron, Research Associate      |
| Phone: 212 263 5210 Fax: 212 263 7190  New York University Medical Center  |
| 560 First Avenue                       Digital Pager <1> (212) 397 9330    |
| New York, New York 10016               <2> 10896   <3> <your-number-here>  |

blbates@AERO36.LARC.NASA.GOV (Brent Bates ViGYAN AAD/TAB) (03/25/91)

  A while back Stefan Farestam <farestam@orion.cerfacs.fr> posted some
equations for finding the proper twist angle and I just recently added
them to something I'm working on.  Here is part of his note:

 >* view_point is the point you are looking from
 >
 >* ref_point is the point you are looking towards
 >
 >* up_vec is a vector that represents the direction up (i.e. if you would
 >  plot up_vec after lookat was applied it would be a vertical vector
 >  pointing upwards on the screen)
 >
 >* v = view_point - ref_point
 >
 >* w = (up_vec) x (v)   where 'x' signifies the vector product
 >
 >* v and w are normalized
 >
 >then the twist angle used in lookat can be calculated by:
 >
 >   swy = (int) copysign(1.0, wy) ;
 >
 >   twist = swy * acos((wx*vz - wz*vx) / sqrt(vx*vx         + vz*vz)
 >                                      / sqrt(wx*wx + wy*wy + wz*wz)) ;
 >
 >and the call to lookat would look like:
 >
 >   lookat(view_point->x, view_point->y, view_point->z,
 >           ref_point->x,  ref_point->y,  ref_point->z, twist * 180/PI * 10) ;

   Below is my implemention in C:

/* I am looking from origin to a point in space, however the look vector is
   in opposite direction */
      look_x=(-x);
      look_y=(-y);
      look_z=(-z);

/* Non ambiguous direction vector in same x/y direction as REAL look vector */
      ra_x=x;
      ra_y=y;
      ra_z=0.0;

/* up_vec = look_vec X (unit_z_vec X ra_vec)
   (unit_z_vec X ra_vec) creates a vector perpendicular to look vector with
   the general "up" direction I want. */
      up_x=(-ra_x*z);
      up_y=(-ra_y*z);
      up_z=x*ra_x+y*ra_y;
/* Now up vector is perpendicular to look vector */

/* w_vec = up_vec X look_vec */
      w_x=up_y*look_z-up_z*look_y;
      w_y=up_z*look_x-up_x*look_z;
      w_z=up_x*look_y-up_y*look_x;
      mag=sqrt(look_x*look_x+look_y*look_y+look_z*look_z);
      look_x=look_x/mag;
      look_y=look_y/mag;
      look_z=look_z/mag;
      mag=sqrt(w_x*w_x+w_y*w_y+w_z*w_z);
      w_x=w_x/mag;
      w_y=w_y/mag;
      w_z=w_z/mag;
      swy=copysign(1.0,w_y);
      twist=(Angle) (swy*acos((w_x*look_z-w_z*look_x)/
                     sqrt(look_x*look_x+look_z*look_z)/
                     sqrt(w_x*w_x+w_y*w_y+w_z*w_z))*1800.0/pi);
      lookat(0.0,0.0,0.0,(Coord) x,(Coord) y,(Coord) z,twist);

   The important thing to note is that the up vector MUST be perpendicular
to the look vector.  I kept getting incorrect results until did this.
This seems to work for all look vectors.  I have tried various look vectors
that could give problems and the results were still correct.  Hope this
helps.

	Brent L. Bates
	NASA-Langley Research Center
	M.S. 361
	Hampton, Virginia  23665-5225
	Phone:(804) 864-2854          FAX:(804) 864-6792
	E-mail: blbates@aero36.larc.nasa.gov or blbates@aero8.larc.nasa.gov

andru@electron.lcs.mit.edu (Andrew Myers) (03/30/91)

In article <9103232036.AA05543@karron.med.nyu.edu> karron@cmcl2.nyu.edu writes:
>
>Has anyone have an algoritm for removing the annoying twist when you
>use lookat() to view an object from arbitrary locations ?

Contrary to popular belief, lookat has no "annoying twist" problem. Rather,
it has an "annoying axis" problem. lookat aligns things so that the Y axis
is up, not the Z axis as you might expect. You can compensate for this
by simply putting 90 degree rotations around your lookat command.

Andrew

thant@horus.esd.sgi.com (Thant Tessman) (04/04/91)

Here is my twice-yearly posting of an alternative to the lookat command:

To compile:  cc example.c upat.c -lgl_s -lm -o example

----------------------cut here--------------------------
#!/bin/sh -v
# this is a shell archive (once) named upat.mail
# created by thant@horus at Thu Jan 17 08:59:48 PST 1991
# 
# The rest of this file is a shell script which will extract: 
#	example.c
#	upat.c
#
#	to use, type sh upat.mail
#
echo x -  example.c
cat >  example.c  << 'EndOfRecord'
#include <gl.h>
#include <device.h>

extern void upat(float vx, float vy, float vz,
		 float px, float py, float pz,
		 float ux, float uy, float uz);

main() {

    int dev;
    short val;
    float i, j, k;

    initialize();

    /* first flyby */

    printf("Y is up.  (Just like lookat.)\n");
    j = 2.0;
    for (i = -8.0, k=12.0; i<12.0; i+=0.05, k-=0.05) {
	color(BLACK); clear();
	perspective(400, 5.0/4.0, 1.0, 50.0);
	upat(i, j, k,
	     0.0, 0.0, 0.0,
	     0.0, 1.0, 0.0);
	draw_axes();
	swapbuffers();
    }


    /* second flyby */

    printf("Z is up, but same path.\n");
    j = 2.0;
    for (i = -8.0, k=12.0; i<12.0; i+=0.05, k-=0.05) {
	color(BLACK); clear();
	perspective(400, 5.0/4.0, 1.0, 50.0);
	upat(i, j, k,
	     0.0, 0.0, 0.0,
	     0.0, 0.0, 1.0);
	draw_axes();
	swapbuffers();
    }


    /* third flyby */

    printf("(1, 1, 1) is up.  Different path.\n");
    for (i = -8.0, j=10.0, k=12.0; i<12.0; i+=0.05, k-=0.05, j-=0.05) {
	color(BLACK); clear();
	perspective(400, 5.0/4.0, 1.0, 50.0);
	upat(i, j, k,
	     0.0, 0.0, 0.0,
	     1.0, 1.0, 1.0);
	draw_axes();
	swapbuffers();
    }
}


initialize() {

    keepaspect(5, 4);
    winopen("");

    doublebuffer();
    gconfig();

    qdevice(ESCKEY);
}


draw_axes() {

    color(RED);
    move(0.0, 0.0, 0.0);
    draw(1.0, 0.0, 0.0);

    cmov(1.2, 0.0, 0.0);
    charstr("x");

    color(GREEN);
    move(0.0, 0.0, 0.0);
    draw(0.0, 1.0, 0.0);

    cmov(0.0, 1.2, 0.0);
    charstr("y");

    color(BLUE);
    move(0.0, 0.0, 0.0);
    draw(0.0, 0.0, 1.0);

    cmov(0.0, 0.0, 1.2);
    charstr("z");

    color(WHITE);
    move(0.5, 0.5, 0.5);
    draw(0.5, 0.5, 0.0);

    move(0.5, 0.0, 0.0);
    draw(0.5, 0.5, 0.0);
    draw(0.0, 0.5, 0.0);

    move(0.5, 0.5, 0.5);
    draw(0.0, 0.5, 0.5);

    move(0.0, 0.5, 0.0);
    draw(0.0, 0.5, 0.5);
    draw(0.0, 0.0, 0.5);

    move(0.5, 0.5, 0.5);
    draw(0.5, 0.0, 0.5);

    move(0.5, 0.0, 0.0);
    draw(0.5, 0.0, 0.5);
    draw(0.0, 0.0, 0.5);
}
EndOfRecord
len=`wc -c <  example.c `
if [ $len !=  1993  ] ; then
echo error:  example.c  was $len bytes long, should have been  1993
fi
echo x -  upat.c
cat >  upat.c  << 'EndOfRecord'
#include "math.h"
#include "gl.h"
#include "stdio.h"

#define X 0
#define Y 1
#define Z 2

void normalize(float *);
void cross(float *result, float *v1, float *v2);

void upat(float vx, float vy, float vz,
          float px, float py, float pz,
          float ux, float uy, float uz) {

    int i;
    float forward[3], side[3], up[3];
    float m[4][4];

    forward[X] = px - vx;
    forward[Y] = py - vy;
    forward[Z] = pz - vz;

    up[X] = ux;	    /* temporarily use view-up to hold world-up */
    up[Y] = uy;
    up[Z] = uz;

    normalize(forward);

    /* make side from view forward and world up */
    cross(side, forward, up);
    normalize(side);

    /* make view up from view forward and view side */
    cross(up, side, forward);

    m[0][0] = side[X];
    m[1][0] = side[Y];
    m[2][0] = side[Z];
    m[3][0] = 0.0;

    m[0][1] = up[X];
    m[1][1] = up[Y];
    m[2][1] = up[Z];
    m[3][1] = 0.0;

    m[0][2] = -forward[X];
    m[1][2] = -forward[Y];
    m[2][2] = -forward[Z];
    m[3][2] = 0.0;

    m[0][3] = 0.0;
    m[1][3] = 0.0;
    m[2][3] = 0.0;
    m[3][3] = 1.0;

    multmatrix(m);
    translate(-vx, -vy, -vz);

}


void normalize(float *v) {

    float r;

    r = sqrt( v[X]*v[X] + v[Y]*v[Y] + v[Z]*v[Z] );

    v[X] /= r;
    v[Y] /= r;
    v[Z] /= r;
}


void cross(float *result, float *v1, float *v2) {

    result[X] = v1[Y]*v2[Z] - v1[Z]*v2[Y];
    result[Y] = v1[Z]*v2[X] - v1[X]*v2[Z];
    result[Z] = v1[X]*v2[Y] - v1[Y]*v2[X];
}
EndOfRecord
len=`wc -c <  upat.c `
if [ $len !=  1480  ] ; then
echo error:  upat.c  was $len bytes long, should have been  1480
fi