/* Copyright (C) 1981-1999 Ken Turkowski. * * All rights reserved. * * Warranty Information * Even though I have reviewed this software, I make no warranty * or representation, either express or implied, with respect to this * software, its quality, accuracy, merchantability, or fitness for a * particular purpose. As a result, this software is provided "as is," * and you, its user, are assuming the entire risk as to its quality * and accuracy. * * This code may be used and freely distributed as long as it includes * this copyright notice and the above warranty information. */ #include #include /******************************************************************************** * typedefs ********************************************************************************/ typedef float vec3[3]; /******************************************************************************** * FOVToFocalLength * f = (h-1)/2 cot(t/2) ********************************************************************************/ static float FOVToFocalLength(float fov, long height) { return((height - 1) * 0.5F / tan(fov * 0.5F)); } /******************************************************************************** * FocalLengthToFOV * t = 2 atan((h-1)/(2f)) ********************************************************************************/ static float FocalLengthToFOV(float focalLength, long height) { return(2.0F * atan((height - 1) * 0.5F / focalLength)); } /******************************************************************************** * SetRotationX3x3 ********************************************************************************/ static void SetRotationX3x3(float rad, float *M) { register vec3* m = (vec3*)M; float s = sin(rad); float c = cos(rad); m[0][0] = 1; m[0][1] = 0; m[0][2] = 0; m[1][0] = 0; m[1][1] = c; m[1][2] = s; m[2][0] = 0; m[2][1] = -s; m[2][2] = c; } /******************************************************************************** * SetRotationY3x3 ********************************************************************************/ static void SetRotationY3x3(float rad, float *M) { register vec3* m = (vec3*)M; float s = sin(rad); float c = cos(rad); m[0][0] = c; m[0][1] = 0; m[0][2] = -s; m[1][0] = 0; m[1][1] = 1; m[1][2] = 0; m[2][0] = s; m[2][1] = 0; m[2][2] = c; } /******************************************************************************** * ScaleMatrixNxN() scales the (nRows x nCols) matrix by the given scale. ********************************************************************************/ static void ScaleMatrixNxM(register float scale, register float *m, unsigned long nRows, unsigned long nCols) { register unsigned long i = nRows * nCols; for ( ; i--; m++) *m *= scale; } /******************************************************************************** * LinearTransform ********************************************************************************/ static void LinearTransform( real *L, /* nRow x lCol */ real *R, /* lCol x rCol */ register real *P, /* nRow x rCol */ register unsigned long nRow, /* Number of rows in L and P */ unsigned long lCol, /* Number of columns in L and number of rows in R */ unsigned long rCol /* Number of columns in R and P */ ) { register real *ap, *bp; register unsigned long k, j, i; double_t sum; /* Extended precision for intermediate results */ for (i = nRow; i--; L += lCol) { /* Each row in L */ for (j = 0; j < rCol; j++) { /* Each column in R */ ap = L; /* Left of ith row of L */ bp = R + j; /* Top of jth column of R */ sum = 0.0; for (k = lCol; k--; bp += rCol) sum += *ap++ * (*bp); /* *P += L[i'][k'] * R[k'][j]; */ *P++ = sum; } } } /******************************************************************************** * ComputeProjectionMatrix3x3 * This computes the 3x3 matrix to project a 3D image to 2D. ********************************************************************************/ static void ComputeProjectionMatrix3x3( unsigned long width, /* Image width */ unsigned long height, /* Image height */ float dist, /* Distance to the image */ float copX, /* Center of projection, relative to center of image */ float copY, /* Center of projection, relative to center of image */ float *M /* Projection matrix, from 3D to 2D */ ) { float cx, cy; cx = (width - 1) * 0.5f + copX; cy = (height - 1) * 0.5f + copY; M[0*3+0] = -dist; M[0*3+1] = 0.0f; M[0*3+2] = 0.0f; M[1*3+0] = 0.0f; M[1*3+1] = dist; M[1*3+2] = 0.0f; M[2*3+0] = cx; M[2*3+1] = cy; M[2*3+2] = 1.0f; } /******************************************************************************** * ComputeImmersionMatrix3x3 * This computes the 3x3 matrix to immerse a 2D image into 3D. ********************************************************************************/ static void ComputeImmersionMatrix3x3( unsigned long width, /* Image width */ unsigned long height, /* Image height */ float dist, /* Distance to the image */ float copX, /* Center of projection, relative to center of image */ float copY, /* Center of projection, relative to center of image */ float *M /* Immersion matrix, from 2D to 3D */ ) { float cx, cy, dinv; /* Compute projection matrix parameters */ dinv = 1.0f / dist; cx = ((width - 1) * 0.5f + copX) * dinv; cy = ((height - 1) * 0.5f + copY) * dinv; M[0*3+0] = -dinv; M[0*3+1] = 0.0f; M[0*3+2] = 0.0f; M[1*3+0] = 0.0f; M[1*3+1] = dinv; M[1*3+2] = 0.0f; M[2*3+0] = cx; M[2*3+1] = -cy; M[2*3+2] = 1.0f; } /******************************************************************************** * PlaceImageViewRot3x3 * This computes the 3x3 matrix to place an image in 3-space. ********************************************************************************/ static void PlaceImageViewRot3x3( unsigned long width, /* Image width */ unsigned long height, /* Image height */ float dist, /* Distance to the image */ const float *Ri, /* Rotation matrix from 3D to 2D */ const float *cop, /* 2-vector center of projection, relative to center of image, in image coordinates (0,0 if NULL) */ float *M, /* The resultant matrix (if non-NULL) */ float *Mi /* and its inverse matrix (if non-NULL) */ ) { float C[3][3]; float cx, cy; if (cop != NULL) { cx = cop[0]; cy = cop[1]; } else { cx = 0; cy = 0; } if (Mi != NULL) { /* From 3D to 2D space */ ComputeProjectionMatrix3x3(width, height, dist, cx, cy, C[0]); LinearTransform(Ri, C[0], Mi, 3, 3, 3); } if (M != 0) { /* From 2D to 3D space */ float R[3][3]; R[0][0] = Ri[0]; R[0][1] = Ri[3]; R[0][2] = Ri[6]; /* Transpose (invert) orientation matrix */ R[1][0] = Ri[1]; R[1][1] = Ri[4]; R[1][2] = Ri[7]; R[2][0] = Ri[2]; R[2][1] = Ri[5]; R[2][2] = Ri[8]; ComputeImmersionMatrix3x3(width, height, dist, cx, cy, C[0]); LinearTransform(C[0], R[0], M, 3, 3, 3); } } /******************************************************************************** * PlaceImagePanTiltFOV3x3 * This computes the 3x3 matrix to place an image in 3-space. ********************************************************************************/ static void PlaceImagePanTiltFOV3x3( unsigned long width, /* Image width */ unsigned long height, /* Image height */ float pan, /* Pan */ float tilt, /* Tilt */ float fov, /* Vertical field of view */ const float *cop, /* 2-vector center of projection, relative to center of image, in image coordinates (0,0 if NULL) */ float *M, /* The resultant matrix (if non-NULL) */ float *Mi /* and its inverse matrix (if non-NULL) */ ) { float R[3][3], P[3][3], T[3][3]; float dist; /* Compute orientation frame */ SetRotationY3x3(-pan, P[0]); SetRotationX3x3(-tilt, T[0]); LinearTransform(P[0], T[0], R[0], 3, 3, 3); /* Compute focal length from fov */ dist = FOVToFocalLength(fov, height); PlaceImageViewRot3x3(width, height, dist, R[0], cop, M, Mi); } /******************************************************************************* * MakeQTMatrix * Convert a floating-point matrix to a QuickTime matrix. *******************************************************************************/ static void MakeQTMatrix(float F[3][3], MatrixRecord *Q) { float x[3][3], H[3][3], G[3][3]; float max, t; /* Convert from pixels-as-points to pixels-as-squares */ x[0][0] = 1; x[0][1] = 0; x[0][2] = 0; x[1][0] = 0; x[1][1] = 1; x[1][2] = 0; x[2][0] = -0.5; x[2][1] = -0.5; x[2][2] = 1; LinearTransform(x[0], F[0], H[0], 3, 3, 3); x[2][0] = 0.5; x[2][1] = 0.5; LinearTransform(H[0], x[0], G[0], 3, 3, 3); /* Convert from floating-point to fixed-point */ max = fabs(G[0][2]); if (max < (t = fabs(G[1][2]))) max = t; if (max < (t = fabs(G[2][2]))) max = t; if (max > 1) ScaleMatrixNxM(1/max, G[0], 3, 3); Q->matrix[0][0] = G[0][0] * 65536.0f; Q->matrix[0][1] = G[0][1] * 65536.0f; Q->matrix[0][2] = G[0][2] * 1073741824.0f; Q->matrix[1][0] = G[1][0] * 65536.0f; Q->matrix[1][1] = G[1][1] * 65536.0f; Q->matrix[1][2] = G[1][2] * 1073741824.0f; Q->matrix[2][0] = G[2][0] * 65536.0f; Q->matrix[2][1] = G[2][1] * 65536.0f; Q->matrix[2][2] = G[2][2] * 1073741824.0f; } /******************************************************************************** * ImmerseImageInQTVR * * Generates a floating-point 3x3 matrix to embed an image in QTVR. ********************************************************************************/ void ImmerseImageInQTVR( GWorldPtr imageGW, float imagePan, float imageTilt, float imageFOV, GWorldPtr viewGW, float viewPan, float viewTilt, float viewFOV, float *M ) { Rect r; unsigned long width, height; float V[3][3], P[3][3]; /* Compute image placement matrix */ r = imageGW->portRect; width = r.right - r.left; height = r.bottom - r.top; PlaceImagePanTiltFOV3x3(width, height, imagePan, imageTilt, imageFOV, NULL, P[0], NULL); /* Compute view matrix */ r = viewGW->portRect; width = r.right - r.left; height = r.bottom - r.top; PlaceImagePanTiltFOV3x3(width, height, viewPan, viewTilt, viewFOV, NULL, NULL, V[0]); /* Concatenate */ LinearTransform(P[0], V[0], M, 3, 3, 3); } /******************************************************************************** * ImmerseQTImageInQTVR * * Generates a QuickTime-style 3x3 matrix to embed an image in QTVR. ********************************************************************************/ void ImmerseQTImageInQTVR( GWorldPtr imageGW, float imagePan, float imageTilt, float imageFOV, GWorldPtr viewGW, float viewPan, float viewTilt, float viewFOV, MatrixRecord *Q ) { float M[3][3]; ImmerseImageInQTVR(imageGW, imagePan, imageTilt, imageFOV, viewGW, viewPan, viewTilt, viewFOV, M[0]); MakeQTMatrix(M, Q); }