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: rmcamera.c,v 1.10 2005/09/12 04:02:24 wes Exp $
27  * Version: $Name: OpenRM-1-6-0-2-RC2 $
28  * $Revision: 1.10 $
29  * $Log: rmcamera.c,v $
30  * Revision 1.10  2005/09/12 04:02:24  wes
31  * Minor documentation updates.
32  *
33  * Revision 1.9  2005/02/19 16:22:50  wes
34  * Distro sync and consolidation.
35  *
36  * Revision 1.8  2005/01/23 17:00:22  wes
37  * Copyright updated to 2005.
38  *
39  * Revision 1.7  2004/04/09 14:48:03  wes
40  * Fix some compile warnings about unused variables.
41  *
42  * Revision 1.6  2004/03/30 14:13:31  wes
43  * Fixed declarations and man page docs for several routines.
44  *
45  * Revision 1.5  2004/02/13 00:46:31  wes
46  * Updated inline documentation.
47  *
48  * Revision 1.4  2004/01/16 16:43:24  wes
49  * Updated copyright line for 2004.
50  *
51  * Revision 1.3  2003/06/13 03:08:39  wes
52  * Minor documentation tweak.
53  *
54  * Revision 1.2  2003/02/02 02:07:15  wes
55  * Updated copyright to 2003.
56  *
57  * Revision 1.1.1.1  2003/01/28 02:15:23  wes
58  * Manual rebuild of rm150 repository.
59  *
60  * Revision 1.7  2003/01/16 22:21:17  wes
61  * Updated all source files to reflect new organization of header files:
62  * all header files formerly located in include/rmaux, include/rmi, include/rmv
63  * are now located in include/rm.
64  *
65  * Revision 1.6  2002/08/17 15:07:46  wes
66  * rmCamera3DComputeViewFromGeometry() - updated computation of near/far
67  * clip planes to allow more front-to-back maneuvering space.
68  *
69  * Revision 1.5  2002/04/30 19:28:55  wes
70  * Updated copyright dates.
71  *
72  * Revision 1.4  2001/03/31 17:12:38  wes
73  * v1.4.0-alpha-2 checkin.
74  *
75  * Revision 1.3  2000/08/23 23:24:52  wes
76  * Updated documentation.
77  *
78  * Revision 1.2  2000/04/20 16:29:47  wes
79  * Documentation additions/enhancements, some code rearragement.
80  *
81  * Revision 1.1.1.1  2000/02/28 21:29:40  wes
82  * OpenRM 1.2 Checkin
83  *
84  * Revision 1.1.1.1  2000/02/28 17:18:47  wes
85  * Initial entry - pre-RM120 release, source base for OpenRM 1.2.
86  *
87  */
88 
89 #include <rm/rm.h>
90 #include "rmprivat.h"
91 
92 /* default camera parameters */
93 extern float      RM_DEFAULT_2DCAMERA_XMIN, RM_DEFAULT_2DCAMERA_YMIN, RM_DEFAULT_2DCAMERA_XMAX,  RM_DEFAULT_2DCAMERA_YMAX;
94 extern float      RM_DEFAULT_2DCAMERA_ASPECT;
95 extern float 	  RM_DEFAULT_3DCAMERA_HITHER, RM_DEFAULT_3DCAMERA_YON, RM_DEFAULT_3DCAMERA_ASPECT, RM_DEFAULT_3DCAMERA_FOV;
96 extern RMenum     RM_DEFAULT_3DCAMERA_PROJECTION;
97 extern RMvertex3D RM_DEFAULT_3DCAMERA_EYE, RM_DEFAULT_3DCAMERA_LOOKAT, RM_DEFAULT_3DCAMERA_UP;
98 
99 /* PRIVATE declarations */
100 static void private_rmCamera2DComputeMatrix (const RMcamera2D *c, RMmatrix *m);
101 
102 /*
103  * ----------------------------------------------------
104  * @Name rmCamera2DNew
105  @pstart
106  RMcamera2D * rmCamera2DNew (void)
107  @pend
108 
109  @astart
110  No arguments.
111  @aend
112 
113  @dstart
114 
115  A default 2D camera is created and a handle to the requested
116  RMcamera2D is returned.  If the 2D camera cannot be created, then a
117  NULL pointer is returned.
118 
119  @dend
120  * ----------------------------------------------------
121  */
122 RMcamera2D *
rmCamera2DNew(void)123 rmCamera2DNew (void)
124 {
125     RMcamera2D *c;
126 
127     if ((c = (RMcamera2D *)malloc(sizeof(RMcamera2D))) == NULL)
128 	return(NULL);
129 
130     /* defaults */
131     rmCamera2DSetExtents(c, RM_DEFAULT_2DCAMERA_XMIN, RM_DEFAULT_2DCAMERA_YMIN, RM_DEFAULT_2DCAMERA_XMAX, RM_DEFAULT_2DCAMERA_YMAX);
132     rmCamera2DSetAspectRatio(c, RM_DEFAULT_2DCAMERA_ASPECT);
133 
134     return(c);
135 }
136 
137 
138 /*
139  * ----------------------------------------------------
140  * @Name rmCamera2DCopy
141  @pstart
142  RMenum rmCamera2DCopy (RMcamera2D *dst,
143 	                const RMcamera2D *src)
144  @pend
145 
146  @astart
147  RMcamera2D *dst - a handle to an RMcamera2D object to be modified
148     (output).
149 
150  const RMcamera2D *src - a handle to an RMcamera2D object that will be
151     copied (input).
152  @aend
153 
154  @dstart
155 
156  Copies all parameters and attributes from one RMcamera2D object to
157  another. Returns RM_CHILL upon success, or RM_WHACKED upon failure.
158 
159  @dend
160  * ----------------------------------------------------
161  */
162 RMenum
rmCamera2DCopy(RMcamera2D * dst,const RMcamera2D * src)163 rmCamera2DCopy (RMcamera2D *dst,
164 	        const RMcamera2D *src)
165 {
166     if ((RM_ASSERT(dst, "rmCamera2DCopy error: the dst RMcamera2D pointer is NULL") == RM_WHACKED) ||
167 	(RM_ASSERT(src, "rmCamera2DCopy error: the src RMcamera2D pointer is NULL") == RM_WHACKED))
168 	return(RM_WHACKED);
169 
170     memcpy((void *)dst, (void *)src, sizeof(RMcamera2D));
171 
172     return(RM_CHILL);
173 }
174 
175 
176 /*
177  * ----------------------------------------------------
178  * @Name rmCamera2DDelete
179  @pstart
180  void rmCamera2DDelete (RMcamera2D *toDelete)
181  @pend
182 
183  @astart
184  RMcamera2D *toDelete - a handle to  RMcamera2D object to delete (modified).
185  @aend
186 
187  @dstart
188 
189  Free the RMcamera2D resources pointed to by c.  It is assumed that c
190  points to a valid RMcamera2D previously created with rmCamera2DNew().
191 
192  @dend
193  * ----------------------------------------------------
194  */
195 void
rmCamera2DDelete(RMcamera2D * c)196 rmCamera2DDelete (RMcamera2D *c)
197 {
198     if (c != NULL)
199         free(c);
200 }
201 
202 
203 /*
204  * ----------------------------------------------------
205  * @Name rmDefaultCamera2D
206  @pstart
207  RMenum rmDefaultCamera2D (RMcamera2D *toModify)
208  @pend
209 
210  @astart
211  RMcamera2D *toModify - a handle to a caller supplied RMcamera2D
212     object that will be modified by this routine (modified).
213  @aend
214 
215  @dstart
216 
217  This routine assigns a set of default parameters to an RMcamera2D
218  object.  All existing settings of the input RMcamera2D object will be
219  overwritten.  Returns RM_CHILL upon success, or RM_WHACKED upon
220  failure.
221 
222  The new values written by this routine are:
223 
224  1. 2D extents set to: (-1,-1)..(1,1)
225 
226  2. Aspect ratio set to 1.0.
227 
228  @dend
229  * ----------------------------------------------------
230  */
231 RMenum
rmDefaultCamera2D(RMcamera2D * c)232 rmDefaultCamera2D (RMcamera2D *c)
233 {
234     if (RM_ASSERT(c, "rmDefaultCamera2D error: the input RMcamera2D object is NULL") == RM_WHACKED)
235 	return(RM_WHACKED);
236 
237     rmCamera2DSetExtents(c, RM_DEFAULT_2DCAMERA_XMIN, RM_DEFAULT_2DCAMERA_YMIN, RM_DEFAULT_2DCAMERA_XMAX, RM_DEFAULT_2DCAMERA_YMAX);
238     rmCamera2DSetAspectRatio(c, RM_DEFAULT_2DCAMERA_ASPECT);
239 
240     return(RM_CHILL);
241 }
242 
243 
244 /*
245  * ----------------------------------------------------
246  * @Name rmCamera2DSetAspectRatio
247  @pstart
248  RMenum rmCamera2DSetAspectRatio (RMcamera2D *toModify,
249 			          float newAspect)
250 
251  @pend
252 
253  @astart
254  RMcamera2D *toModify - a handle to the RMcamera2D object that will be
255     modified by this routine.
256 
257  float newAspect - a floating point value specifying the new aspect
258     ratio for the RMcamera2D object.
259  @aend
260 
261  @dstart
262 
263  Use this routine to set the aspect ratio of a 2D camera. Returns
264  RM_CHILL upon success, or RM_WHACKED upon failure.
265 
266  The aspect ratio is a fraction that is the width of the field of view
267  divided by the height. It is used to control the stretch and squish
268  of rendering. For example, consider rendering into a window whose
269  width is 400 pixels and height is 300 pixels and using a logically
270  square viewport (0,0..1,1). If the camera aspect ratio is set to 1.0,
271  the resulting image will appear to be stretched along the horizontal
272  axis because the square viewport is being mapped to a rectangular
273  window.
274 
275  In this example, we would want to set the aspect ratio to 400/300 to
276  avoid stretching. The aspect ratio is applied at the stage when we
277  compute the view transformation matrix.
278 
279  @dend
280  * ----------------------------------------------------
281  */
282 RMenum
rmCamera2DSetAspectRatio(RMcamera2D * toModify,float newAspect)283 rmCamera2DSetAspectRatio (RMcamera2D *toModify,
284 			  float newAspect)
285 {
286     if (RM_ASSERT(toModify, "rmCamera2DSetAspectRatio error: the input RMcamera2D object is NULL") == RM_WHACKED)
287 	return(RM_WHACKED);
288 
289     toModify->aspect_ratio = newAspect;
290 
291     return(RM_CHILL);
292 }
293 
294 
295 /*
296  * ----------------------------------------------------
297  * @Name rmCamera2DResetAspectRatio
298  @pstart
299  RMenum rmCamera2DResetAspectRatio (RMcamera2D *toModify,
300 			            const float *vp,
301 			            int windowWidth,
302 				    int windowWeight)
303  @pend
304 
305  @astart
306  RMcamera2D *toModify - a handle to the RMcamera2D that will be
307     modified by this routine.
308 
309  const float *vp - an array of floating point values of length 4. This
310     is a viewport specification, ordered {xmin, ymin, xmax, ymax}. The
311     viewport coordinates are in the range 0..1, ranging from the lower
312     left corner of the display window (0,0) to the upper right corner
313     (1,1).
314 
315  int windowWidth, windowWeight - integer values specifying the size in
316     pixels of the display window. These values are used in computing a
317     new aspect ratio for the RMcamera2D object.
318  @aend
319 
320  @dstart
321 
322  Given viewport and window geometry, compute an aspect ratio that will
323  cause pixels to "remain square." In other words, an aspect ratio will
324  be computed so that there is no stretch or squish in the rendered
325  image: a square in world coordinates will remain square in the
326  rendered image, regardless of viewport or window geometry.
327 
328  Returns RM_CHILL upon success, or RM_WHACKED upon failure.
329 
330  @dend
331  * ----------------------------------------------------
332  */
333 RMenum
rmCamera2DResetAspectRatio(RMcamera2D * toModify,const float vp[4],int windowWidth,int windowHeight)334 rmCamera2DResetAspectRatio (RMcamera2D *toModify,
335 			    const float vp[4],
336 			    int windowWidth,
337 			    int windowHeight)
338 {
339     float sx, sy, new_aspect;
340 
341     if ((RM_ASSERT(toModify, "rmCamera2DResetAspectRatio error: the input RMcamera2D error is NULL") == RM_WHACKED) ||
342 	(RM_ASSERT(vp, "rmCamera2DResetAspectRatio error: the input viewport is NULL")) == RM_WHACKED)
343 	return(RM_WHACKED);
344 
345     sx = vp[2] - vp[0];
346     sy = vp[3] - vp[1];
347 
348     new_aspect = ((float)(windowWidth) * sx) / ((float)(windowHeight) * sy);
349     rmCamera2DSetAspectRatio(toModify, new_aspect);
350 
351     return(RM_CHILL);
352 }
353 
354 
355 /*
356  * ----------------------------------------------------
357  * @Name rmCamera2DGetAspectRatio
358  @pstart
359  RMenum rmCamera2DGetAspectRatio (const RMcamera2D *toQuery,
360 			          float *retValue)
361  @pend
362 
363  @astart
364  const RMcamera2D *toQuery - a handle to the RMcamera2D object that
365     will be queried by this routine.
366 
367  float *retValue - a handle to a caller-supplied float. The
368     RMcamera2D's aspect ratio attribute will be copied into this
369     memory location.
370  @aend
371 
372  @dstart
373 
374  Use this routine to query the aspect ratio of an RMcamera2D object.
375  RM_CHILL is returned upon success, otherwise RM_WHACKED is returned.
376 
377  The aspect ratio is a fraction that is the width of the field of view
378  divided by the height. It is used to control the stretch and squish
379  of rendering. For example, consider rendering into a window whose
380  width is 400 pixels and height is 300 pixels and using a logically
381  square viewport (0,0..1,1). If the camera aspect ratio is set to 1.0,
382  the resulting image will appear to be stretched along the horizontal
383  axis because the square viewport is being mapped to a rectangular
384  window.
385 
386  In this example, we would want to set the aspect ratio to 400/300 to
387  avoid stretching. The aspect ratio is applied at the stage when we
388  compute the view transformation matrix.
389 
390  @dend
391  * ----------------------------------------------------
392  */
393 RMenum
rmCamera2DGetAspectRatio(const RMcamera2D * toQuery,float * retValue)394 rmCamera2DGetAspectRatio (const RMcamera2D *toQuery,
395 			  float *retValue)
396 {
397     if ((RM_ASSERT(toQuery, "rmCamera2DGetAspectRatio error: the input RMcamera2D object is NULL") == RM_WHACKED) ||
398 	(RM_ASSERT(retValue, "rmCamera2DGetAspectRatio error: the return float pointer is NULL.") == RM_WHACKED))
399 	return(RM_WHACKED);
400 
401     *retValue = toQuery->aspect_ratio;
402 
403     return(RM_CHILL);
404 }
405 
406 
407 /*
408  * ----------------------------------------------------
409  * @Name rmCamera2DSetExtents
410  @pstart
411  RMenum rmCamera2DSetExtents (RMcamera2D *toModify,
412 		              float xmin,
413 			      float ymin,
414 			      float xmax,
415 			      float ymax)
416 
417  @pend
418 
419  @astart
420  RMcamera2D *toModify - a handle to an RMcamera2D object that will be
421     modified by this routine.
422 
423  float xmin, ymin, xmax, ymax - floating point values that specify the
424     new x/y extents "seen" by the 2D camera. These values are
425     specified in world coordinates.
426  @aend
427 
428  @dstart
429 
430  Use this routine to specify the extents for a 2D camera. Returns
431  RM_CHILL upon success, or RM_WHACKED upon failure.
432 
433  A 2D camera is composed of two parameters in OpenRM: an extents and
434  an aspect ratio. The extents defines a 2D region in world coordinates
435  that will be seen by the 2D camera, these extents are specified with
436  a minimum and maximum coordinate. The aspect ratio defines the
437  relationship between the camera width and height and controls the
438  stretch and squish of rendering, something that is useful if your
439  window is not square.
440 
441  @dend
442  * ----------------------------------------------------
443  */
444 RMenum
rmCamera2DSetExtents(RMcamera2D * c,float xmin,float ymin,float xmax,float ymax)445 rmCamera2DSetExtents (RMcamera2D *c,
446 		      float xmin,
447 		      float ymin,
448 		      float xmax,
449 		      float ymax)
450 {
451     if (RM_ASSERT(c, "rmCamera2DSetExtents error: the input RMcamera2D object is NULL") == RM_WHACKED)
452 	return(RM_WHACKED);
453 
454     c->xmin = xmin;
455     c->ymin = ymin;
456     c->xmax = xmax;
457     c->ymax = ymax;
458 
459     return(RM_CHILL);
460 }
461 
462 
463 /*
464  * ----------------------------------------------------
465  * @Name rmCamera2DGetExtents
466  @pstart
467  RMenum rmCamera2DGetExtents (const RMcamera2D *toQuery,
468 		              float *xmin,
469 			      float *ymin,
470 			      float *xmax,
471 			      float *ymax)
472 
473  @pend
474 
475  @astart
476  const RMcamera2D *toQuery - a handle to an RMcamera2D object that
477     will be queried by this routine.
478 
479  float *xmin, *ymin, *xmax, *ymax - pointers to caller-supplied
480     floats. The 2D cameras extents parameters will be written into
481     these memory locations.  It is permissible to specify NULL for any
482     or all of these parameters - the corresponding 2D camera extents
483     parameter will not be reported for NULL return parameters.
484  @aend
485 
486  @dstart
487 
488  Use this routine to query the extents of a 2D camera. Returns
489  RM_CHILL upon success, or RM_WHACKED upon failure.
490 
491  A 2D camera is composed of two parameters in OpenRM: an extents and
492  an aspect ratio. The extents defines a 2D region in world coordinates
493  that will be seen by the 2D camera, these extents are specified with
494  a minimum and maximum coordinate. The aspect ratio defines the
495  relationship between the camera width and height and controls the
496  stretch and squish of rendering, something that is useful if your
497  window is not square.
498 
499  @dend
500  * ----------------------------------------------------
501  */
502 RMenum
rmCamera2DGetExtents(const RMcamera2D * c,float * xmin,float * ymin,float * xmax,float * ymax)503 rmCamera2DGetExtents (const RMcamera2D *c,
504 		      float *xmin,
505 		      float *ymin,
506 		      float *xmax,
507 		      float *ymax)
508 {
509     if (RM_ASSERT(c, "rmCamera2DGetExtents error: the input RMcamera2D object is NULL") == RM_WHACKED)
510 	return(RM_WHACKED);
511 
512     if (xmin != NULL)
513 	*xmin = c->xmin;
514 
515     if (ymin != NULL)
516 	*ymin = c->ymin;
517 
518     if (xmax != NULL)
519 	*xmax = c->xmax;
520 
521     if (ymax != NULL)
522 	*ymax = c->ymax;
523 
524     return(RM_CHILL);
525 }
526 
527 
528 /*
529  * ----------------------------------------------------
530  * @Name rmCamera2DComputeViewMatrix
531  @pstart
532  RMenum rmCamera2DComputeViewMatrix (const RMcamera2D *camera,
533 			             RMmatrix *matrixReturn)
534  @pend
535 
536  @astart
537  const RMcamera2D *camera - a handle to an RMcamera2D object (input).
538 
539  RMmatrix *matrixReturn - a handle to a caller-supplied RMmatrix
540     object (result).
541  @aend
542 
543  @dstart
544 
545  Computes the view matrix defined by the 2D input camera, copying the
546  result into caller supplied memory. Upon success, RM_CHILL is
547  returned. Upon failure, RM_WHACKED is returned, and matrixReturn
548  remains undisturbed.
549 
550  Note that the 2D camera does not produce a projection matrix, unlike
551  the 3D camera.
552 
553  @dend
554  * ----------------------------------------------------
555  */
556 RMenum
rmCamera2DComputeViewMatrix(const RMcamera2D * c,RMmatrix * m)557 rmCamera2DComputeViewMatrix (const RMcamera2D *c,
558 			     RMmatrix *m)
559 {
560     if ((RM_ASSERT(c, "rmCamera2DComputeViewMatrix error: the input RMcamera2D object is NULL") == RM_WHACKED) ||
561 	(RM_ASSERT(m, "rmCamera2DComputeViewMatrix error: the return view RMmatrix object is NULL") == RM_WHACKED))
562 	return(RM_WHACKED);
563 
564     private_rmCamera2DComputeMatrix(c, m);
565 
566     return(RM_CHILL);
567 }
568 
569 
570 /*
571  * ----------------------------------------------------
572  * @Name rmCamera2DComputeViewFromGeometry
573  @pstart
574  RMenum rmCamera2DComputeViewFromGeometry (RMcamera2D *toModify,
575 				           const RMnode *source)
576  @pend
577 
578  @astart
579  RMcamera2D *toModify - a handle to an RMcamera2D object that will be
580     modified by this routine.
581 
582  const RMnode *source - a handle to an RMnode.
583  @aend
584 
585  @dstart
586 
587  Use this routine to automatically compute 2D view parameters from the
588  2D bounding box of scene graph node. Returns RM_CHILL upon success,
589  or RM_WHACKED upon failure.
590 
591  The 2D camera's parameters will be set to reflect the min/max extents
592  of the input RMnode's 2D bounding box. The aspect ratio of the 2D
593  camera remains unmodified.
594 
595  @dend
596  * ----------------------------------------------------
597  */
598 RMenum
rmCamera2DComputeViewFromGeometry(RMcamera2D * c,const RMnode * r)599 rmCamera2DComputeViewFromGeometry (RMcamera2D *c,
600 				   const RMnode *r)
601 {
602     RMvertex3D bmin, bmax;
603 
604     if ((RM_ASSERT(c, "rmCamera2DComputeViewFromGeometry error: the input RMcamera2D object is NULL") == RM_WHACKED) ||
605 	(RM_ASSERT(r, "rmCamera2DComputeViewFromGeometry error: the input RMnode is NULL") == RM_WHACKED))
606 	return(RM_WHACKED);
607 
608     rmNodeGetBoundingBox (r, &bmin, &bmax);
609     rmCamera2DSetExtents(c, bmin.x, bmin.y, bmax.x, bmax.y);
610 
611     return(RM_CHILL);
612 }
613 
614 
615 /*
616  * ----------------------------------------------------
617  * @Name rmCamera3DNew
618  @pstart
619  RMcamera3D * rmCamera3DNew (void)
620  @pend
621 
622  @astart
623  No arguments.
624  @aend
625 
626  @dstart
627 
628  This routine creates a new RMcamera3D object, initializes it to some
629  default values, and returns a handle to the caller. If there is an error
630  (malloc failure), an error message is displayed with rmError and NULL is
631  returned.
632 
633  For initialization, rmDefaultCamera3D is applied to the newly created
634  RMcamera3D object so that all fields are initialized. See the description
635  of rmDefaultCamera3D for particulars about the default values.
636 
637  @dend
638  * ----------------------------------------------------
639  */
640 RMcamera3D *
rmCamera3DNew(void)641 rmCamera3DNew (void)
642 {
643     RMcamera3D  *c;
644 
645     if ((c = (RMcamera3D *)malloc(sizeof(RMcamera3D))) == NULL)
646     {
647 	rmError("rmCamera3DNew() error: unable to malloc memory for a new RMcamera3D struct. ");
648 	return(NULL);
649     }
650 
651     /* defaults */
652     rmDefaultCamera3D(c);
653 
654     return(c);
655 }
656 
657 
658 /*
659  * ----------------------------------------------------
660  * @Name rmCamera3DCopy
661  @pstart
662  RMenum rmCamera3DCopy (RMcamera3D *dst,
663 	                const RMcamera3D *src)
664  @pend
665 
666  @astart
667  RMcamera3D *dst - a handle to an RMcamera3D object to be modified
668     (output).
669 
670  const RMcamera3D *src - a handle to an RMcamera3D object that will be
671     copied (input).
672  @aend
673 
674  @dstart
675 
676  Copies all parameters and attributes from one RMcamera3D object to
677  another. Returns RM_CHILL upon success, or RM_WHACKED upon failure.
678 
679  @dend
680  * ----------------------------------------------------
681  */
682 RMenum
rmCamera3DCopy(RMcamera3D * dst,const RMcamera3D * src)683 rmCamera3DCopy (RMcamera3D *dst,
684 	        const RMcamera3D *src)
685 {
686     if ((RM_ASSERT(dst, "rmCamera3DCopy error: the dst RMcamera3D pointer is NULL") == RM_WHACKED) ||
687 	(RM_ASSERT(src, "rmCamera3DCopy error: the src RMcamera3D pointer is NULL") == RM_WHACKED))
688 	return(RM_WHACKED);
689 
690     memcpy((void *)dst, (void *)src, sizeof(RMcamera3D));
691 
692     return(RM_CHILL);
693 }
694 
695 
696 /*
697  * ----------------------------------------------------
698  * @Name rmCamera3DDelete
699  @pstart
700  void rmCamera3DDelete (RMcamera3D *toDelete)
701  @pend
702 
703  @astart
704  RMcamera3D *toDelete - a handle to RMcamera3D object to delete (modified).
705  @aend
706 
707  @dstart
708 
709  Free the RMcamera3D resources pointed to by c.  It is assumed that c
710  points to a valid RMcamera3D previously created with rmCamera3DNew().
711 
712  @dend
713  * ----------------------------------------------------
714  */
715 void
rmCamera3DDelete(RMcamera3D * c)716 rmCamera3DDelete (RMcamera3D *c)
717 {
718     if (c != NULL)
719         free((void *)c);
720 }
721 
722 
723 /*
724  * ----------------------------------------------------
725  * @Name rmDefaultCamera3D
726  @pstart
727  RMenum rmDefaultCamera3D (RMcamera3D *toModify)
728 
729  @pend
730 
731  @astart
732  RMcamera3D *toModify - a handle to an RMcamera3D object that will be
733     modified by this routine.
734  @aend
735 
736  @dstart
737 
738  Assigns some default parameters to an RMcamera3D object. Returns
739  RM_CHILL upon success, or RM_WHACKED upon failure.
740 
741  The parameters set by this routine are:
742 
743  1. Eye point set to (10,10,50). (rmCamera3DSetEye)
744 
745  2. Lookat point is the origin, (0,0,0). (rmCamera3DSetAt)
746 
747  3. Up vector is (0,1,0) (rmCamera3DSetUp)
748 
749  4. Field of view set to 45 degrees (rmCamera3DSetFOV)
750 
751  5. Monoscopic camera (rmCamera3DSetStereo)
752 
753  6. Perspective projection (rmCamera3DSetProjection)
754 
755  7. Front and back clipping planes set to 2.0 and 40.0, respectively.
756     (rmCamera3DSetHither, rmCamera3DSetYon)
757 
758  Returns RM_CHILL upon success, or RM_WHACKED if the input RMcamera3D
759  object is NULL.
760 
761  @dend
762  * ----------------------------------------------------
763  */
764 RMenum
rmDefaultCamera3D(RMcamera3D * c)765 rmDefaultCamera3D (RMcamera3D *c)
766 {
767     if (RM_ASSERT(c, "rmDefaultCamera3D error: the input RMcamera3D object is NULL") == RM_WHACKED)
768 	return(RM_WHACKED);
769 
770     /*
771      * assign some default values:
772      * the camera's default position has the
773      * - eye at (10,10,50)
774      * - lookat point is the origin (0,0,0)
775      * - up vector is +y axis
776      * - hither is 2 and yon is 40
777      * - perspective projection
778      * - monoscopic
779      */
780     rmCamera3DSetFOV(c, RM_DEFAULT_3DCAMERA_FOV);
781     rmCamera3DSetAspectRatio(c, RM_DEFAULT_3DCAMERA_ASPECT);
782 
783     rmCamera3DSetEye(c, &RM_DEFAULT_3DCAMERA_EYE);
784     rmCamera3DSetAt(c, &RM_DEFAULT_3DCAMERA_LOOKAT);
785     rmCamera3DSetUpVector(c, &RM_DEFAULT_3DCAMERA_UP);
786 
787     rmCamera3DSetHither(c, RM_DEFAULT_3DCAMERA_HITHER);
788     rmCamera3DSetYon(c, RM_DEFAULT_3DCAMERA_YON);
789     rmCamera3DSetProjection(c, RM_DEFAULT_3DCAMERA_PROJECTION);
790     rmCamera3DSetStereo(c, RM_FALSE);
791     rmCamera3DSetEyeSeparation (c, 0.0F);
792     rmCamera3DSetFocalDistance (c, 1.0F);
793 
794     return(RM_CHILL);
795 }
796 
797 
798 /*
799  * ----------------------------------------------------
800  * @Name rmCamera3DSetAspectRatio
801  @pstart
802  RMenum rmCamera3DSetAspectRatio (RMcamera3D *toModify,
803 		                  const float newAspect)
804 
805  @pend
806 
807  @astart
808  RMcamera3D *toModify - a handle to an RMcamera3D object (modified).
809 
810  const float newAspect - a floating point value used to specify the
811     new aspect ratio for the RMcamera3D object.
812  @aend
813 
814  @dstart
815 
816  Use this routine to modify the aspect ratio attribute of an
817  RMcamera3D object. Returns RM_CHILL upon success, or RM_WHACKED upon
818  failure.
819 
820  @dend
821  * ----------------------------------------------------
822  */
823 RMenum
rmCamera3DSetAspectRatio(RMcamera3D * toModify,const float newAspect)824 rmCamera3DSetAspectRatio (RMcamera3D *toModify,
825 		          const float newAspect)
826 {
827     if (RM_ASSERT(toModify, "rmCamera3DSetAspectRatio error: the input RMcamera3D object is NULL") == RM_WHACKED)
828 	return(RM_WHACKED);
829 
830     toModify->aspect = newAspect;
831 
832     return(RM_CHILL);
833 }
834 
835 
836 /*
837  * ----------------------------------------------------
838  * @Name rmCamera3DResetAspectRatio
839  @pstart
840  RMenum rmCamera3DResetAspectRatio (RMcamera3D *toModify,
841 			            const float *vp,
842 				    int windowWidth,
843 				    int windowHeight)
844 
845  @pend
846 
847  @astart
848  RMcamera3D *toModify - a handle to the RMcamera3D object that will be
849     modified by this routine.
850 
851  const float *vp - an array of floating point values of length 4. This
852     is a viewport specification, ordered {xmin, ymin, xmax, ymax}. The
853     viewport coordinates are in the range 0..1, ranging from the lower
854     left corner of the display window (0,0) to the upper right corner
855     (1,1).
856 
857  int windowWidth, windowWeight - integer values specifying the size in
858     pixels of the display window. These values are used in computing a
859     new aspect ratio for the RMcamera3D object.
860  @aend
861 
862  @dstart
863 
864  Given viewport and window geometry, compute an aspect ratio that will
865  cause pixels to "remain square." In other words, an aspect ratio will
866  be computed so that there is no stretch or squish in the rendered
867  image: a square in world coordinates will remain square in the
868  rendered image, regardless of viewport or window geometry.
869 
870  All other camera parameters are ignored and remain unaffected.
871 
872  Returns RM_CHILL upon success, or RM_WHACKED upon failure.
873 
874  @dend
875  * ----------------------------------------------------
876  */
877 RMenum
rmCamera3DResetAspectRatio(RMcamera3D * toModify,const float * vp,int windowWidth,int windowHeight)878 rmCamera3DResetAspectRatio (RMcamera3D *toModify,
879 			    const float *vp,
880 			    int windowWidth,
881 			    int windowHeight)
882 {
883     float sx, sy;
884     float new_aspect;
885 
886     if ((RM_ASSERT(toModify, "rmCamera3DResetAspectRatio error: input RMcamera3D object is NULL.") == RM_WHACKED) ||
887 	(RM_ASSERT(vp, "rmCamera3DResetAspectRatio error: input viewport is NULL.") == RM_WHACKED))
888 	return(RM_WHACKED);
889 
890     sx = vp[2] - vp[0];
891     sy = vp[3] - vp[1];
892 
893     new_aspect = ((float)windowWidth * sx) / ((float)windowHeight * sy);
894     rmCamera3DSetAspectRatio(toModify, new_aspect);
895 
896     return(RM_CHILL);
897 }
898 
899 
900 /*
901  * ----------------------------------------------------
902  * @Name rmCamera3DGetAspectRatio
903  @pstart
904  float rmCamera3DGetAspectRatio (const RMcamera3D *toQuery)
905  @pend
906 
907  @astart
908  const RMcamera3D *toQuery - a handle to the RMcamera3D object that
909     will be queried by this routine.
910  @aend
911 
912  @dstart
913 
914  Use this routine to query the aspect ratio of an RMcamera3D object.
915  RM_CHILL is returned upon success, otherwise RM_WHACKED is returned.
916 
917  The aspect ratio is a fraction that is the width of the field of view
918  divided by the height. It is used to control the stretch and squish
919  of rendering. For example, consider rendering into a window whose
920  width is 400 pixels and height is 300 pixels and using a logically
921  square viewport (0,0..1,1). If the camera aspect ratio is set to 1.0,
922  the resulting image will appear to be stretched along the horizontal
923  axis because the square viewport is being mapped to a rectangular
924  window.
925 
926  In this example, we would want to set the aspect ratio to 400/300 to
927  avoid stretching. The aspect ratio is applied at the stage when we
928  compute the view transformation matrix.
929 
930  @dend
931  * ----------------------------------------------------
932  */
933 float
rmCamera3DGetAspectRatio(const RMcamera3D * toQuery)934 rmCamera3DGetAspectRatio (const RMcamera3D *toQuery)
935 {
936     if (RM_ASSERT(toQuery, "rmCamera3DGetAspectRatio error: input RMcamera3D pointer is NULL. returning 1.0") == RM_WHACKED)
937 	return(1.0F);
938 
939     return(toQuery->aspect);
940 
941 }
942 
943 
944 /*
945  * ----------------------------------------------------
946  * @Name rmCamera3DSetProjection
947  @pstart
948  RMenum rmCamera3DSetProjection (RMcamera3D *toModify,
949                                  RMenum newVal)
950  @pend
951 
952  @astart
953  RMcamera3D *toModify - a handle to an RMcamera3D object that will be
954     modified by this routine (modified).
955 
956  RMenum newVal - an RMenum value specifying the new projection method
957     for the RMcamera3D object. Should be either
958     RM_PROJECTION_PERSPECTIVE or RM_PROJECTION_ORTHOGRAPHIC.
959  @aend
960 
961  @dstart
962 
963  Use this routine to assign a projection method to a 3D
964  camera. Returns RM_CHILL upon success, or RM_WHACKED upon failure.
965 
966  Perspective projections are used to achieve a "perspective
967  foreshortening" effect. This is how humans see in the real world:
968  objects that are further away appear smaller than similarly-sized
969  objects that are closer.  In contrast, in orthographics projections
970  size does not diminish as a function of distance to the viewer. This
971  type of projection is often used in CAD systems because it does not
972  distort angles.
973 
974  @dend
975  * ----------------------------------------------------
976  */
977 RMenum
rmCamera3DSetProjection(RMcamera3D * toModify,RMenum newVal)978 rmCamera3DSetProjection (RMcamera3D *toModify,
979 			 RMenum newVal)
980 {
981     if (RM_ASSERT(toModify, "rmCamera3DSetProjection error: input camera is NULL") == RM_WHACKED)
982 	return(RM_WHACKED);
983 
984     if ((newVal != RM_PROJECTION_PERSPECTIVE) && (newVal != RM_PROJECTION_ORTHOGRAPHIC))
985         return(RM_WHACKED);
986 
987     toModify->projection = newVal;
988 
989     return(RM_CHILL);
990 }
991 
992 
993 /*
994  * ----------------------------------------------------
995  * @Name rmCamera3DGetProjection
996  @pstart
997  RMenum rmCamera3DGetProjection (const RMcamera3D *toQuery)
998  @pend
999 
1000  @astart
1001 
1002  const RMcamera3D *toQuery - a handle to an RMcamera3D object that
1003     will be queried by this routine (input).
1004  @aend
1005 
1006  @dstart
1007 
1008  Use this routine to obtain the projection method attribute of the
1009  RMcamera3D object. If successful, either RM_PROJECTION_PERSPECTIVE or
1010  RM_PROJECTION_ORTHOGRAPHIC will be returned to the caller. Otherwise,
1011  upon failure, RM_WHACKED will be returned.
1012 
1013  @dend
1014  * ----------------------------------------------------
1015  */
1016 RMenum
rmCamera3DGetProjection(const RMcamera3D * toQuery)1017 rmCamera3DGetProjection (const RMcamera3D *toQuery)
1018 {
1019     if (RM_ASSERT(toQuery, "rmCamera3DGetProjection error: input camera is NULL") == RM_WHACKED)
1020 	return(RM_WHACKED);
1021 
1022     return(toQuery->projection);
1023 }
1024 
1025 
1026 /*
1027  * ----------------------------------------------------
1028  * @Name rmCamera3DSetEye
1029  @pstart
1030  RMenum rmCamera3DSetEye (RMcamera3D *toModify,
1031 		          const RMvertex3D *newEye)
1032 
1033  @pend
1034 
1035  @astart
1036  RMcamera3D *toModify - a handle to the RMcamera3D object that will be
1037     modified by this routine.
1038 
1039  const RMvertex3D *newEye - a handle to an RMvertex3D object.
1040  @aend
1041 
1042  @dstart
1043 
1044  Use this routine to specify a new eye point for a 3D camera. The eye
1045  point is the location of the viewer, and is specified in world
1046  coordinates.  The new eye point is copied from the caller-supplied
1047  RMvertex3D object into the RMcamera3D object.
1048 
1049  Returns RM_CHILL upon success, or RM_WHACKED upon failure.
1050 
1051  @dend
1052  * ----------------------------------------------------
1053  */
1054 RMenum
rmCamera3DSetEye(RMcamera3D * toModify,const RMvertex3D * newEye)1055 rmCamera3DSetEye (RMcamera3D *toModify,
1056 		  const RMvertex3D *newEye)
1057 {
1058     if ((RM_ASSERT(toModify, "rmCamera3DSetEye error: input camera is null.") == RM_WHACKED) ||
1059 	(RM_ASSERT(newEye, "rmCamera3DSetEye error: input RMvertex3D pointer is null.") == RM_WHACKED))
1060 	return(RM_WHACKED);
1061 
1062     VCOPY(newEye, &(toModify->eye));
1063 
1064     return(RM_CHILL);
1065 }
1066 
1067 
1068 /*
1069  * ----------------------------------------------------
1070  * @Name rmCamera3DGetEye
1071  @pstart
1072  RMenum rmCamera3DGetEye (const RMcamera3D *toQuery,
1073 		          RMvertex3D *returnEye)
1074 
1075  @pend
1076 
1077  @astart
1078 
1079  const RMcamera3D *toQuery - a handle to an RMcamera3D object to be
1080     queried (input).
1081 
1082  RMvertex3D *returnEye - a handle to a caller-supplied RMvertex3D
1083     object (modified).
1084  @aend
1085 
1086  @dstart
1087 
1088  Use this routine to obtain the 3D eye position of an RMcamera3D
1089  object.
1090 
1091  Returns RM_CHILL upon success, or RM_WHACKED upon failure.
1092 
1093  @dend
1094  * ----------------------------------------------------
1095  */
1096 RMenum
rmCamera3DGetEye(const RMcamera3D * toQuery,RMvertex3D * returnEye)1097 rmCamera3DGetEye (const RMcamera3D *toQuery,
1098 		  RMvertex3D *returnEye)
1099 {
1100     if ((RM_ASSERT(toQuery, "rmCamera3DGetEye error: input camera is null.") == RM_WHACKED) ||
1101 	(RM_ASSERT(returnEye, "rmCamera3DGetEye error: input RMvertex3D pointer is null.") == RM_WHACKED))
1102 	return(RM_WHACKED);
1103 
1104     VCOPY(&(toQuery->eye), returnEye);
1105 
1106     return(RM_CHILL);
1107 }
1108 
1109 
1110 /*
1111  * ----------------------------------------------------
1112  * @Name rmCamera3DSetAt
1113  @pstart
1114  RMenum rmCamera3DSetAt (RMcamera3D *toModify,
1115 		         const RMvertex3D *newAt)
1116 
1117  @pend
1118 
1119  @astart
1120  RMcamera3D *toModify - a handle to an RMcamera3D object (modified).
1121 
1122  const RMvertex3D *newAt - a handle to an RMvertex3D object containing
1123     the new look-at point (input).
1124  @aend
1125 
1126  @dstart
1127 
1128  Use this routine to set the look-at point of a 3D camera. The
1129  contents of the caller-supplied newAt RMvertex3D object is copied
1130  into the RMcamera3D object's look-at point attribute.
1131 
1132  Returns RM_CHILL upon success, or RM_WHACKED upon failure.
1133 
1134  @dend
1135  * ----------------------------------------------------
1136  */
1137 RMenum
rmCamera3DSetAt(RMcamera3D * toModify,const RMvertex3D * newAt)1138 rmCamera3DSetAt (RMcamera3D *toModify,
1139 		 const RMvertex3D *newAt)
1140 {
1141     if ((RM_ASSERT(toModify, "rmCamera3DSetAt error: input camera is null.") == RM_WHACKED) ||
1142 	(RM_ASSERT(newAt, "rmCamera3DSetAt error: input RMvertex3D pointer is null.") == RM_WHACKED))
1143 	return(RM_WHACKED);
1144 
1145     VCOPY(newAt, &(toModify->at));
1146 
1147     return(RM_CHILL);
1148 }
1149 
1150 
1151 /*
1152  * ----------------------------------------------------
1153  * @Name rmCamera3DGetAt
1154  @pstart
1155  RMenum rmCamera3DGetAt (const RMcamera3D *toQuery,
1156 		         RMvertex3D *returnAt)
1157 
1158  @pend
1159 
1160  @astart
1161  const RMcamera3D *toQuery - a handle to an RMcamera3D object to be
1162     queried (input).
1163 
1164  RMvertex3D *returnAt - a handle to an RMvertex3D object that will
1165     contain the RMcamera3D's look-at point upon return (modified).
1166 
1167  @aend
1168 
1169  @dstart
1170 
1171  Use this routine to obtain the look-at point of a 3D camera. The
1172  RMcamera3D's look-at point is copied into the caller-supplied object.
1173 
1174  Returns RM_CHILL upon success, or RM_WHACKED upon failure.
1175 
1176  @dend
1177  * ----------------------------------------------------
1178  */
1179 RMenum
rmCamera3DGetAt(const RMcamera3D * toQuery,RMvertex3D * returnAt)1180 rmCamera3DGetAt (const RMcamera3D *toQuery,
1181 		 RMvertex3D *returnAt)
1182 {
1183     if ((RM_ASSERT(toQuery, "rmCamera3DGetAt error: input camera is null.") == RM_WHACKED) ||
1184 	(RM_ASSERT(returnAt, "rmCamera3DGetAt error: input RMvertex3D pointer is null.") == RM_WHACKED))
1185 	return(RM_WHACKED);
1186 
1187     VCOPY(&(toQuery->at), returnAt);
1188 
1189     return(RM_CHILL);
1190 }
1191 
1192 
1193 /*
1194  * ----------------------------------------------------
1195  * @Name rmCamera3DSetUpVector
1196  @pstart
1197  RMenum rmCamera3DSetUpVector (RMcamera3D *toModify,
1198 		               const RMvertex3D *newUpVector)
1199 
1200  @pend
1201 
1202  @astart
1203  RMcamera3D *toModify - a handle to the RMcamera3D object to be
1204     modified (result).
1205 
1206  const RMvertex3D *newUpVector - a handle to an RMvertex3D object
1207     supplying the new up vector (input).
1208  @aend
1209 
1210  @dstart
1211 
1212  Use this routine to set the Up vector attribute of an RMcamera3D
1213  object.  The contents of the caller-supplied newUpVector is copied to
1214  the RMcamera3D object's up vector attribute.
1215 
1216  Returns RM_CHILL upon success, or RM_WHACKED upon failure.
1217 
1218  @dend
1219  * ----------------------------------------------------
1220  */
1221 RMenum
rmCamera3DSetUpVector(RMcamera3D * toModify,const RMvertex3D * newUpVector)1222 rmCamera3DSetUpVector (RMcamera3D *toModify,
1223 		       const RMvertex3D *newUpVector)
1224 {
1225     if ((RM_ASSERT(toModify, "rmCamera3DSetUpVector error: input RMcamera3D pointer is NULL.") == RM_WHACKED) ||
1226 	(RM_ASSERT(newUpVector, "rmCamera3DSetUpVector error: input RMvertex3D pointer is NULL") == RM_WHACKED))
1227 	return(RM_WHACKED);
1228 
1229     VCOPY(newUpVector, &(toModify->up));
1230 
1231     return(RM_CHILL);
1232 }
1233 
1234 
1235 /*
1236  * ----------------------------------------------------
1237  * @Name rmCamera3DGetUpVector
1238  @pstart
1239  RMenum rmCamera3DGetUpVector (const RMcamera3D *toQuery,
1240 		               RMvertex3D *returnUpVector)
1241 
1242  @pend
1243 
1244  @astart
1245  const RMcamera3D *toQuery - a handle to the RMcamera3D object to
1246     query (input).
1247 
1248  RMvertex3D *returnUpVector - a handle to an RMvertex3D object
1249     supplied by the caller (modified).
1250  @aend
1251 
1252  @dstart
1253 
1254  Use this routine to query the Up vector attribute of an RMcamera3D
1255  object.  The RMcamera3D object's up vector is copied into the
1256  caller-supplied memory.
1257 
1258  Returns RM_CHILL upon success, or RM_WHACKED upon failure.
1259 
1260  @dend
1261  * ----------------------------------------------------
1262  */
1263 RMenum
rmCamera3DGetUpVector(const RMcamera3D * toQuery,RMvertex3D * returnUpVector)1264 rmCamera3DGetUpVector (const RMcamera3D *toQuery,
1265 		       RMvertex3D *returnUpVector)
1266 {
1267     if ((RM_ASSERT(toQuery, "rmCamera3DGetUpVector error: input RMcamera3D pointer is NULL.") == RM_WHACKED) ||
1268 	(RM_ASSERT(returnUpVector, "rmCamera3DGetUpVector error: input RMvertex3D pointer is NULL") == RM_WHACKED))
1269 	return(RM_WHACKED);
1270 
1271     VCOPY(&(toQuery->up), returnUpVector);
1272 
1273     return(RM_CHILL);
1274 }
1275 
1276 
1277 /*
1278  * ----------------------------------------------------
1279  * @Name rmCamera3DSetFOV
1280  @pstart
1281  RMenum rmCamera3DSetFOV (RMcamera3D *toModify,
1282                           float newFOV)
1283  @pend
1284 
1285  @astart
1286  RMcamera3D *toModify - a handle to an RMcamera3D object that will be
1287     modified by this routine (modified).
1288 
1289  float newFOV - a floating point value, the new FOV parameter (input).
1290  @aend
1291 
1292  @dstart
1293 
1294  Use this routine to set the FOV parameter at a 3D camera object. If
1295  successful, RM_CHILL is returned, otherwise, RM_WHACKED is returned.
1296 
1297  FOV in OpenRM refers to the horizontal field of view, not the
1298  vertical field of view. Increasing the FOV in a 3D camera will cause
1299  more of the scene to be visible along the horizontal axis.
1300 
1301  @dend
1302  * ----------------------------------------------------
1303  */
1304 RMenum
rmCamera3DSetFOV(RMcamera3D * toModify,float newFOV)1305 rmCamera3DSetFOV (RMcamera3D *toModify,
1306 		  float newFOV)
1307 {
1308     if (RM_ASSERT(toModify, "rmCamera3DSetFOV error: input camera is NULL.") == RM_WHACKED)
1309 	return(RM_WHACKED);
1310 
1311     toModify->fov = newFOV;	/* range check?? */
1312 
1313     return(RM_CHILL);
1314 }
1315 
1316 
1317 /*
1318  * ----------------------------------------------------
1319  * @Name rmCamera3DGetFOV
1320  @pstart
1321  float rmCamera3DGetFOV (const RMcamera3D *toQuery)
1322  @pend
1323 
1324  @astart
1325  const RMcamera3D *toQuery - a handle to an RMcamera3D object that
1326     will be queried (input).
1327  @aend
1328 
1329  @dstart
1330 
1331  Use this routine to obtain the FOV parameter from a 3D camera object.
1332  A floating point value is returned to the caller.
1333 
1334  FOV in OpenRM refers to the horizontal field of view, not the
1335  vertical field of view. Increasing the FOV in a 3D camera will cause
1336  more of the scene to be visible along the horizontal axis.
1337 
1338  @dend
1339  * ----------------------------------------------------
1340  */
1341 float
rmCamera3DGetFOV(const RMcamera3D * toQuery)1342 rmCamera3DGetFOV (const RMcamera3D *toQuery)
1343 {
1344     if (RM_ASSERT(toQuery, "rmCamera3DGetFOV error: input camera is NULL. Returning 0.0F.") == RM_WHACKED)
1345 	return(0.0F);
1346 
1347     return(toQuery->fov);
1348 }
1349 
1350 
1351 /*
1352  * ----------------------------------------------------
1353  * @Name rmCamera3DSetHither
1354  @pstart
1355  RMenum rmCamera3DSetHither (RMcamera3D *toModify,
1356                             float newHither)
1357  @pend
1358 
1359  @astart
1360  RMcamera3D *toModify - a handle to the RMcamera3D object (modified).
1361 
1362  float newHither - a floating point value containing the new front
1363     clip plane value (input).
1364  @aend
1365 
1366  @dstart
1367 
1368  Used to set the value of the front clip plane attribute of the
1369  RMcamera3D object. The front clip plane value is a distance, in world
1370  coordinates, from the eye point to the front clip plane.  Returns
1371  RM_CHILL upon success, or RM_WHACKED upon failure.
1372 
1373  @dend
1374  * ----------------------------------------------------
1375  */
1376 RMenum
rmCamera3DSetHither(RMcamera3D * toModify,float newHither)1377 rmCamera3DSetHither (RMcamera3D *toModify,
1378 		     float newHither)
1379 {
1380     if (RM_ASSERT(toModify, "rmCamera3DSetHither error: input camera is NULL") == RM_WHACKED)
1381 	return(RM_WHACKED);
1382 
1383     toModify->hither = newHither;
1384 
1385     return(RM_CHILL);
1386 }
1387 
1388 
1389 /*
1390  * ----------------------------------------------------
1391  * @Name rmCamera3DGetHither
1392  @pstart
1393  float rmCamera3DGetHither (const RMcamera3D *toQuery)
1394  @pend
1395 
1396  @astart
1397  const RMcamera3D *toQuery - a handle to the RMcamera3D object to be
1398     queried (input).
1399  @aend
1400 
1401  @dstart
1402 
1403  Returns to the caller the value of the front clip plane attribute of
1404  the RMcamera3D object. The front clip plane value is a distance, in
1405  world coordinates, from the eye point to the front clip plane.
1406 
1407  @dend
1408  * ----------------------------------------------------
1409  */
1410 float
rmCamera3DGetHither(const RMcamera3D * toQuery)1411 rmCamera3DGetHither (const RMcamera3D *toQuery)
1412 {
1413     if (RM_ASSERT(toQuery, "rmCamera3DGetHither error: input camera is NULL, returning 0.0F") == RM_WHACKED)
1414 	return(0.0F);
1415 
1416     return(toQuery->hither);
1417 }
1418 
1419 
1420 /*
1421  * ----------------------------------------------------
1422  * @Name rmCamera3DSetYon
1423  @pstart
1424  RMenum rmCamera3DSetYon (RMcamera3D *toModify,
1425                           float newYon)
1426  @pend
1427 
1428  @astart
1429  RMcamera3D *toModify - a handle to the RMcamera3D object (modified).
1430 
1431  float newYon - a floating point value containing the new back clip
1432     plane value (input).
1433  @aend
1434 
1435  @dstart
1436 
1437  Used to set the value of the back clip plane attribute of the
1438  RMcamera3D object. The back clip plane value is a distance, in world
1439  coordinates, from the eye point to the back clip plane.  Returns
1440  RM_CHILL upon success, or RM_WHACKED upon failure.
1441 
1442  @dend
1443  * ----------------------------------------------------
1444  */
1445 RMenum
rmCamera3DSetYon(RMcamera3D * toModify,float newYon)1446 rmCamera3DSetYon(RMcamera3D *toModify,
1447 		 float newYon)
1448 {
1449     if (RM_ASSERT(toModify,"rmCamera3DSetYon error: input RMcamera3D pointer is NULL") == RM_WHACKED)
1450 	return(RM_WHACKED);
1451 
1452     toModify->yon = newYon;
1453 
1454     return(RM_CHILL);
1455 }
1456 
1457 
1458 /*
1459  * ----------------------------------------------------
1460  * @Name rmCamera3DGetYon
1461  @pstart
1462  float rmCamera3DGetYon(const RMcamera3D *toQuery)
1463  @pend
1464 
1465  @astart
1466 
1467  const RMcamera3D *toQuery - a handle to the RMcamera3D object to be
1468     queried (input).
1469  @aend
1470 
1471  @dstart
1472 
1473  Returns to the caller the value of the clip plane attribute of the
1474  RMcamera3D object. The back clip plane value is a distance, in world
1475  coordinates, from the eye point to the back clip plane.
1476 
1477  @dend
1478  * ----------------------------------------------------
1479  */
1480 float
rmCamera3DGetYon(const RMcamera3D * toQuery)1481 rmCamera3DGetYon (const RMcamera3D *toQuery)
1482 {
1483     if (RM_ASSERT(toQuery, "rmCamera3DGetYon error: input RMcamera3D pointer is NULL. Returning 0.0F") == RM_WHACKED)
1484 	return(0.0F);
1485 
1486     return(toQuery->yon);
1487 }
1488 
1489 
1490 /*
1491  * ----------------------------------------------------
1492  * @Name rmCamera3DSetStereo
1493  @pstart
1494  RMenum rmCamera3DSetStereo (RMcamera3D *toModify,
1495 		             RMenum newBoolValue)
1496  @pend
1497 
1498  @astart
1499  RMcamera3D *toModify - a handle to an RMcamera3D object (modified).
1500 
1501  RMenum newBoolValue - a boolean value, either RM_TRUE or RM_FALSE
1502     (input).
1503  @aend
1504 
1505  @dstart
1506 
1507  Use this routine to set the stereo attribute of a 3D camera. Returns
1508  RM_CHILL upon success or RM_WHACKED upon failure.
1509 
1510  A stereo 3D camera in OpenRM defines a binocular viewing geometry.
1511  The binocular viewing geometry is a function of eye separation
1512  (rmCamera3DSetEyeSeparation) and focal length
1513  (rmCamera3DSetFocalDistance).
1514 
1515  When the 3D camera is set to be a stereo camera with this routine,
1516  the confluence of multipass rendering control and stereo 3D camera
1517  parameters are used to internally compute left- and right-eye views
1518  used for rendering. If the 3D camera's "is stereo" attribute is set
1519  to RM_FALSE, a monocular view will be used during a stereo multipass
1520  rendering.
1521 
1522  @dend
1523  * ----------------------------------------------------
1524  */
1525 RMenum
rmCamera3DSetStereo(RMcamera3D * c,RMenum rm_bool)1526 rmCamera3DSetStereo (RMcamera3D *c,
1527 		     RMenum rm_bool)
1528 {
1529     if (RM_ASSERT(c, "rmCamera3DSetStereo error: input RMcamera3D object is NULL. ") == RM_WHACKED)
1530 	return(RM_WHACKED);
1531 
1532     if ((rm_bool != RM_TRUE) && (rm_bool != RM_FALSE))
1533 	return(RM_WHACKED);
1534     else
1535 	c->isStereo = rm_bool;
1536 
1537     return(RM_CHILL);
1538 }
1539 
1540 
1541 /*
1542  * ----------------------------------------------------
1543  * @Name rmCamera3DGetStereo
1544  @pstart
1545  RMenum rmCamera3DGetStereo (const RMcamera3D *toQuery)
1546  @pend
1547 
1548  @astart
1549  const RMcamera3D *toQuery - a handle to an RMcamera3D object that
1550     will be queried by this routine (input).
1551  @aend
1552 
1553  @dstart
1554 
1555  Use this routine to obtain the stereo attribute of a 3D
1556  camera. Returns RM_WHACKED upon failure, otherwise returns RM_TRUE or
1557  RM_FALSE.
1558 
1559  A stereo 3D camera in OpenRM defines a binocular viewing geometry.
1560  The binocular viewing geometry is a function of eye separation
1561  (rmCamera3DSetEyeSeparation) and focal length
1562  (rmCamera3DSetFocalDistance).
1563 
1564  When the 3D camera is set to be a stereo camera with this routine,
1565  the confluence of multipass rendering control and stereo 3D camera
1566  parameters are used to internally compute left- and right-eye views
1567  used for rendering. If the 3D camera's "is stereo" attribute is set
1568  to RM_FALSE, a monocular view will be used during a stereo multipass
1569  rendering.
1570 
1571  @dend
1572  * ----------------------------------------------------
1573  */
1574 RMenum
rmCamera3DGetStereo(const RMcamera3D * c)1575 rmCamera3DGetStereo (const RMcamera3D *c)
1576 {
1577     if (RM_ASSERT(c, "rmCamera3DGetStereo error: input RMcamera3D object is NULL. Returning RM_WHACKED.") == RM_WHACKED)
1578 	return(RM_WHACKED);
1579 
1580     return(c->isStereo);
1581 }
1582 
1583 
1584 /*
1585  * ----------------------------------------------------
1586  * @Name rmCamera3DSetEyeSeparation
1587  @pstart
1588  RMenum rmCamera3DSetEyeSeparation (RMcamera3D *toModify, float newVal)
1589  @pend
1590 
1591  @astart
1592  RMcamera3D *toModify - a handle to an RMcamera3D object (modified).
1593 
1594  float newVal - a floating point value specifying the interocular
1595     displacement in degrees (input).
1596  @aend
1597 
1598  @dstart
1599 
1600  Use this routine to modify the interocular separation parameter of a
1601  stereo 3D camera. Returns RM_WHACKED upon failure, otherwise returns
1602  RM_CHILL.
1603 
1604  A stereo 3D camera in OpenRM defines a binocular viewing geometry.
1605  The binocular viewing geometry is a function of eye separation
1606  (rmCamera3DSetEyeSeparation) and focal length
1607  (rmCamera3DSetFocalDistance).
1608 
1609  The stereo camera's binocular geometry, specifically the interocular
1610  distance, is computed using the 3D camera's eye point, the look-at
1611  point, the focal length and the interocular distance. The "real" look
1612  at point of a 3D stereo camera is computed as the product of the
1613  focal length with the (unitized) eye/look-at vector. The left and
1614  right eyes both look at the "real" stereo look-at point (not the one
1615  specified by rmCamera3DSetAt). The apex of the triangle formed by the
1616  left and right eyes and the "real" stereo look at point will have an
1617  angular measure of X degrees, where X is specified as the parameter
1618  to the routine rmCamera3DSetEyeSeparation. We have found that values
1619  in the range 2.5-3.5 degrees work well for most viewers.
1620 
1621  @dend
1622  * ----------------------------------------------------
1623  */
1624 RMenum
rmCamera3DSetEyeSeparation(RMcamera3D * c,float newval)1625 rmCamera3DSetEyeSeparation (RMcamera3D *c,
1626 			    float newval)
1627 {
1628     if (RM_ASSERT(c, "rmCamera3DSetEyeSeparation error: input RMcamera3D points is NULL. ") == RM_WHACKED)
1629 	return(RM_WHACKED);
1630 
1631     c->degrees_eye_separation = newval;
1632 
1633     return(RM_CHILL);
1634 }
1635 
1636 
1637 /*
1638  * ----------------------------------------------------
1639  * @Name rmCamera3DGetEyeSeparation
1640  @pstart
1641  RMenum rmCamera3DGetEyeSeparation (RMcamera3D *toQuery)
1642  @pend
1643 
1644  @astart
1645 
1646  const RMcamera3D *toQuery - a handle to an RMcamera3D object that
1647     will be queried by this routine (input).
1648 
1649  @aend
1650 
1651  @dstart
1652 
1653  Use this routine to query the interocular separation parameter of a
1654  stereo 3D camera. Returns a floating point value representing the 3D
1655  stereo camera's interocular distance parameter.
1656 
1657  A stereo 3D camera in OpenRM defines a binocular viewing geometry.
1658  The binocular viewing geometry is a function of eye separation
1659  (rmCamera3DSetEyeSeparation) and focal length
1660  (rmCamera3DSetFocalDistance).
1661 
1662  The stereo camera's binocular geometry, specifically the interocular
1663  distance, is computed using the 3D camera's eye point, the look-at
1664  point, the focal length and the interocular distance. The "real" look
1665  at point of a 3D stereo camera is computed as the product of the
1666  focal length with the (unitized) eye/look-at vector. The left and
1667  right eyes both look at the "real" stereo look-at point (not the one
1668  specified by rmCamera3DSetAt). The apex of the triangle formed by the
1669  left and right eyes and the "real" stereo look at point will have an
1670  angular measure of X degrees, where X is specified as the parameter
1671  to the routine rmCamera3DSetEyeSeparation. We have found that values
1672  in the range 2.5-3.5 degrees work well for most viewers.
1673 
1674  @dend
1675  * ----------------------------------------------------
1676  */
1677 float
rmCamera3DGetEyeSeparation(const RMcamera3D * toQuery)1678 rmCamera3DGetEyeSeparation (const RMcamera3D *toQuery)
1679 {
1680     if (RM_ASSERT(toQuery, "rmCamera3DGetEyeSeparation error: input RMcamera3D points is NULL. Returning 1.0F.") == RM_WHACKED)
1681 	return(1.0F);
1682 
1683     return(toQuery->degrees_eye_separation);
1684 }
1685 
1686 
1687 /*
1688  * ----------------------------------------------------
1689  * @Name rmCamera3DSetFocalDistance
1690  @pstart
1691  RMenum rmCamera3DSetFocalDistance (RMcamera3D *toModify, float newVal)
1692  @pend
1693 
1694  @astart
1695  RMcamera3D *toModify - a handle to an RMcamera3D object (modified).
1696 
1697  float newVal - a floating point value specifying new focal distance
1698     length (input).
1699  @aend
1700 
1701  @dstart
1702 
1703  Use this routine to modify the focal distance parameter of a stereo
1704  3D camera. Returns RM_WHACKED upon failure, otherwise returns
1705  RM_CHILL.
1706 
1707  A stereo 3D camera in OpenRM defines a binocular viewing geometry.
1708  The binocular viewing geometry is a function of eye separation
1709  (rmCamera3DSetEyeSeparation) and focal length
1710  (rmCamera3DSetFocalDistance).
1711 
1712  In a binocular view model, the left and right eyes look at a point
1713  somewhere in space. This point is computed from the 3D stereo camera
1714  parameters: eye point (rmCamera3DSetEye), the look-at point
1715  (rmCamera3DSetAt) and the focal distance
1716  (rmCamera3DSetFocalDistance). The point is computed as the eye point
1717  plus the focal distance times the eye-at vector.
1718 
1719  A value of 1.0 for the focal distance places the stereo look-at point
1720  at the look-at point specified by rmCamera3DSetAt. A value of, say,
1721  0.707, places the stereo look at point in front of the 3D camera's
1722  look-at point, and a value of, say, 1.414, places the stereo look-at
1723  point somewhere behind the 3D camera's look at point.
1724 
1725  Manipulation of the focal distance and the interocular separation
1726  parameters can have a profound effect upon the perceived stereo view.
1727 
1728  @dend
1729  * ----------------------------------------------------
1730  */
1731 RMenum
rmCamera3DSetFocalDistance(RMcamera3D * toModify,float newval)1732 rmCamera3DSetFocalDistance (RMcamera3D *toModify,
1733 			    float newval)
1734 {
1735     if (RM_ASSERT(toModify, "rmCamera3DSetFocalDistance error: input RMcamera3D is NULL") == RM_WHACKED)
1736 	return(RM_WHACKED);
1737 
1738     toModify->focalDistance = newval;
1739 
1740     return(RM_CHILL);
1741 }
1742 
1743 
1744 /*
1745  * ----------------------------------------------------
1746  * @Name rmCamera3DGetFocalDistance
1747  @pstart
1748  RMenum rmCamera3DGetFocalDistance (const RMcamera3D *toQuery)
1749  @pend
1750 
1751  @astart
1752  const RMcamera3D *toQuery - a handle to an RMcamera3D object that
1753     will be queried by this routine (input).
1754  @aend
1755 
1756  @dstart
1757 
1758  Use this routine to query the focal distance parameter of a stereo 3D
1759  camera. A floating point value is returned by this routine.
1760 
1761  A stereo 3D camera in OpenRM defines a binocular viewing geometry.
1762  The binocular viewing geometry is a function of eye separation
1763  (rmCamera3DSetEyeSeparation) and focal length
1764  (rmCamera3DSetFocalDistance).
1765 
1766  In a binocular view model, the left and right eyes look at a point
1767  somewhere in space. This point is computed from the 3D stereo camera
1768  parameters: eye point (rmCamera3DSetEye), the look-at point
1769  (rmCamera3DSetAt) and the focal distance
1770  (rmCamera3DSetFocalDistance). The point is computed as the eye point
1771  plus the focal distance times the eye-at vector.
1772 
1773  A value of 1.0 for the focal distance places the stereo look-at point
1774  at the look-at point specified by rmCamera3DSetAt. A value of, say,
1775  0.707, places the stereo look at point in front of the 3D camera's
1776  look-at point, and a value of, say, 1.414, places the stereo look-at
1777  point somewhere behind the 3D camera's look at point.
1778 
1779  Manipulation of the focal distance and the interocular separation
1780  parameters can have a profound effect upon the perceived stereo view.
1781 
1782  @dend
1783  * ----------------------------------------------------
1784  */
1785 float
rmCamera3DGetFocalDistance(const RMcamera3D * c)1786 rmCamera3DGetFocalDistance (const RMcamera3D *c)
1787 {
1788     if (RM_ASSERT(c, "rmCamera3DGetFocalDistance error: input RMcamera3D is NULL. Returning 1.0F") == RM_WHACKED)
1789 	return(1.0F);
1790 
1791     return(c->focalDistance);
1792 }
1793 
1794 
1795 /*
1796  * ----------------------------------------------------
1797  * @Name rmCamera3DComputeViewMatrix
1798  @pstart
1799  RMenum rmCamera3DComputeViewMatrix (const RMcamera3D *source,
1800 			             RMmatrix *viewReturn,
1801 				     RMmatrix *projectionReturn)
1802  @pend
1803 
1804  @astart
1805  const RMcamera3D *source - a handle to an RMcamera3D object (input).
1806 
1807  RMmatrix *viewReturn - a handle to an RMmatrix object (result).
1808 
1809  RMmatrix *projectionReturn - a handle to an RMmatrix object (result).
1810  @aend
1811 
1812  @dstart
1813 
1814  Computes a view and projection matrix from an RMcamera3D object,
1815  returning the computed matrices in the "viewReturn" and
1816  "projectionReturn" parameters, respectively. RM_CHILL is returned
1817  upon success, or RM_WHACKED upon failure. Failure can occur if either
1818  of the input parameters is NULL, or if there is some type of fatal
1819  numerical error caused by bogus 3D camera parameters. These latter
1820  problems are detected and reported via rmError().
1821 
1822  The view matrix represents the affine transformation that transforms
1823  from world coordinates into "eye coordinates," assuming a monocular
1824  view.  The same matrix is returned regardless of whether or not the
1825  input camera is a stereo or mono camera.
1826 
1827  The projection matrix represents the transformation from eye space
1828  coordinates to NDC coordinates. If the camera uses perspective
1829  projection, that transformation will be present in the
1830  projectionReturn matrix.
1831 
1832  @dend
1833  * ----------------------------------------------------
1834  */
1835 RMenum
rmCamera3DComputeViewMatrix(const RMcamera3D * c,RMmatrix * m,RMmatrix * p)1836 rmCamera3DComputeViewMatrix (const RMcamera3D *c,
1837 			     RMmatrix *m,
1838 			     RMmatrix *p)
1839 {
1840     if ((RM_ASSERT(c, "rmCamera3DComputeViewMatrix error: the input RMcamera3D object is NULL") == RM_WHACKED) ||
1841 	(RM_ASSERT(m, "rmCamera3DComputeViewMatrix error: the return view RMmatrix object is NULL") == RM_WHACKED) ||
1842 	(RM_ASSERT(p, "rmCamera3DComputeViewMatrix error: the return projection RMmatrix object is NULL") == RM_WHACKED))
1843 	return(RM_WHACKED);
1844 
1845     private_rmComputeViewMatrix(c, m, p);
1846 
1847     return(RM_CHILL);
1848 }
1849 
1850 
1851 /*
1852  * ----------------------------------------------------
1853  * @Name rmCamera3DComputeViewFromGeometry
1854  @pstart
1855  RMenum rmCamera3DComputeViewFromGeometry (RMcamera3D *toModify,
1856 			                   const RMnode *source,
1857 					   int windowWidth,
1858 					   int windowHeight)
1859 
1860  @pend
1861 
1862  @astart
1863  RMcamera3D *toModify - a handle to the RMcamera3D object (modified).
1864 
1865  const RMnode *source - a handle to an RMnode. the bounding box of
1866     this RMnode will be used in computing 3D camera parameters
1867     (input).
1868 
1869  int windowWidth, windowHeight - int values specifying the width and
1870     height in pixels of the display window. These values are used in
1871     computing an aspect ratio for the camera (input).
1872  @aend
1873 
1874  @dstart
1875 
1876  This routine computes and assigns 3D camera parameters such that a
1877  volume defined by the bounding box attribute of the input RMnode will
1878  be visible, regardless of box orientation. If present, the scale matrix
1879  transformation attribute of the RMnode will be used to enlarge or shrink
1880  the bounding box parameter of the RMnode for the purposes of computing
1881  new camera parameters.
1882 
1883  The 3D camera must be a valid RMcamera3D object allocated by the
1884  caller.  This routine merely computes new values for an existing
1885  camera using the bounding box of an RMnode. It neither creates nor
1886  destroys an RMcamera3D.
1887 
1888  The 3D camera's FOV parameter is used as part of the computation, and
1889  will not be overwritten by this routine. All stereo parameters, if
1890  any, are ignored and remain unmodified by this routine.
1891 
1892  The new 3D camera parameters are computed thus:
1893 
1894  1. The 3D camera's look-at point will be the center of the RMnode's
1895     bounding box.
1896 
1897  2. The 3D camera's new eye position is the same as the new look-at
1898     point, but offset along the Z axis a distance D. The distance D is
1899     computed as boxsize/tan(FOV/2) where boxsize is the distance from
1900     the bounding box's min vertex to the box's max vertex. The basic
1901     idea is to move the eye point away from the look-at point a
1902     distance which is sufficient for the entire box to be visible,
1903     given the requested FOV.
1904 
1905  3. The near and far clip planes are set to a distance F which is
1906     computed as D-boxsize*1.4 and D+boxsize*1.4, respectively, where D
1907     is the same D computed in (2). Again, the point is to have the
1908     entire bounding box within the view frustum regardless of
1909     orientation.
1910 
1911     Note: 8/16/02 - changed computation of near/far to D-boxsize*2.0,
1912     D+boxsize*5.0, respectively. This gives more room to maneuver before
1913     the object gets clipped.
1914 
1915  4. The aspect ratio for the 3D camera is set using the input values
1916     windowWidth and WindowHeight.
1917 
1918  @dend
1919  * ----------------------------------------------------
1920  */
1921 RMenum
rmCamera3DComputeViewFromGeometry(RMcamera3D * c,const RMnode * r,int w,int h)1922 rmCamera3DComputeViewFromGeometry (RMcamera3D *c,	/* input and output */
1923 				   const RMnode *r,	/* input, has geometry & stuff */
1924 				   int w,		/* image width, for aspect ratio */
1925 				   int h)		/* image height, for aspect ratio */
1926 {
1927     float         new_hither, new_yon;
1928     float         vp[] = {0.0, 0.0, 1.0, 1.0};
1929     double        d, d2, boxsize;
1930     RMvertex3D    new_at;
1931     RMvertex3D    new_eye;
1932     RMvertex3D    new_up;
1933     RMvertex3D    bmin, bmax;
1934     const RMnode *root;
1935     RMmatrix      scale, C, minusC;
1936     extern double sqrt(double);
1937 
1938     if ((RM_ASSERT(c, "rmCamera3DComputeViewFromGeometry error: the input RMcamera3D object is NULL") == RM_WHACKED) ||
1939 	(RM_ASSERT(r, "rmCamera3DComputeViewFromGeometry error: the input RMnode is NULL") == RM_WHACKED))
1940 	return(RM_WHACKED);
1941 
1942     root = r;
1943     rmNodeGetBoundingBox(root, &bmin, &bmax);
1944 
1945     /* get the scale matrix */
1946     if (rmNodeGetScaleMatrix(r, &scale) == RM_WHACKED)
1947 	rmMatrixIdentity(&scale);
1948 
1949     /* multiply the bounding box with the scale matrix */
1950     {
1951         RMvertex3D center;
1952 	RMmatrix   composite;
1953 
1954 	rmNodeGetCenter(r, &center);
1955         rmMatrixIdentity(&C);
1956 	rmMatrixIdentity(&minusC);
1957 
1958 	C.m[3][0] = center.x;
1959 	C.m[3][1] = center.y;
1960 	C.m[3][2] = center.z;
1961 
1962 	minusC.m[3][0] = -1.0 * center.x;
1963 	minusC.m[3][1] = -1.0 * center.y;
1964 	minusC.m[3][2] = -1.0 * center.z;
1965 
1966 	rmMatrixIdentity(&composite);
1967 	rmMatrixMultiply(&minusC, &composite, &composite);
1968 	rmMatrixMultiply(&composite, &scale, &composite);
1969 	rmMatrixMultiply(&composite, &C, &composite);
1970 
1971 	rmPointMatrixTransform(&bmin, &composite, &bmin);
1972 	rmPointMatrixTransform(&bmax, &composite, &bmax);
1973     }
1974 
1975     /* we're going to assume that the bounding boxes are all sync'ed up. */
1976     new_up.x = new_up.z = 0.0;
1977     new_up.y = 1.0;
1978 
1979     /* the new "at" point will be a point at the x/y center of the
1980      front face of the bounding box. */
1981 
1982     new_at.x = bmin.x + ((bmax.x - bmin.x) * 0.5);
1983     new_at.y = bmin.y + ((bmax.y - bmin.y) * 0.5);
1984     /*    new_at.z = bmax.z; */
1985     new_at.z = bmin.z + ((bmax.z - bmin.z) * 0.5);
1986 
1987     /* compute the length of the diagonal of the box */
1988     d = ((bmax.x - bmin.x) * (bmax.x - bmin.x)) + ((bmax.y - bmin.y) * (bmax.y - bmin.y)) + ((bmax.z - bmin.z) * (bmax.z - bmin.z));
1989     boxsize = sqrt(d);
1990     boxsize *= 0.5;
1991 
1992     /* the new eye point will lie some distance along the +z axis
1993      * away from the new at point.  "some distance" is defined as a
1994      * distance which is "far enough" away so that the front face of
1995      * the bounding box will lie within FOV degrees of view.
1996      */
1997 
1998     d = boxsize;
1999 
2000     d2 = RM_DEGREES_TO_RADIANS(c->fov);
2001     d2 *= 0.5;
2002     d2 = tan(d2);
2003 
2004     d = boxsize / d2;
2005     d2 = d;
2006 
2007     /* the choices for hither and yon are somewhat arbitrary. what we
2008      * want is a choice of hither/yon that will permit the entire
2009      * object to be displayed w/o clipping regardless of orientation. so,
2010      * we need to look at the maximum dimension of the box.  also, such
2011      * a goal may not be achievable under some conditions.  for example, when
2012      * the requested fov is > 90, then the eye starts getting closer to
2013      * the box rather than further away from it.  oh well...
2014      *
2015      * hither/yon are a function of the boxsize.  these are set so that
2016      * the entire bbox will always be visible.  possible error conditions
2017      * include the case of d being < boxsize.
2018      */
2019     if (d < boxsize * 2.0)
2020       fprintf(stderr," error: we're about to set the front clip plane behind the eye. \n");
2021 
2022     new_hither = d - (boxsize * 2.0);
2023     new_yon = d + (boxsize * 5.0F);
2024 
2025     rmCamera3DResetAspectRatio(c, vp, w, h);
2026 
2027     new_eye.x = new_at.x;
2028     new_eye.y = new_at.y;
2029     new_eye.z = new_at.z + (d2 * 1.1);
2030 
2031     rmCamera3DSetEye(c, &new_eye);
2032     rmCamera3DSetAt(c, &new_at);
2033     rmCamera3DSetUpVector(c, &new_up);
2034     rmCamera3DSetHither(c, new_hither);
2035     rmCamera3DSetYon(c, new_yon);
2036 
2037     return(RM_CHILL);
2038 }
2039 
2040 
2041 /* PRIVATE */
2042 static void
private_rmCamera2DComputeMatrix(const RMcamera2D * c,RMmatrix * m)2043 private_rmCamera2DComputeMatrix (const RMcamera2D *c,
2044 				 RMmatrix *m)
2045 {
2046     float    x, y, z;
2047     float    tx, ty, tz;
2048     float    cwidth, center, cxmax, cxmin;
2049     double   left ,right, top, bottom;
2050     RMmatrix P;
2051 
2052     rmMatrixIdentity(&P);
2053 
2054     cwidth = c->xmax - c->xmin;
2055     center = ((c->xmax - c->xmin) * 0.5F) + c->xmin;
2056     cxmax = center + (cwidth * 0.5F) * c->aspect_ratio;
2057     cxmin = center - (cwidth * 0.5F) * c->aspect_ratio;
2058 
2059     left = cxmin;
2060     right = cxmax;
2061     bottom = c->ymin;
2062     top = c->ymax;
2063 
2064     x = 2.0 / (right - left);
2065     y = 2.0 / (top - bottom);
2066     z = 1.0;
2067     tx = -(right + left) / (right  -left);
2068     ty = -(top + bottom) / (top - bottom);
2069     tz = 0.;
2070 
2071     P.m[3][0] = tx;
2072     P.m[3][1] = ty;
2073     P.m[3][2] = tz;
2074 
2075     P.m[0][0] = x;
2076     P.m[1][1] = y;
2077     P.m[2][2] = z;
2078 
2079     *m = P;
2080 }
2081 
2082 
2083 /* PRIVATE */
2084 void
private_rmComputeViewMatrix(const RMcamera3D * c,RMmatrix * m,RMmatrix * proj)2085 private_rmComputeViewMatrix (const RMcamera3D *c,
2086 			     RMmatrix *m,
2087 			     RMmatrix *proj)
2088 {
2089     /* compute both the view and projection matrices  */
2090     RMmatrix   R, T, TR, P;
2091     RMvertex3D z, y, x;
2092 
2093     /* Make the translation matrix */
2094     rmMatrixIdentity(&T);
2095     T.m[3][0] = -1.0 * c->eye.x;
2096     T.m[3][1] = -1.0 * c->eye.y;
2097     T.m[3][2] = -1.0 * c->eye.z;
2098 
2099     /* Make rotation matrix */
2100     rmMatrixIdentity(&R);
2101 
2102     /* Z vector */
2103     rmVertex3DDiff(&(c->eye), &(c->at),&z);
2104     rmVertex3DNormalize(&z);
2105 
2106     y = c->up;
2107     rmVertex3DNormalize(&y);
2108 
2109     /* X vector = Y cross Z */
2110     rmVertex3DCross(&y, &z, &x);
2111 
2112     /* Recompute Y = Z cross X */
2113     rmVertex3DCross(&z, &x, &y);
2114 
2115    /* cross product gives area of parallelogram, which is < 1.0 for
2116     non-perpendicular unit-length vectors; so normalize x, y here */
2117 
2118     /* this might be transposed */
2119     R.m[0][0] = x.x;
2120     R.m[1][0] = x.y;
2121     R.m[2][0] = x.z;
2122 
2123     R.m[0][1] = y.x;
2124     R.m[1][1] = y.y;
2125     R.m[2][1] = y.z;
2126 
2127     R.m[0][2] = z.x;
2128     R.m[1][2] = z.y;
2129     R.m[2][2] = z.z;
2130 
2131     /* concatenate the translational and rotational pieces together */
2132     rmMatrixMultiply(&T, &R, &TR);
2133 
2134     /* build the projection matrix */
2135     rmMatrixIdentity(&P);
2136 
2137     if (rmCamera3DGetProjection(c) == RM_PROJECTION_PERSPECTIVE)
2138     {
2139         double fovy, aspect, znear, zfar;
2140         double xmin, xmax, ymin, ymax;
2141         double x, y, a, b, C, d;
2142         double left, right, bottom, top, nearval, farval;
2143 
2144         fovy = c->fov;
2145         aspect = c->aspect;
2146         znear = c->hither;
2147         zfar = c->yon;
2148 
2149         ymax = znear * tan(fovy * RM_PI / 360.0);
2150         ymin = -ymax;
2151 
2152         xmin = ymin * aspect;
2153         xmax = ymax * aspect;
2154 
2155         left = xmin;
2156         right = xmax;
2157         bottom = ymin;
2158         top = ymax;
2159         nearval = znear;
2160         farval = zfar;
2161 
2162         x = (2.0 * nearval) / (right - left);
2163         y = (2.0 * nearval) / (top - bottom);
2164         a = (right + left) / (right - left);
2165         b = (top + bottom) / (top - bottom);
2166         C = -(farval + nearval) / ( farval - nearval);
2167         d = -(2.0 * farval * nearval) / (nearval - farval);  /* error? */
2168 
2169         P.m[0][0] = x;
2170         P.m[2][0] = a;
2171         P.m[1][1] = y;
2172         P.m[2][1] = b;
2173         P.m[2][2] = C;
2174         P.m[2][3] = -1.0;
2175         P.m[3][2] = -d;
2176         P.m[3][3] = 1.0;		/* should be 0, but OGL puts a 1 there */
2177     }
2178     else /* RM_PROJECTION_ORTHOGONAL */
2179     {
2180         float      x, y, z;
2181         float      tx, ty, tz;
2182         double     width, height;
2183         double     sfov, mag;
2184         double     left, right, top, bottom, hither, yon;
2185         RMvertex3D eye_at;
2186 
2187         /* set the width of the view volume to be proportional to
2188 	 * the field-of-view angle.  we do this a little differently
2189 	 * than OpenGL, using the FOV to specify the horizontal field
2190 	 * of view, rather than the vertical field of view.
2191 	 */
2192 	sfov = sin((double)c->fov);
2193 
2194 	eye_at.x = c->eye.x - c->at.x;
2195         eye_at.y = c->eye.y - c->at.y;
2196         eye_at.z = c->eye.z - c->at.z;
2197 
2198         mag = rmVertex3DMag(&eye_at);
2199         width = mag * sfov;
2200 
2201         height = width / c->aspect;
2202 
2203         left = -width;
2204         right = width;
2205         bottom = -height;
2206         top = height;
2207         hither = c->hither;
2208         yon = c->yon;
2209 
2210         x = 2.0 / (right - left);
2211         y = 2.0 / (top - bottom);
2212         z = -2.0 / (yon - hither);
2213         tx = -(right + left) / (right - left);
2214         ty = -(top + bottom) / (top - bottom);
2215         tz = -(yon + hither) / (yon - hither);
2216 
2217         P.m[3][0] = tx;
2218         P.m[3][1] = ty;
2219         P.m[3][2] = tz;
2220 
2221         P.m[0][0] = x;
2222         P.m[1][1] = y;
2223         P.m[2][2] = z;
2224     }
2225     *proj = P;
2226     *m = TR;
2227 }
2228 /* EOF */
2229