1 /*
2  * Copyright (C) 1997-2005, R3vis Corporation.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
17  * USA, or visit http://www.gnu.org/copyleft/lgpl.html.
18  *
19  * Original Contributor:
20  *   Wes Bethel, R3vis Corporation, Marin County, California
21  * Additional Contributor(s):
22  *
23  * The OpenRM project is located at http://openrm.sourceforge.net/.
24  */
25 /*
26  * $Id: rmmatrix.c,v 1.7 2005/07/04 16:49:38 wes Exp $
27  * Version: $Name: OpenRM-1-6-0-2-RC2 $
28  * $Revision: 1.7 $
29  * $Log: rmmatrix.c,v $
30  * Revision 1.7  2005/07/04 16:49:38  wes
31  * Minor change to private_rmNewWCfromDCOffset.
32  *
33  * Revision 1.6  2005/06/26 18:52:35  wes
34  * Streamlined computation of viewport matrix: created single routine that
35  * computes the viewport matrix, invoke that routine where needed rather
36  * than having duplicate copies of viewport matrix computation code.
37  *
38  * Revision 1.5  2005/02/19 16:22:50  wes
39  * Distro sync and consolidation.
40  *
41  * Revision 1.4  2005/01/23 17:04:03  wes
42  * Copyright update to 2005.
43  *
44  * Revision 1.3  2004/01/16 16:46:09  wes
45  * Updated copyright line for 2004.
46  *
47  * Revision 1.2  2003/02/02 02:07:15  wes
48  * Updated copyright to 2003.
49  *
50  * Revision 1.1.1.1  2003/01/28 02:15:23  wes
51  * Manual rebuild of rm150 repository.
52  *
53  * Revision 1.10  2003/01/16 22:21:17  wes
54  * Updated all source files to reflect new organization of header files:
55  * all header files formerly located in include/rmaux, include/rmi, include/rmv
56  * are now located in include/rm.
57  *
58  * Revision 1.9  2002/04/30 19:32:22  wes
59  * Updated copyright dates.
60  *
61  * Revision 1.8  2001/10/15 00:08:40  wes
62  * Minor change in private_rmDC2fromWC() needed to display image
63  * data. Problem was that the modelview and projection matrices
64  * definitions changed slightly in the RMstate structure during
65  * the multistage rendering work.
66  *
67  * Revision 1.7  2001/03/31 17:12:38  wes
68  * v1.4.0-alpha-2 checkin.
69  *
70  * Revision 1.6  2000/12/03 22:35:38  wes
71  * Mods for thread safety.
72  *
73  * Revision 1.5  2000/08/19 16:09:06  wes
74  * Fixed logic bug in rmMatrixGetValue().
75  *
76  * Revision 1.4  2000/07/05 04:59:50  wes
77  * Fixed index range check error in rmMatrixGetValue.
78  *
79  * Revision 1.3  2000/04/20 16:29:47  wes
80  * Documentation additions/enhancements, some code rearragement.
81  *
82  * Revision 1.2  2000/02/29 23:43:53  wes
83  * Compile warning cleanups.
84  *
85  * Revision 1.1.1.1  2000/02/28 21:29:40  wes
86  * OpenRM 1.2 Checkin
87  *
88  * Revision 1.1.1.1  2000/02/28 17:18:48  wes
89  * Initial entry - pre-RM120 release, source base for OpenRM 1.2.
90  *
91  */
92 
93 #include <rm/rm.h>
94 #include "rmprivat.h"
95 
96 /*
97  * ----------------------------------------------------
98  * @Name rmMatrixNew
99  @pstart
100  RMmatrix * rmMatrixNew (void)
101  @pend
102 
103  @astart
104  No arguments.
105  @aend
106 
107  @dstart
108 
109  Creates a new RMmatrix matrix, returning a handle to the new object
110  to the caller upon success, or NULL upon failure.  Use rmMatrixDelete
111  to free an RMmatrix object.
112 
113  The RMmatrix object is a 4x4 array of floating point values.
114  Applications have two alternative approaches that may be used to set
115  the individual values in the RMmatrix: either by using
116  rmMatrixSetValue()/rmMatrixGetValue(), or through direct C-struct
117  access.
118 
119  The RMmatrix object consists of a single 4x4 array of floating point
120  values. The array is indexed with A[row][column], so that A[3][0]
121  refers to an entry that is in the third row, and zero'th column. That
122  particular entry controls translations along the X axis; in RM,
123  matrix multiplies occur from the left.
124 
125  The general form of RMmatrices is
126  <pre>
127        |  1  0  0  0  |       |  Sx 0  0  0  |
128        |  0  1  0  0  |       |  0  Sy 0  0  |
129   T =  |  0  0  1  0  |   S = |  0  0  Sz 0  |
130        |  Tx Ty Tz 1  |       |  0  0  0  1  |
131  </pre>
132 
133  Where T and S are the translation and scale matrices, respectively.
134 
135  Access to a specific matrix entity using the C-struct interface would
136  be performed as follows: to access the Tx, or X-axis translation in T
137  above, Tx = matrix->m[3][0], where "matrix" is a handle to the
138  RMmatrix object. The "m" entity is the single matrix struct inside
139  the RMmatrix object.
140 
141  Access to the same element through the API would be:
142  Tx = rmMatrixGetValue(matrix,3,0).
143 
144  @dend
145  * ----------------------------------------------------
146  */
147 RMmatrix *
rmMatrixNew(void)148 rmMatrixNew (void)
149 {
150     RMmatrix *m = (RMmatrix *)malloc(sizeof(RMmatrix));
151     rmMatrixIdentity(m);
152     return(m);
153 }
154 
155 
156 /*
157  * ----------------------------------------------------
158  * @Name rmMatrixCopy
159  @pstart
160  RMenum rmMatrixCopy (RMmatrix *dst,
161 	              const RMmatrix *src)
162  @pend
163 
164  @astart
165  RMmatrix *dst - a handle to the "copy to" RMmatrix (modified).
166 
167  const RMmatrix *src - a handle to the "copy from" RMmatrix (input).
168  @aend
169 
170  @dstart
171 
172  Copies the contents of one RMmatrix object to another. Returns
173  RM_CHILL upon success, or RM_WHACKED upon failure.
174 
175  @dend
176  * ----------------------------------------------------
177  */
178 RMenum
rmMatrixCopy(RMmatrix * d,const RMmatrix * s)179 rmMatrixCopy (RMmatrix *d,
180 	      const RMmatrix *s)
181 {
182     if ((RM_ASSERT(d, "rmMatrixCopy() error: the dest RMmatrix pointer is NULL.") == RM_WHACKED) ||
183 	(RM_ASSERT(s, "rmMatrixCopy() error: the src RMmatrix pointer is NULL.") == RM_WHACKED))
184 	return (RM_WHACKED);
185 
186     memcpy((char *)d, (char *)s, sizeof(RMmatrix));
187     return(RM_CHILL);
188 }
189 
190 
191 /*
192  * ----------------------------------------------------
193  * @Name rmMatrixDelete
194  @pstart
195  RMenum rmMatrixDelete (RMmatrix *toDelete)
196  @pend
197 
198  @astart
199  RMmatrix *toDelete - a handle to an RMmatrix object to delete
200     (modified).
201  @aend
202 
203  @dstart
204 
205  Releases resources associated with an RMmatrix object. Returns
206  RM_CHILL upon success, or RM_WHACKED upon failure.
207 
208  @dend
209  * ----------------------------------------------------
210  */
211 RMenum
rmMatrixDelete(RMmatrix * m)212 rmMatrixDelete (RMmatrix *m)
213 {
214     if (RM_ASSERT(m, "rmMatrixDelete() error: the input RMmatrix pointer is NULL.") == RM_WHACKED)
215 	return(RM_WHACKED);
216 
217     free(m);
218     return(RM_CHILL);
219 }
220 
221 
222 /*
223  * ----------------------------------------------------
224  * @Name rmMatrixIdentity
225  @pstart
226  RMenum rmMatrixIdentity (RMmatrix *toModify)
227  @pend
228 
229  @astart
230  RMmatrix *toModify - a handle to an RMmatrix object (modified).
231  @aend
232 
233  @dstart
234 
235  This routine will set the RMmatrix "toModify" to contain the Identity
236  matrix. Returns RM_CHILL upon success, or RM_WHACKED upon failure.
237 
238  @dend
239  * ----------------------------------------------------
240  */
241 RMenum
rmMatrixIdentity(RMmatrix * m)242 rmMatrixIdentity (RMmatrix *m)
243 {
244     int i, j;
245 
246     if (RM_ASSERT(m, "rmMatrixIdentity() error: the input RMmatrix is NULL.") == RM_WHACKED)
247 	return(RM_WHACKED);
248 
249     for (j = 0; j < 4; j++)
250 	for (i = 0; i < 4; i++)
251 	    m->m[i][j] = (i == j) ? 1.0 : 0.0;
252 
253     return(RM_CHILL);
254 }
255 
256 
257 /*
258  * ----------------------------------------------------
259  * @Name rmMatrixSetValue
260  @pstart
261  RMenum rmMatrixSetValue (RMmatrix *toModify,
262 		          int row,
263 			  int col,
264 			  float newValue)
265  @pend
266 
267  @astart
268  RMmatrix *toModify - a handle to an RMmatrix object (modified).
269 
270  int row, col - integer values, treated as indices. Must both be in
271     the range 0..3, otherwise an error condition results.
272 
273  float newValue - a floating point value to place into the RMmatrix
274     object (input).
275  @aend
276 
277  @dstart
278 
279  Assigns a floating point value (newValue) to a single entry in the
280  4x4 matrix contained in the RMmatrix object, returning RM_CHILL upon
281  success or RM_WHACKED upon failure.
282 
283  @dend
284  * ----------------------------------------------------
285  */
286 RMenum
rmMatrixSetValue(RMmatrix * m,int row,int col,float newValue)287 rmMatrixSetValue (RMmatrix *m,
288 		  int row,
289 		  int col,
290 		  float newValue)
291 {
292     RMenum rstat = RM_CHILL;
293 
294     if (RM_ASSERT(m, "rmMatrixSetValue() error: the input RMmatrix is NULL.") == RM_WHACKED)
295 	return(RM_WHACKED);
296 
297     if (!(row < 0 || row > 3 || col < 0 || col > 3))
298     {
299 	m->m[row][col] = newValue;
300     }
301     else
302     {
303 	rmWarning("rmMatrixSetValue warning: either the row or column input parameters are out of range.");
304         rstat = RM_WHACKED;
305     }
306     return(rstat);
307 }
308 
309 
310 /*
311  * ----------------------------------------------------
312  * @Name rmMatrixGetValue
313  @pstart
314  float rmMatrixGetValue (const RMmatrix *toQuery,
315 		         int row,
316 			 int col)
317  @pend
318 
319 
320  @astart const RMmatrix *toQuery - a handle to an RMmatrix object to
321     query (input).
322 
323  int row, col - integer values assumed to be indices into the 4x4
324     matrix contained within the RMmatrix object. Must be in the range
325     0..3 else an error condition is detected.
326  @aend
327 
328  @dstart
329 
330  Use this routine to obtain the value of a single entry from a 4x4
331  matrix within an RMmatrix object. Returns the entry at [row][col]
332  from the matrix upon success, otherwise an error message is issued
333  and 0.0 is returned.
334 
335  @dend
336  * ----------------------------------------------------
337  */
338 float
rmMatrixGetValue(const RMmatrix * m,int row,int col)339 rmMatrixGetValue (const RMmatrix *m,
340 		  int row,
341 		  int col)
342 {
343     float rval;
344 
345     if (RM_ASSERT(m, "rmMatrixGetValue() error: the input RMmatrix is NULL.") == RM_WHACKED)
346 	return(0.0F);
347 
348     if (!((row < 0) || (row > 3) || (col < 0) || (col > 3)))
349     {
350 	rval = m->m[row][col];
351     }
352     else
353     {
354 	rval = 0.0F;
355 	rmWarning("rmMatrixGetValue warning: either the row or column input parameters are out of range.");
356     }
357     return(rval);
358 }
359 
360 
361 /*
362  * ----------------------------------------------------
363  * @Name rmMatrixTranspose
364  @pstart
365  RMenum rmMatrixTranspose (const RMmatrix *src,
366 		           RMmatrix *dst)
367  @pend
368 
369  @astart
370  const RMmatrix *src - a handle to an RMmatrix object (input).
371 
372  RMmatrix *dst - a handle to an RMmatrix object (modified).
373  @aend
374 
375  @dstart
376 
377  Transposes the input matrix, copying the results back into the "dst"
378  RMmatrix object. Returns RM_CHILL upon success, otherwise RM_WHACKED
379  is returned.
380 
381  This routine is written such that "src" and "dst" may be the same
382  RMmatrix object.
383 
384  @dend
385  * ----------------------------------------------------
386  */
387 RMenum
rmMatrixTranspose(const RMmatrix * s,RMmatrix * d)388 rmMatrixTranspose (const RMmatrix *s,
389 		   RMmatrix *d)
390 {
391     int      i, j;
392     RMmatrix t;
393 
394     if ((RM_ASSERT(s, "rmMatrixTranspose() error: the input RMmatrix object is NULL") == RM_WHACKED) ||
395 	(RM_ASSERT(d, "rmMatrixTranspose() error: the dest RMmatrix object is NULL")) == RM_WHACKED)
396 	return(RM_WHACKED);
397 
398     for (j = 0; j < 4; j++)
399       for (i = 0; i < 4; i++)
400         t.m[j][i] = s->m[i][j];
401     memcpy(d, &t, sizeof(RMmatrix));
402 
403     return(RM_CHILL);
404 }
405 
406 
407 /*
408  * ----------------------------------------------------
409  * @Name rmMatrixMultiply
410  @pstart
411  RMenum rmMatrixMultiply (const RMmatrix *srcA,
412 		          const RMmatrix *srcB,
413 			  RMmatrix *dst)
414  @pend
415 
416  @astart
417  const RMmatrix *srcA, *srcB - handles to RMmatrix objects (input).
418 
419  RMmatrix *dst - a handle to an RMmatrix object (modified).
420  @aend
421 
422  @dstart
423 
424  Computes the matrix product srcA * srcB, placing the result into
425  "dst".  Returns RM_CHILL upon success, or RM_WHACKED upon failure.
426 
427  Internally, rmMatrixMultiply is written such that "dst" may be the
428  same RMmatrix object as either srcA or srcB.
429 
430  @dend
431  * ----------------------------------------------------
432  */
433 RMenum
rmMatrixMultiply(const RMmatrix * a,const RMmatrix * b,RMmatrix * c)434 rmMatrixMultiply (const RMmatrix *a,
435 		  const RMmatrix *b,
436 		  RMmatrix *c)
437  /* take product of a and b, put result in c */
438 {
439     int      row, col;
440     RMmatrix tmp;
441 
442     if ((RM_ASSERT(a, "rmMatrixMultiply() error: the input A RMmatrix is NULL") == RM_WHACKED) ||
443 	(RM_ASSERT(b, "rmMatrixMultiply() error: the input B RMmatrix is NULL") == RM_WHACKED) ||
444 	(RM_ASSERT(c, "rmMatrixMultiply() error: the destination RMmatrix is NULL") == RM_WHACKED))
445 	return(RM_WHACKED);
446 
447     for (row = 0; row < 4; row++)
448     {
449 	for (col = 0; col < 4; col++)
450 	    tmp.m[row][col] = (a->m[row][0] * b->m[0][col]) + (a->m[row][1] * b->m[1][col]) + (a->m[row][2] * b->m[2][col]) + (a->m[row][3] * b->m[3][col]);
451     }
452     for (row = 0; row < 4; row++)
453 	for (col = 0; col < 4; col++)
454 	    c->m[row][col] = tmp.m[row][col];
455 
456     return(RM_CHILL);
457 }
458 
459 
460 /*
461  * ----------------------------------------------------
462  * @Name rmMatrixInverse
463  @pstart
464  RMenum rmMatrixInverse (const RMmatrix *src,
465 		         RMmatrix *dst)
466  @pend
467 
468  @astart
469  const RMmatrix *src - a handle to an RMmatrix object (input).
470 
471  RMmatrix *dst - a handle to an RMmatrix object (result).
472  @aend
473 
474  @dstart
475 
476  Will compute the inverse of the 4x4 matrix "src," placing the result
477  into "dst". Returns RM_CHILL upon success, or RM_WHACKED upon
478  failure.
479 
480  Matrix inversion is performed using Gaussian elimination using a
481  customized version of SGEFA from the Linpack distribution. If the
482  input matrix is singular, an error is reported with rmWarning(), and
483  RM_WHACKED is returned.
484 
485  @dend
486  * ----------------------------------------------------
487  */
488 RMenum
rmMatrixInverse(const RMmatrix * in,RMmatrix * out)489 rmMatrixInverse (const RMmatrix *in,
490 		 RMmatrix *out)
491 {
492     /* the following is a software interface to some routines from LINPACK which are called to compute a matrix inverse */
493     int    lda, n, ipvt[5], info, job;
494     int   *ip;
495     float  m[16];
496     float  work[4*4], det[4][2];
497     float *a;
498 
499     /* external LINPACK-hack */
500     extern void sgefa (float *a, int *lda, int *n, int *ipvt, int *info);
501     extern void sgedi (float *a, int *lda, int *n, int *ipvt, float *det, float *work, int *job);
502 
503     if ((RM_ASSERT(in, "rmMatrixInverse() error: the src RMmatrix pointer is NULL") == RM_WHACKED) ||
504 	(RM_ASSERT(out, "rmMatrixInverse() error: the dst RMmatrix pointer is NULL") == RM_WHACKED))
505 	return(RM_WHACKED);
506 
507     memcpy((char *)m, (char *)in, sizeof(RMmatrix));
508     job = 1;
509     lda = 4;
510     n = 4;
511     a = m;
512     ip = ipvt;
513 
514     sgefa(a, &lda, &n, ip, &info);
515 
516     if (info == 0)
517 	sgedi(a, &lda, &n, ip, &(det[0][0]), work, &job);
518     else
519     {
520 	rmWarning("rmMatrixInverse - the input matrix is singular. ");
521 	return(RM_WHACKED);
522     }
523     memcpy((char *)out, (char *)m, sizeof(RMmatrix));
524 
525     return(RM_CHILL);
526 }
527 
528 
529 /*
530  * ----------------------------------------------------
531  * @Name rmPoint4MatrixTransform
532  @pstart
533  RMenum rmPoint4MatrixTransform (const float *src,
534 			         const RMmatrix *matrix,
535 				 float *dst)
536  @pend
537 
538  @astart
539  const float *src - a handle to an array of floats of length 4
540     (input).
541 
542  const RMmatrix *matrix - a handle to an RMmatrix object.
543 
544  float *dst - a handle to an array of floats of length 4 (ouput).
545  @aend
546 
547  @dstart
548 
549  rmPoint4MatrixTransform is one of a family of routines that perform
550  vector-matrix multiplication. This routine multiplies the vector
551  "src" by the matrix, placing the result into vector "dst." It is
552  assumed that both "src" and "dst" are floating point arrays of length
553  4.  Returns RM_CHILL upon success, or RM_WHACKED upon failure.
554 
555  This routine is written such that "src" and "dst" may be the same
556  array from the caller's perspective.
557 
558  This routine implements the following operation:
559  <pre>
560 
561                  |  a  b  c  d  |
562                  |  e  f  g  h  |
563  [s0 s1 s2 s3] * |  i  j  k  l  | = [d0 d1 d2 d3]
564 		 |  m  n  o  p  |
565 
566  </pre>
567 
568  The "src" vector multiplies the matrix from the left (pre
569  multiplication) such that each element of "dst" is the dot product of
570  "src" with a column from the matrix.
571 
572  See also rmPointMatrixTransform.
573 
574  @dend
575  * ----------------------------------------------------
576  */
577 RMenum
rmPoint4MatrixTransform(const float * v,const RMmatrix * m,float * d)578 rmPoint4MatrixTransform (const float *v,
579 			 const RMmatrix *m,
580 			 float *d)
581 {
582     float t[4];
583 
584     if ((RM_ASSERT(v, "rmPoint4MatrixTransform() error: the input src vector is NULL") == RM_WHACKED) ||
585 	(RM_ASSERT(m, "rmPoint4MatrixTransform() error: the input RMmatrix object is NULL") == RM_WHACKED) ||
586 	(RM_ASSERT(d, "rmPoint4MatrixTransform() error: the result dst vector is NULL") == RM_WHACKED))
587 	return(RM_WHACKED);
588 
589     t[0] = (v[0] * m->m[0][0]) + (v[1] * m->m[1][0]) + (v[2] * m->m[2][0]) + (v[3] * m->m[3][0]);
590     t[1] = (v[0] * m->m[0][1]) + (v[1] * m->m[1][1]) + (v[2] * m->m[2][1]) + (v[3] * m->m[3][1]);
591     t[2] = (v[0] * m->m[0][2]) + (v[1] * m->m[1][2]) + (v[2] * m->m[2][2]) + (v[3] * m->m[3][2]);
592     t[3] = (v[0] * m->m[0][3]) + (v[1] * m->m[1][3]) + (v[2] * m->m[2][3]) + (v[3] * m->m[3][3]);
593 
594     memcpy(d, t, sizeof(float) * 4);
595 
596     return(RM_CHILL);
597 }
598 
599 
600 /*
601  * ----------------------------------------------------
602  * @Name rmPointMatrixTransform
603  @pstart
604  RMenum rmPointMatrixTransform (const RMvertex3D *src,
605 			        const RMmatrix *matrix,
606 			        RMvertex3D *dst)
607  @pend
608 
609  @astart
610  const RMvertex3D *src - a handle to an RMvertex3D object (input).
611 
612  const RMmatrix *matrix - a handle to an RMmatrix object.
613 
614  RMvertex3D *dst - a handle to an RMvertex3D (ouput).
615  @aend
616 
617  @dstart
618 
619  rmPointMatrixTransform is one of a family of routines that perform
620  vector-matrix multiplication. This routine multiplies the RMvertex3D
621  "src" by the matrix, placing the result into RMvertex3D "dst."
622 
623  This routine differs from rmPoint4MatrixTransform in that it is
624  assumed that the w-coordinate of both "src" and "dst" is 1.0, and
625  that the resulting w-coordinate is not available to the caller upon
626  return.
627 
628  Returns RM_CHILL upon success, or RM_WHACKED upon failure.
629 
630  This routine is written such that "src" and "dst" may be the same
631  array from the caller's perspective.
632 
633  This routine implements the following operation:
634  <pre>
635 
636                  |  a  b  c  d  |
637                  |  e  f  g  h  |
638  [s0 s1 s2 1]  * |  i  j  k  l  | = [d0 d1 d2 n/a]
639 		 |  m  n  o  p  |
640 
641  </pre>
642 
643  The "src" vector multiplies the matrix from the left (pre
644  multiplication) such that each element of "dst" is the dot product of
645  "src" with a column from the matrix.
646 
647  See also rmPoint4MatrixTransform.
648 
649  @dend
650  * ----------------------------------------------------
651  */
652 RMenum
rmPointMatrixTransform(const RMvertex3D * s,const RMmatrix * m,RMvertex3D * d)653 rmPointMatrixTransform (const RMvertex3D *s,
654 		        const RMmatrix *m,
655 		        RMvertex3D *d)
656 {
657     RMvertex3D t;
658 
659     if ((RM_ASSERT(s, "rmPointMatrixTransform error: the input S RMvertex3D pointer is NULL") == RM_WHACKED) ||
660 	(RM_ASSERT(m, "rmPointMatrixTransform error: the input RMmatrix pointer is NULL") == RM_WHACKED) ||
661 	(RM_ASSERT(d, "rmPointMatrixTransform error: the destination RMvertex3D pointer is NULL") == RM_WHACKED))
662 	return(RM_WHACKED);
663 
664     t.x = (s->x * m->m[0][0]) + (s->y * m->m[1][0]) + (s->z * m->m[2][0]) + m->m[3][0];
665     t.y = (s->x * m->m[0][1]) + (s->y * m->m[1][1]) + (s->z * m->m[2][1]) + m->m[3][1];
666     t.z = (s->x * m->m[0][2]) + (s->y * m->m[1][2]) + (s->z * m->m[2][2]) + m->m[3][2];
667 
668     VCOPY(&t, d);
669 
670     return(RM_CHILL);
671 }
672 
673 
674 /*
675  * ----------------------------------------------------
676  * @Name rmPrintMatrix
677  @pstart
678  RMenum rmPrintMatrix (const RMmatrix *toPrint)
679  @pend
680 
681  @astart
682  const RMmatrix *toPrint - a handle to an RMmatrix that will be
683     printed (input).
684  @aend
685 
686  @dstart
687 
688  Prints the contents of an RMmatrix object using C-printf's. Returns
689  RM_CHILL upon success or RM_WHACKED upon failure.
690 
691  @dend
692  * ----------------------------------------------------
693  */
694 RMenum
rmPrintMatrix(const RMmatrix * m)695 rmPrintMatrix (const RMmatrix *m)
696 {
697     int i, j;
698 
699     if (RM_ASSERT(m, "rmPrintMatrix() error: the input RMmatrix object is NULL") == RM_WHACKED)
700 	return(RM_WHACKED);
701 
702     for (j = 0; j < 4; j++)
703     {
704 	for (i = 0; i < 4; i++)
705 	    printf("\t%g", m->m[j][i]);
706 	printf("\n");
707     }
708     return(RM_CHILL);
709 }
710 
711 
712 /*
713  * ----------------------------------------------------
714  * @Name rmPointMin
715  @pstart
716  RMenum rmPointMin (const float *input,
717 	            int count,
718 		    int vdims,
719 		    int stride,
720 		    RMvertex3D *minReturn)
721  @pend
722 
723  @astart
724  const float *input - a handle to an array of floating point values
725     (input).
726 
727  int count - an integer value indicating the number of "vertices" that
728     will be processed (input).
729 
730  int vdims - an integer value indicating the cardinality, or number of
731     dimensions, of each vertex. Should be either 1, 2 or 3 (input).
732 
733  int stride - an integer value specifying the stride length in floats
734     from one vertex to the next (input).
735 
736  RMvertex3D *minReturn - a handle to an RMvertex3D object (modified).
737  @aend
738 
739  @dstart
740 
741  Finds the minimum point, or vertex value, from a flat array of
742  floating point values. Upon success, the minimum vertex value is
743  copied into the caller-supplied "minReturn," and RM_CHILL is
744  returned. Otherwise, RM_WHACKED is returned upon failure.
745 
746  The parameter "vdims" defines the number of coordinates in each
747  vertex. Use a value of 2 to specify two-dimensional vertices (x and y
748  values), or 3 for three-dimensional vertices (x, y and z values).  A
749  value of 1 is also acceptable.
750 
751  The parameter "count" specifies how many such vertices are present in
752  the input array. Note that this routine assumes that "input" is just
753  a flat array of floats, and will be logically interpreted as groups
754  of vertices, each of which consists of "vdims" floats per vertex. The
755  coordinates of each vertex are assumed to be contiguously located.
756 
757  The parameter "stride" tells how many floating point values to skip
758  from one vertex to the next.  If we assume that "input" consists of a
759  flat array of RMvertex3D objects (cast to float * when calling this
760  routine), the best way to compute "stride" is:
761 
762  <pre>
763        stride = sizeof(RMvertex3D)/sizeof(float)
764  </pre>
765 
766  The reason for "stride" is because some platforms force padding of
767  C-structures to the nearest 8-byte boundary. Hence,
768  sizeof(RMvertex3D) is not always equal to sizeof(float)*3.
769 
770  @dend
771  * ----------------------------------------------------
772  */
773 RMenum
rmPointMin(const float * input,int count,int vdims,int stride,RMvertex3D * ret)774 rmPointMin (const float *input,
775 	    int count,
776 	    int vdims,
777 	    int stride,
778 	    RMvertex3D *ret)
779 {
780     register int          i;
781     register float        mx, my, mz;
782     register float        v1;
783     register const float *v;
784 
785     mx = my = mz = RM_MAXFLOAT;
786 
787     if ((RM_ASSERT(input, "rmPointMin error: the input floating point array is NULL") == RM_WHACKED) ||
788 	(RM_ASSERT(ret, "rmPointMin error: the return RMvertex3D * is NULL.") == RM_WHACKED))
789 	return(RM_WHACKED);
790 
791     stride = stride / sizeof(float);
792 
793     v = input;
794 
795     for (i = 0; i < count; i++, v += stride)
796     {
797         /* x-component */
798         v1 = v[0];
799 	if (v1 < mx)
800 	    mx = v1;
801 
802 	if (vdims > 1)
803 	{
804 	    /* y-component */
805 	  v1 = v[1];
806 	  if (v1 < my)
807 	    my = v1;
808 	}
809 	else
810 	    my = 0.0;
811 
812 	if (vdims > 2)
813 	{
814 	    /* z-component */
815 	  v1 = v[2];
816 	  if (v1 < mz)
817 	      mz = v1;
818 	}
819 	else
820 	    mz = 0.0;
821     }
822     ret->x = mx;
823     ret->y = my;
824     ret->z = mz;
825 
826     return(RM_CHILL);
827 }
828 
829 
830 /*
831  * ----------------------------------------------------
832  * @Name rmPointMax
833  @pstart
834  RMenum rmPointMax (const float *input,
835 	            int count,
836 		    int vdims,
837 		    int stride,
838 		    RMvertex3D *maxReturn)
839  @pend
840 
841  @astart
842  const float *input - a handle to an array of floating point values
843     (input).
844 
845  int count - an integer value indicating the number of "vertices" that
846     will be processed (input).
847 
848  int vdims - an integer value indicating the cardinality, or number of
849     dimensions, of each vertex. Should be either 1, 2 or 3 (input).
850 
851  int stride - an integer value specifying the stride length in floats
852     from one vertex to the next (input).
853 
854  RMvertex3D *maxReturn - a handle to an RMvertex3D object (modified).
855  @aend
856 
857  @dstart
858 
859  Finds the maximum point, or vertex value, from a flat array of
860  floating point values. Upon success, the maximum vertex value is
861  copied into the caller-supplied "maxReturn," and RM_CHILL is
862  returned. Otherwise, RM_WHACKED is returned upon failure.
863 
864  The parameter "vdims" defines the number of coordinates in each
865  vertex. Use a value of 2 to specify two-dimensional vertices (x and y
866  values), or 3 for three-dimensional vertices (x, y and z values).  A
867  value of 1 is also acceptable.
868 
869  The parameter "count" specifies how many such vertices are present in
870  the input array. Note that this routine assumes that "input" is just
871  a flat array of floats, and will be logically interpreted as groups
872  of vertices, each of which consists of "vdims" floats per vertex. The
873  coordinates of each vertex are assumed to be contiguously located.
874 
875  The parameter "stride" tells how many floating point values to skip
876  from one vertex to the next.  If we assume that "input" consists of a
877  flat array of RMvertex3D objects (cast to float * when calling this
878  routine), the best way to compute "stride" is:
879 
880  <pre>
881        stride = sizeof(RMvertex3D)/sizeof(float)
882  </pre>
883 
884  The reason for "stride" is because some platforms force padding of
885  C-structures to the nearest 8-byte boundary. Hence,
886  sizeof(RMvertex3D) is not always equal to sizeof(float)*3.
887 
888  @dend
889  * ----------------------------------------------------
890 */
891 RMenum
rmPointMax(const float * input,int count,int vdims,int stride,RMvertex3D * ret)892 rmPointMax (const float *input,
893 	    int count,
894 	    int vdims,
895 	    int stride,
896 	    RMvertex3D *ret)
897 {
898     register int          i;
899     register float        mx, my, mz;
900     register float        v1;
901     register const float *v;
902 
903     mx = my = mz = -1. * RM_MAXFLOAT;
904 
905     if ((RM_ASSERT(input, "rmPointMax error: the input floating point array is NULL") == RM_WHACKED) ||
906 	(RM_ASSERT(ret, "rmPointMax error: the return RMvertex3D * is NULL.") == RM_WHACKED))
907 	return(RM_WHACKED);
908 
909     stride = stride/sizeof(float);
910 
911     /* assume the RMvertex3D structure has 3 floats, and the RMvertex2d structure has 2 floats */
912     v = input;
913 
914     for (i = 0; i < count; i++, v += stride)
915     {
916         /* x-component */
917         v1 = v[0];
918 	if (v1 > mx)
919 	    mx = v1;
920 
921 	if (vdims > 1)
922 	{
923 	  /* y-component */
924 	  v1 = v[1];
925 	  if (v1 > my)
926 	    my = v1;
927 	}
928 	else
929 	    my = 0.0;
930 
931 	if (vdims > 2)
932 	{
933 	  /* z-component */
934 	  v1 = v[2];
935 	  if (v1 > mz)
936 	    mz = v1;
937 	}
938 	else
939 	    mz = 0.0;
940     }
941     ret->x = mx;
942     ret->y = my;
943     ret->z = mz;
944 
945     return(RM_CHILL);
946 }
947 
948 
949 /*
950  * ----------------------------------------------------
951  * @Name rmPointMinMax
952  @pstart
953  RMenum rmPointMinMax (const float *input,
954 	               int count,
955 		       int vdims,
956 		       int stride,
957 		       RMvertex3D *minReturn,
958 		       RMvertex3D *maxReturn)
959  @pend
960 
961 
962  @astart
963  const float *input - a handle to an array of floating point values
964     (input).
965 
966  int count - an integer value indicating the number of "vertices" that
967     will be processed (input).
968 
969  int vdims - an integer value indicating the cardinality, or number of
970     dimensions, of each vertex. Should be either 1, 2 or 3 (input).
971 
972  int stride - an integer value specifying the stride length in floats
973     from one vertex to the next (input).
974 
975  RMvertex3D *minReturn, *maxReturn - handles to RMvertex3D objects
976     (modified).
977  @aend
978 
979  @dstart
980 
981  Finds the minimum and maximum points, or vertex values, from a flat
982  array of floating point values. Upon success, the minimum and maximum
983  vertex values are copied into the caller-supplied "minReturn" and
984  "maxReturn," respectively, and RM_CHILL is returned. Otherwise,
985  RM_WHACKED is returned upon failure.
986 
987  The parameter "vdims" defines the number of coordinates in each
988  vertex. Use a value of 2 to specify two-dimensional vertices (x and y
989  values), or 3 for three-dimensional vertices (x, y and z values).  A
990  value of 1 is also acceptable.
991 
992  The parameter "count" specifies how many such vertices are present in
993  the input array. Note that this routine assumes that "input" is just
994  a flat array of floats, and will be logically interpreted as groups
995  of vertices, each of which consists of "vdims" floats per vertex. The
996  coordinates of each vertex are assumed to be contiguously located.
997 
998  The parameter "stride" tells how many floating point values to skip
999  from one vertex to the next.  If we assume that "input" consists of a
1000  flat array of RMvertex3D objects (cast to float * when calling this
1001  routine), the best way to compute "stride" is:
1002 
1003  <pre>
1004        stride = sizeof(RMvertex3D)/sizeof(float)
1005  </pre>
1006 
1007  The reason for "stride" is because some platforms force padding of
1008  C-structures to the nearest 8-byte boundary. Hence,
1009  sizeof(RMvertex3D) is not always equal to sizeof(float)*3.
1010 
1011  This routine internally calls rmPointMin, followed by rmPointMax.
1012 
1013  @dend
1014  * ----------------------------------------------------
1015  */
1016 RMenum
rmPointMinMax(const float * input,int count,int vdims,int stride,RMvertex3D * min,RMvertex3D * max)1017 rmPointMinMax (const float *input,
1018 	       int count,
1019 	       int vdims,
1020 	       int stride,
1021 	        RMvertex3D *min,
1022 	      RMvertex3D *max)
1023 {
1024     if ((rmPointMin(input, count, vdims, stride, min) != RM_WHACKED) &&	(rmPointMax(input, count, vdims, stride, max) != RM_WHACKED))
1025 	return(RM_CHILL);
1026 
1027     return(RM_WHACKED);
1028 }
1029 
1030 
1031 /*
1032  * ----------------------------------------------------
1033  * @Name rmVertex3DMag
1034  @pstart
1035  double rmVertex3DMag (const RMvertex3D *src)
1036  @pend
1037 
1038  @astart
1039  const RMvertex3D *src - a handle to an RMvertex3D object (input).
1040  @aend
1041 
1042  @dstart
1043 
1044  Computes the vector magnitude of the input RMvertex3D object (a
1045  3-component vector), returning the result to the caller. If the input
1046  is NULL, an error message is reported, and -1.0 is returned.
1047 
1048  @dend
1049  * ----------------------------------------------------
1050  */
1051 double
rmVertex3DMag(const RMvertex3D * v)1052 rmVertex3DMag (const RMvertex3D *v)
1053 {
1054     double d;
1055 
1056     if (RM_ASSERT(v, "rmVertex3DMag() error: the input RMvertex3D object is NULL") == RM_WHACKED)
1057 	return(-1.0);
1058 
1059     d = (v->x * v->x) + (v->y * v->y) + (v->z * v->z);
1060 
1061     return(sqrt(d));
1062 }
1063 
1064 
1065 /*
1066  * ----------------------------------------------------
1067  * @Name rmVertex3DSum
1068  @pstart
1069  RMenum rmVertex3DSum (const RMvertex3D *a,
1070 	               const RMvertex3D *b,
1071 		       RMvertex3D *dst)
1072  @pend
1073 
1074  @astart
1075  const RMvertex3D *a, *b - handles to RMvertex3D objects (input).
1076 
1077  RMvertex3D *dst - a handle to an RMvertex3D object (result).
1078  @aend
1079 
1080  @dstart
1081 
1082  Computes the vector sum (a + b), placing the result into "dst".  This
1083  is computed such that either a or b may be used as the result c.
1084  Returns RM_CHILL upon success, or RM_WHACKED upon failure.
1085 
1086  @dend
1087  * ----------------------------------------------------
1088  */
1089 RMenum
rmVertex3DSum(const RMvertex3D * a,const RMvertex3D * b,RMvertex3D * c)1090 rmVertex3DSum (const RMvertex3D *a,
1091 	       const RMvertex3D *b,
1092 	       RMvertex3D *c)
1093 {
1094     if ((RM_ASSERT(a, "rmVertex3DSum() error: the input RMvertex3D object A is NULL") == RM_WHACKED) ||
1095 	(RM_ASSERT(b, "rmVertex3DSum() error: the input RMvertex3D object B is NULL") == RM_WHACKED) ||
1096 	(RM_ASSERT(c, "rmVertex3DSum() error: the dest RMvertex3D object is NULL") == RM_WHACKED))
1097 	return(RM_WHACKED);
1098 
1099     /* compute c = a + b */
1100     c->x = a->x + b->x;
1101     c->y = a->y + b->y;
1102     c->z = a->z + b->z;
1103 
1104     return(RM_CHILL);
1105 }
1106 
1107 
1108 /*
1109  * ----------------------------------------------------
1110  * @Name rmVertex3DDiff
1111  @pstart
1112  RMenum rmVertex3DDiff (const RMvertex3D *a,
1113 	                const RMvertex3D *b,
1114 		        RMvertex3D *dst)
1115  @pend
1116 
1117  @astart
1118  const RMvertex3D *a, *b - handles to RMvertex3D objects (input).
1119 
1120  RMvertex3D *dst - a handle to an RMvertex3D object (result).
1121  @aend
1122 
1123  @dstart
1124 
1125  Computes the vector difference (a - b), placing the result into
1126  "dst".  This is computed such that either a or b may be used as the
1127  result c.  Returns RM_CHILL upon success, or RM_WHACKED upon failure.
1128 
1129  @dend
1130  * ----------------------------------------------------
1131  */
1132 RMenum
rmVertex3DDiff(const RMvertex3D * a,const RMvertex3D * b,RMvertex3D * c)1133 rmVertex3DDiff (const RMvertex3D *a,
1134 	        const RMvertex3D *b,
1135 	        RMvertex3D *c)
1136 {
1137     if ((RM_ASSERT(a, "rmVertex3DDiff() error: the input RMvertex3D object A is NULL") == RM_WHACKED) ||
1138 	(RM_ASSERT(b, "rmVertex3DDiff() error: the input RMvertex3D object B is NULL") == RM_WHACKED) ||
1139 	(RM_ASSERT(c, "rmVertex3DDiff() error: the dest RMvertex3D object is NULL") == RM_WHACKED))
1140 	return(RM_WHACKED);
1141 
1142     /* compute c = a - b */
1143     c->x = a->x - b->x;
1144     c->y = a->y - b->y;
1145     c->z = a->z - b->z;
1146 
1147     return(RM_CHILL);
1148 }
1149 
1150 
1151 /*
1152  * ----------------------------------------------------
1153  * @Name rmVertex3DDot
1154  @pstart
1155  double rmVertex3DDot (const RMvertex3D *a,
1156 	               const RMvertex3D *b)
1157  @pend
1158 
1159  @astart
1160  const RMvertex3D *a, *b - handles to RMvertex3D objects (input).
1161  @aend
1162 
1163  @dstart
1164 
1165  Computes the vector dot product of the input RMvertex3D objects "a"
1166  and "b," returning the result. Upon failure, an error message is
1167  issued through RMwarning() and 0.0 is returned.
1168 
1169  @dend
1170  * ----------------------------------------------------
1171  */
1172 double
rmVertex3DDot(const RMvertex3D * a,const RMvertex3D * b)1173 rmVertex3DDot(const RMvertex3D *a,
1174 	      const RMvertex3D *b)
1175 {
1176     double t;
1177 
1178     if ((RM_ASSERT(a, "rmVertex3DDot() error: the input RMvertex3D object A is NULL") == RM_WHACKED) ||
1179 	(RM_ASSERT(b, "rmVertex3DDot() error: the input RMvertex3D object B is NULL") == RM_WHACKED))
1180 	return(0.0);
1181 
1182     /* compute t = <a, b> */
1183     t = (a->x * b->x) + (a->y * b->y) + (a->z * b->z);
1184     if (fabs(t) < 0.0001)
1185 	t = 0.0;
1186     return(t);
1187 }
1188 
1189 
1190 /*
1191  * ----------------------------------------------------
1192  * @Name rmVertex3DCross
1193  @pstart
1194  RMenum rmVertex3DCross (RMvertex3D *p,
1195 		         RMvertex3D *r,
1196 			 RMvertex3D *result)
1197  @pend
1198 
1199  @astart
1200  RMvertex3D *p, *r - handles to RMvertex3D objects (input).
1201 
1202  RMvertex3D *result - a handle to an RMvertex3D object (result).
1203  @aend
1204 
1205  @dstart
1206 
1207  Computes the vector cross product of the two input vectors "P" and
1208  "R", placing the resulting vector into "result". Returns RM_CHILL
1209  upon success or RM_WHACKED upon failure.
1210 
1211  This routine is written such that "result" may be the same object as
1212  either P or R.
1213 
1214  @dend
1215  * ----------------------------------------------------
1216  */
1217 RMenum
rmVertex3DCross(RMvertex3D * p,RMvertex3D * r,RMvertex3D * result)1218 rmVertex3DCross (RMvertex3D *p,
1219 		 RMvertex3D *r,
1220 		 RMvertex3D *result)
1221 {
1222     RMvertex3D t;
1223 
1224     if ((RM_ASSERT(p, "rmVertex3DCross() error: the input P vector is NULL") == RM_WHACKED) ||
1225 	(RM_ASSERT(r, "rmVertex3DCross() error: the input R vector is NULL") == RM_WHACKED) ||
1226 	(RM_ASSERT(result, "rmVertex3DCross() error: the result vector is NULL") == RM_WHACKED))
1227 	return(RM_WHACKED);
1228 
1229     /* compute c = p x r */
1230     t.x = (p->y * r->z) - (p->z * r->y);
1231     t.y = (p->z * r->x) - (p->x * r->z);
1232     t.z = (p->x * r->y) - (p->y * r->x);
1233 
1234     memcpy((void *)result, (void *)&t, sizeof(RMvertex3D));
1235 
1236     return(RM_CHILL);
1237 }
1238 
1239 
1240 /*
1241  * ----------------------------------------------------
1242  * @Name rmVertex3DNormalize
1243  @pstart
1244  RMenum rmVertex3DNormalize (RMvertex3D *toNormalize)
1245  @pend
1246 
1247  @astart
1248  RMvertex3D *toNormalize - a handle to the RMvertex3D object
1249     (modified).
1250  @aend
1251 
1252  @dstart
1253 
1254  Use this routine to normalize the 3-component vector contained in an
1255  RMvertex3D object. Inside this routine, the vector magnitude of the
1256  RMvertex3D object is computed. Next, if the magnitude is non-zero,
1257  each component of the vector is divided by the magnitude, and
1258  RM_CHILL is returned. If the magnitude is zero, RM_WHACKED is
1259  returned.
1260 
1261  @dend
1262  * ----------------------------------------------------
1263  */
1264 RMenum
rmVertex3DNormalize(RMvertex3D * a)1265 rmVertex3DNormalize (RMvertex3D *a)
1266 {
1267     double mag;
1268     RMenum rstat = RM_CHILL;
1269 
1270     if (RM_ASSERT(a, "rmVertex3DNormalize() error: the input RMvertex3D object is NULL") == RM_WHACKED)
1271 	return(RM_WHACKED);
1272 
1273     mag = rmVertex3DMag(a);
1274     if (mag != 0.0)
1275     {
1276         mag = 1.0 / mag;
1277 	a->x *= mag;
1278 	a->y *= mag;
1279 	a->z *= mag;
1280     }
1281     else
1282 	rstat = RM_WHACKED;
1283 
1284     return(rstat);
1285 }
1286 
1287 
1288 /*
1289  * ----------------------------------------------------
1290  * @Name rmVertex3DMagNormalize
1291  @pstart
1292  RMenum rmVertex3DMagNormalize (RMvertex3D *toNormalize,
1293 		                double *magReturn)
1294  @pend
1295 
1296  @astart
1297  RMvertex3D *toNormalize - a handle to an RMvertex3D object
1298     (modified).
1299 
1300  double *magReturn - a pointer to a double (result).
1301  @aend
1302 
1303  @dstart
1304 
1305  Use this routine when you want to normalize a vector, and get the
1306  vector magnitude of the unnormalized vector in one call.
1307 
1308  A return value of RM_CHILL means that all's well, while a return
1309  value of RM_WHACKED means that either one of the input parameters was
1310  NULL, or that the input RMvertex3D had a zero-valued vector
1311  magnitude.
1312 
1313  Upon success, the input RMvertex3D is normalized in-situ, and the
1314  vector magnitude (used to normalize the vector) is returned in the
1315  caller-supplied memory.
1316 
1317  @dend
1318  * ----------------------------------------------------
1319  */
1320 RMenum
rmVertex3DMagNormalize(RMvertex3D * a,double * m)1321 rmVertex3DMagNormalize (RMvertex3D *a,
1322 		        double *m)
1323 {
1324     double mag;
1325     RMenum rstat = RM_CHILL;
1326 
1327     if ((RM_ASSERT(a, "rmVertex3DMagNormalize() error: the input RMvertex3D object is NULL") == RM_WHACKED) ||
1328 	(RM_ASSERT(m, "rmVertex3DMagNormalize() error: the input magReturn pointer is NULL") == RM_WHACKED))
1329 	return(RM_WHACKED);
1330 
1331     *m = mag = rmVertex3DMag(a);
1332     if (mag != 0.0)
1333     {
1334         mag = 1.0 / mag;
1335 	a->x *= mag;
1336 	a->y *= mag;
1337 	a->z *= mag;
1338     }
1339     else
1340 	rstat = RM_WHACKED;
1341 
1342     return(rstat);
1343 }
1344 
1345 
1346 /*
1347  * ----------------------------------------------------
1348  * @Name rmVertex3DMidpoint
1349  @pstart
1350  RMenum rmVertex3DMidpoint (const RMvertex3D *a,
1351 		            const RMvertex3D *b,
1352 			    RMvertex3D *dst)
1353  @pend
1354 
1355  @astart
1356  const RMvertex3D *a,*b - handles to RMvertex3D objects (input).
1357 
1358  RMvertex3D *dst - a handle to an RMvertex3D object (result).
1359  @aend
1360 
1361  @dstart
1362 
1363  Computes the midpoint between the two RMvertex3D objects "a" and "b",
1364  placing the result into "dst". Returns RM_CHILL upon success, or
1365  RM_WHACKED upon failure.
1366 
1367  @dend
1368  * ----------------------------------------------------
1369  */
1370 RMenum
rmVertex3DMidpoint(const RMvertex3D * a,const RMvertex3D * b,RMvertex3D * r)1371 rmVertex3DMidpoint (const RMvertex3D *a,
1372 		    const RMvertex3D *b,
1373 		    RMvertex3D *r)
1374 {
1375     RMvertex3D t;
1376 
1377     if ((RM_ASSERT(a, "rmVertex3DMidpoint() error: the input A RMvertex3D is NULL") == RM_WHACKED) ||
1378 	(RM_ASSERT(b, "rmVertex3DMidpoint() error: the input B RMvertex3D is NULL") == RM_WHACKED))
1379 	return(RM_WHACKED);
1380 
1381     t.x = a->x + 0.5 * (b->x - a->x);
1382     t.y = a->y + 0.5 * (b->y - a->y);
1383     t.z = a->z + 0.5 * (b->z - a->z);
1384 
1385     memcpy((void *)r, (void *)&t, sizeof(RMvertex3D));
1386 
1387     return(RM_CHILL);
1388 }
1389 
1390 
1391 /*
1392  * ----------------------------------------------------
1393  * @Name rmDCFromWC3
1394  @pstart
1395  RMenum rmDCFromWC3 (const RMvertex3D *src,
1396 	             RMvertex3D *dst,
1397 		     int nPoints,
1398 		     const RMcamera3D *cam3d,
1399 		     const RMmatrix *model,
1400 		     float viewPort[4],
1401 		     int windowWidth,
1402 		     int windowHeight)
1403  @pend
1404 
1405  @astart
1406  const RMvertex3D *src - an array of RMvertex3D objects (input).
1407 
1408  RMvertex3D *dst - an array of RMvertex3D objects (result).
1409 
1410  int nPoints - an integer value indicating the number of points from
1411     "src" that will be transformed.
1412 
1413  RMcamera3D *cam3d - a handle to an RMcamera3D object that specifies
1414     view geometry (input).
1415 
1416  RMmatrix *model - an RMmatrix object specifying a model
1417     transformation in world coordinates (input). This parameter is
1418     optional, and NULL may be used in place of an RMmatrix object.
1419 
1420  float viewPort[4] - an array of floating point values (length 4)
1421     specifying the viewport geometry within the display window. Each
1422     array value should be in the range 0..1, specifying the viewport
1423     origin and width/height in NDC coordinates. The array is in the
1424     following order: xmin, ymin, xmax,ymax. In other words, the
1425     viewport is specified using two coordinates defining the min/max
1426     corners of the viewport in NDC space.
1427 
1428  int windowWidth, windowHeight - integer values specifying the width
1429     and height in pixels of the display window (input).
1430  @aend
1431 
1432  @dstart
1433 
1434  Use this routine to compute the device coordinates from world
1435  coordinates.  RM_CHILL is returned upon success, or RM_WHACKED upon
1436  failure.
1437 
1438  Each RMvertex3D from "src", of which there are nPoints, is
1439  transformed through a derived projection matrix producing window
1440  (pixel) coordinates that are placed into "dst." Each dst[i]
1441  represents the projected window coordinates of src[i]. The derived
1442  projection matrix consists of an optional modeling transformation
1443  matrix (model), and a viewing matrix computed from the camera model
1444  defined by the input RMcamera3D object.
1445 
1446  Note that no window is required, the projected coordinates are
1447  computed using the window and viewport dimensions specified by the
1448  input, no actual rendering occurs (OpenGL is not involved with this
1449  routine).
1450 
1451  @dend
1452  * ----------------------------------------------------
1453  */
1454 RMenum
rmDCFromWC3(const RMvertex3D * a,RMvertex3D * ret,int n,const RMcamera3D * cam3d,const RMmatrix * model,float vp[4],int width,int height)1455 rmDCFromWC3 (const RMvertex3D *a,	/* list of world coord points */
1456 	     RMvertex3D *ret,    	/* returned device coords */
1457 	     int n,			/* how many points? */
1458 	     const RMcamera3D *cam3d,	/* a 3d camera  */
1459 	     const RMmatrix *model,	/* world model matrix */
1460 	     float vp[4],		/* viewport  */
1461 	     int width,			/* window width in pixels */
1462 	     int height)		/* windoe height in pixels */
1463 {
1464     int      i;
1465     float    vpWidth, vpHeight;
1466     float    s[4];
1467     RMmatrix projection, vpm;
1468     RMmatrix forward;
1469     RMmatrix myModel, myView;
1470 
1471     /* given a point in 3D world coords, return its pixel coords inwindow space (replicates the OpenGL pipeline) */
1472     if ((RM_ASSERT(a, "rmDCFromWC3() error: the input list of coordinates is NULL") == RM_WHACKED) ||
1473 	(RM_ASSERT(ret, "rmDCFromWC3() error: the return RMvertex3D handle is NULL") == RM_WHACKED) ||
1474 	(RM_ASSERT(cam3d, "rmDCFromWC3() error: the input RMcamera3D object is NULL") == RM_WHACKED))
1475 	return(RM_WHACKED);
1476 
1477     rmCamera3DComputeViewMatrix(cam3d, &myView, &projection);
1478 
1479     if (model == NULL)
1480 	rmMatrixIdentity(&myModel);
1481     else
1482 	rmMatrixCopy(&myModel, model);
1483 
1484 /*    rmMatrixInverse(&projection, &projection); */
1485     rmMatrixMultiply(&myModel, &myView, &forward);
1486     rmMatrixMultiply(&forward, &projection, &forward);
1487 
1488     /* now, composite has the entire wc to pixel mapping */
1489     vpWidth = vp[2] - vp[0];
1490     vpHeight = vp[3] - vp[1];
1491 
1492     rmMatrixIdentity(&vpm);
1493     private_rmComputeViewportMatrix(vp, vpWidth, vpHeight, &vpm);
1494 
1495     VCOPY(a, (RMvertex3D *)s);
1496 
1497     for (i = 0; i < n; i++)
1498     {
1499 	int   j;
1500 	float w;
1501 	float s[4];
1502 
1503 	VCOPY((a + i), s);
1504 	s[3] = 1.0;
1505 
1506 	/* run the point through the model and projection matrices */
1507 	rmPoint4MatrixTransform(s, &forward, s);
1508 
1509 	/*
1510 	 * the point is now in "clip coords".
1511 	 * next, convert to ndc space. this is done by dividing by
1512 	 * the homogenous-space coordinate.
1513 	 */
1514 	w = s[3];
1515 	w = 1.0 / w;
1516 
1517 	for (j = 0; j < 4; j++)
1518 	    s[j] *= w;
1519 
1520 	/* now transform through vpm. this gives us the window coords */
1521 	rmPoint4MatrixTransform(s, &vpm, s);
1522 
1523 	VCOPY(s, (ret + i));
1524     }
1525     return(RM_CHILL);
1526 }
1527 
1528 
1529 /*
1530  * ----------------------------------------------------
1531  * @Name rmDCFromWC2
1532  @pstart
1533  RMenum rmDCFromWC2 (const RMvertex2D *src,
1534 	             RMvertex2D *dst,
1535 		     int nPoints,
1536 		     const RMcamera2D *cam3d,
1537 		     const RMmatrix *model,
1538 		     float viewPort[4],
1539 		     int windowWidth,
1540 		     int windowHeight)
1541  @pend
1542 
1543  @astart
1544  const RMvertex2D *src - an array of RMvertex2D objects (input).
1545 
1546  RMvertex2D *dst - an array of RMvertex2D objects (result).
1547 
1548  int nPoints - an integer value indicating the number of points from
1549     "src" that will be transformed.
1550 
1551  RMcamera2D *cam2d - a handle to an RMcamera2D object that specifies
1552     view geometry (input).
1553 
1554  RMmatrix *model - an RMmatrix object specifying a model
1555     transformation in world coordinates (input). This parameter is
1556     optional, and NULL may be used in place of an RMmatrix object.
1557 
1558  float viewPort[4] - an array of floating point values (length 4)
1559     specifying the viewport geometry within the display window. Each
1560     array value should be in the range 0..1. The array is in the
1561     following order: xmin, ymin, xmax,ymax. In other words, the
1562     viewport is specified using two coordinates defining the min/max
1563     corners of the viewport in NDC space.
1564 
1565  int windowWidth, windowHeight - integer values specifying the width
1566     and height in pixels of the display window (input).
1567  @aend
1568 
1569  @dstart
1570 
1571  Use this routine to compute the device coordinates from world
1572  coordinates.  RM_CHILL is returned upon success, or RM_WHACKED upon
1573  failure.
1574 
1575  Each RMvertex2D from "src", of which there are nPoints, is
1576  transformed through a derived projection matrix producing window
1577  (pixel) coordinates that are placed into "dst." Each dst[i]
1578  represents the projected window coordinates of src[i]. The derived
1579  projection matrix consists of an optional modeling transformation
1580  matrix (model), and a viewing matrix computed from the camera model
1581  defined by the input RMcamera2D object.
1582 
1583  Note that no window is required, the projected coordinates are
1584  computed using the window and viewport dimensions specified by the
1585  input, no actual rendering occurs (OpenGL is not involved with this
1586  routine).
1587 
1588  @dend
1589  * ----------------------------------------------------
1590  */
1591 RMenum
rmDCFromWC2(const RMvertex2D * a,RMvertex2D * ret,int n,const RMcamera2D * cam2d,const RMmatrix * model,float vp[4],int width,int height)1592 rmDCFromWC2 (const RMvertex2D *a,	/* list of world coord points */
1593 	     RMvertex2D *ret,      	/* returned device coords */
1594 	     int n,			/* how many points? */
1595 	     const RMcamera2D *cam2d,	/* a 3d camera  */
1596 	     const RMmatrix *model,	/* world model matrix */
1597 	     float vp[4],		/* viewport  */
1598 	     int width,			/* window width in pixels */
1599 	     int height)		/* windoe height in pixels */
1600 {
1601     int      i;
1602     float    s[4];
1603     float    vpWidth, vpHeight;
1604     double   w;
1605     RMmatrix projection, vpm;
1606     RMmatrix forward;
1607     RMmatrix myModel;
1608 
1609     /* given a point in 2D world coords, return its pixel coords inwindow space (replicates the OpenGL pipeline) */
1610     if ((RM_ASSERT(a, "rmDCFromWC2() error: the input list of coordinates is NULL") == RM_WHACKED) ||
1611 	(RM_ASSERT(ret, "rmDCFromWC2() error: the return RMvertex2D handle is NULL") == RM_WHACKED) ||
1612 	(RM_ASSERT(cam2d, "rmDCFromWC3() error: the input RMcamera2D object is NULL") == RM_WHACKED))
1613 	return(RM_WHACKED);
1614 
1615 
1616     rmCamera2DComputeViewMatrix(cam2d, &projection);
1617 
1618     if (model == NULL)
1619 	rmMatrixIdentity(&myModel);
1620     else
1621 	rmMatrixCopy(&myModel, model);
1622 
1623     rmMatrixMultiply(&myModel, &projection, &forward);
1624 
1625     /* now, composite has the entire wc to pixel mapping */
1626     vpWidth = vp[2] - vp[0];
1627     vpHeight = vp[3] - vp[1];
1628 
1629     rmMatrixIdentity(&vpm);
1630     private_rmComputeViewportMatrix(vp, vpWidth, vpHeight, &vpm);
1631 
1632     for (i = 0; i < n; i++)
1633     {
1634 	int j;
1635 
1636 	s[0] = a[i].x;
1637 	s[1] = a[i].y;
1638 	s[2] = 0.0;
1639 	s[3] = 1.0;
1640 
1641 	/* run the point through the model and projection matrices */
1642 	rmPoint4MatrixTransform(s, &forward, s);
1643 
1644 	/*
1645 	 * the point is now in "clip coords".
1646 	 * next, convert to ndc space. this is done by dividing by
1647 	 * the homogenous-space coordinate.
1648 	 */
1649 	w = s[3];
1650 	w = 1.0 / w;
1651 
1652 	for (j = 0; j < 4; j++)
1653 	    s[j] *= w;
1654 
1655 	/* now transform through vpm. this gives us the window coords */
1656 	rmPoint4MatrixTransform(s, &vpm, s);
1657 
1658 	ret[i].x = s[0];
1659 	ret[i].y = s[1];
1660 
1661     }
1662     return(RM_CHILL);
1663 }
1664 
1665 
1666 /* PRIVATE
1667  *
1668  * this routine is like the public routine, but the transformation
1669  * information is contained within the RMstate object. this routine is
1670  * useful from within inside a draw routine to obtain the projected
1671  * pixel coordinates of a vertex, perhaps to perform some view dependent
1672  * operations.
1673  */
1674 void
private_rmDCFromWC3(RMvertex3D * a,RMvertex3D * ret,int n,RMstate * state)1675 private_rmDCFromWC3 (RMvertex3D *a,	/* list of world coord points */
1676 		     RMvertex3D *ret,	/* returned device coords */
1677 		     int n,		/* how many points? */
1678 		     RMstate *state)
1679 {
1680     int      i;
1681     float    s[4];
1682     RMmatrix vpm;
1683     RMmatrix forward;
1684 
1685     /* given a point in 3D world coords, return its pixel coords inwindow space (replicates the OpenGL pipeline) */
1686     /*    rmMatrixMultiply(&(state->model), &(state->view), &forward); */
1687     rmMatrixMultiply(&(state->modelView), &(state->projection), &forward);
1688 
1689     /* now, composite has the entire wc to pixel mapping */
1690     rmMatrixIdentity(&vpm);
1691 
1692     private_rmComputeViewportMatrix(state->vp, (float)state->w,
1693 				    (float)state->h, &vpm);
1694 
1695     VCOPY(a,(RMvertex3D *)s);
1696 
1697     for (i = 0; i < n; i++)
1698     {
1699 	int   j;
1700 	float w;
1701 
1702 	VCOPY((a + i), s);
1703 	s[3] = 1.0;
1704 
1705 	/* run the point through the model and projection matrices */
1706 	rmPoint4MatrixTransform(s, &forward, s);
1707 
1708 	/*
1709 	 * the point is now in "clip coords".
1710 	 * next, convert to ndc space. this is done by dividing by
1711 	 * the homogenous-space coordinate.
1712 	 */
1713 	w = s[3];
1714 	w = 1.0 / w;
1715 
1716 	for (j = 0; j < 4; j++)
1717 	    s[j] *= w;
1718 
1719 	/* now transform through vpm. this gives us the window coords */
1720 	rmPoint4MatrixTransform(s, &vpm, s);
1721 	VCOPY(s, (ret + i));
1722     }
1723 }
1724 
1725 
1726 /* PRIVATE
1727  *
1728  * this routine is like the public routine, but the transformation
1729  * information is contained within the RMstate object. this routine is
1730  * useful from within inside a draw routine to obtain the projected
1731  * pixel coordinates of a vertex, perhaps to perform some view dependent
1732  * operations.
1733  */
1734 void
private_rmDCFromWC2(RMvertex3D * a,RMvertex3D * ret,int n,RMstate * state)1735 private_rmDCFromWC2 (RMvertex3D *a,	/* list of world coord points */
1736 		     RMvertex3D *ret,	/* returned device coords */
1737 		     int n,		/* how many points? */
1738 		     RMstate *state)
1739 {
1740     RMmatrix vpm;
1741     RMmatrix forward;
1742     int      i;
1743     float    s[4];
1744 
1745     /* given a point in 2D world coords, return its pixel coords inwindow space (replicates the OpenGL pipeline) */
1746 #if 0
1747     rmMatrixMultiply(&(state->model), &(state->view), &forward);
1748     rmMatrixMultiply(&forward, &(state->projection), &forward);
1749 #endif
1750 
1751     /* 7/28/01 - use the modelView matrix, rather than the separate
1752      model & view matrices. The separate model & view matrices are no
1753      longer guaranteed to be valid in the multistage rendering, but
1754      modelView will be valid. There are no explicit commands to update
1755      the model & view matrices separately; these are subsumed by a
1756      single modelView matrix in order to more closely model the OpenGL
1757      matrix stack. */
1758 
1759     rmMatrixMultiply(&(state->modelView), &(state->projection), &forward);
1760 
1761     /* now, composite has the entire wc to pixel mapping */
1762     rmMatrixIdentity(&vpm);
1763 
1764     private_rmComputeViewportMatrix(state->vp, (float)state->w,
1765 				    (float)state->h, &vpm);
1766 
1767 #if 0
1768     vpm.m[0][0] = state->vp[2] * 0.5; /*  * state->w; */
1769     vpm.m[1][1] = state->vp[3] * 0.5; /*  * state->h; */
1770     vpm.m[3][0] = vpm.m[0][0] + ((state->w  * state->vp[0]) / state->w);
1771     vpm.m[3][1] = vpm.m[1][1] + ((state->h  * state->vp[1]) / state->h);
1772 #endif
1773 
1774     for (i = 0; i < n; i++)
1775     {
1776 	int   j;
1777 	float w;
1778 
1779 	s[0] = a[i].x;
1780 	s[1] = a[i].y;
1781 	s[2] = 0.0;
1782 	s[3] = 1.0;
1783 
1784 	/* run the point through the model and projection matrices */
1785 	rmPoint4MatrixTransform(s,&forward,s);
1786 
1787 	/*
1788 	 * the point is now in "clip coords".
1789 	 * next, convert to ndc space. this is done by dividing by
1790 	 * the homogenous-space coordinate.
1791 	 */
1792 	w = s[3];
1793 	w = 1.0 / w;
1794 
1795 	for (j = 0; j < 4; j++)
1796 	    s[j] *= w;
1797 
1798 	/* now transform through vpm. this gives us the window coords */
1799 	rmPoint4MatrixTransform(s, &vpm, s);
1800 	ret[i].x = s[0];
1801 	ret[i].y = s[1];
1802 	ret[i].z = s[2];
1803     }
1804 }
1805 
1806 
1807 /*
1808  * the following routines do conversions to/from floating point
1809  * and fixed point.
1810  *
1811  * these conversion routines are designed to represent numbers
1812  * in the range -2048..2048. we use 11 bits for representing the
1813  * integer part, and 8 bits for representing the fractional part..
1814  * that leads to a fractional resolution (potential error) of 0.00390625.
1815  */
1816 
1817 /* PRIVATE */
1818 int
private_rmFixedFromFloat(float a)1819 private_rmFixedFromFloat (float a)
1820 {
1821     /* we only do positive numbers */
1822     float        whole, frac;
1823     unsigned int iwhole, ifrac, isign;
1824 
1825     if (a < 0.0)
1826     {
1827 	isign = (1 << 19);
1828 	a = -1.0 * a;
1829     }
1830     else
1831 	isign = 0;
1832 
1833     whole = (float)((int)a);
1834     frac = a - whole;
1835 
1836     frac = frac * 256.0;
1837     ifrac = (int)frac;
1838     ifrac &= 0x0ff;		/* keep 8 bits of fraction */
1839 
1840     iwhole = (int)whole;
1841     iwhole = iwhole & 0x07FF;	/* keep 11 bits */
1842     iwhole = iwhole << 8;	/* make room for the fraction */
1843 
1844     return(iwhole | ifrac | isign);
1845 }
1846 
1847 
1848 /* PRIVATE */
1849 float
private_rmFloatFromFixed(int a)1850 private_rmFloatFromFixed (int a)
1851 {
1852     float frac, whole, result;
1853     int   ifrac, iwhole, isign;
1854 
1855     isign = a & (1 << 19);
1856     ifrac = a & 0x0ff;		/* grab the fraction */
1857     iwhole = (a >> 8) & 0x07ff;	/* grab the integer part */
1858 
1859     frac = (float)ifrac * 0.003921568; /* 1/255 */
1860     whole = (float)iwhole;
1861 
1862     result = whole + frac;
1863 
1864     if (isign)
1865 	result *= -1.0;
1866 
1867     return(result);
1868 }
1869 
1870 /* PRIVATE */
1871 void
private_rmNewWCfromDCOffset(RMvertex3D * source,float xmove,float ymove,RMvertex3D * dest,RMmatrix * vpm,RMmatrix * vpm_inv,RMmatrix * forward,RMmatrix * inv)1872 private_rmNewWCfromDCOffset(RMvertex3D *source,	/* input 3d coordinate */
1873 			    float xmove, 	/* pixel offset */
1874 			    float ymove, 	/* pixel offset */
1875 			    RMvertex3D *dest, 	/* result */
1876 			    RMmatrix *vpm, 	/* viewport matrix */
1877 			    RMmatrix *vpm_inv, 	/* inverse viewport matrix */
1878 			    RMmatrix *forward, 	/* model+view+projection */
1879 			    RMmatrix *inv) 	/* model/view/projection inverse */
1880 {
1881     /*
1882      * given a world coordinate "source", what is the new
1883      * world coordinate that is the sum of "source" and
1884      * (xmove, ymove) where xmove, ymove are pixel offsets from
1885      * the projected point "source"?
1886      *
1887      * in some sense, this problem is underspecified because we have
1888      * no Z information in the delta. we just assume the resulting
1889      * coordinate will have the same z-value as the input coordinate.
1890      */
1891     float      f[4], f2[4];
1892     float      save_z, w;
1893     RMvertex3D t2;
1894 
1895     VCOPY(source, (RMvertex3D *)f);
1896     f[3] = 1.0;
1897 
1898     rmPoint4MatrixTransform(f, forward, f);
1899 
1900     save_z = f[2];
1901     w = f[3];
1902 
1903     VCOPY(f,&t2);
1904 
1905     t2.x = t2.x / w;
1906     t2.y = t2.y / w;
1907     t2.z = t2.z / w;
1908 
1909     rmPointMatrixTransform(&t2, vpm, &t2);
1910 
1911     t2.x += xmove;
1912     t2.y += ymove;
1913 
1914     rmPointMatrixTransform(&t2, vpm_inv, (RMvertex3D *)f);
1915     f[0] *= w;
1916     f[1] *= w;
1917     f[2] *= w;
1918     f[3] = w;
1919     rmPoint4MatrixTransform(f, inv, f2);
1920     VCOPY(f2, dest);
1921 }
1922 
1923 /* PRIVATE */
1924 void
private_rmNewWCfromDC(RMvertex3D * source,RMvertex3D * dest,RMmatrix * vpm,RMmatrix * vpm_inv,RMmatrix * forward,RMmatrix * inv)1925 private_rmNewWCfromDC(RMvertex3D *source,	/* input 3d coordinate */
1926 		      RMvertex3D *dest, 	/* result */
1927 		      RMmatrix *vpm, 	/* viewport matrix */
1928 		      RMmatrix *vpm_inv, 	/* inverse viewport matrix */
1929 		      RMmatrix *forward, 	/* model+view+projection */
1930 		      RMmatrix *inv)  /* model/view/projection inverse */
1931 {
1932     /*
1933      * given an input device coordinate in "source", what is the
1934      * corresponding world coordinate?
1935      */
1936     float      f[4], f2[4];
1937     float      save_z, w;
1938     RMvertex3D t2;
1939 
1940     VCOPY(source, (RMvertex3D *)f);
1941     f[3] = 1.0;
1942 
1943     rmPoint4MatrixTransform(f, forward, f);
1944 
1945     save_z = f[2];
1946     w = f[3];
1947 
1948     VCOPY(f,&t2);
1949 
1950     t2.x = t2.x / w;
1951     t2.y = t2.y / w;
1952     t2.z = t2.z / w;
1953 
1954     rmPointMatrixTransform(&t2, vpm, &t2);
1955 
1956     rmPointMatrixTransform(&t2, vpm_inv, (RMvertex3D *)f);
1957     f[0] *= w;
1958     f[1] *= w;
1959     f[2] = save_z;
1960     f[3] = w;
1961     rmPoint4MatrixTransform(f, inv, f2);
1962     VCOPY(f2, dest);
1963 }
1964 
1965 /* PRIVATE */
1966 void
private_rmComputeViewportMatrix(float vp[4],float fw,float fh,RMmatrix * returnMatrix)1967 private_rmComputeViewportMatrix(float vp[4],
1968 				float fw,
1969 				float fh,
1970 				RMmatrix *returnMatrix)
1971 {
1972     RMmatrix *m = returnMatrix;
1973 
1974     /*
1975      * the following formulation should work in both cases, where:
1976      * (a) the vp AND fw/fh are NDC coordinates/values, and
1977      * (b) the vp AND fw/fh are pixel values.
1978      *
1979      * vp should be xmin, ymin, xwidth, yheight
1980      */
1981     m->m[0][0] = vp[2] * 0.5F;
1982     m->m[1][1] = vp[3] * 0.5F;
1983     m->m[3][0] = m->m[0][0] + fw * vp[0];
1984     m->m[3][1] = m->m[1][1] + fh * vp[1];
1985 }
1986 /* EOF */
1987