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