1 /****************************************************************************
2  *                  vbuffer.cpp
3  *
4  * This module implements functions that implement the vista buffer.
5  *
6  * This module was written by Dieter Bayer [DB].
7  *
8  * from Persistence of Vision(tm) Ray Tracer version 3.6.
9  * Copyright 1991-2003 Persistence of Vision Team
10  * Copyright 2003-2004 Persistence of Vision Raytracer Pty. Ltd.
11  *---------------------------------------------------------------------------
12  * NOTICE: This source code file is provided so that users may experiment
13  * with enhancements to POV-Ray and to port the software to platforms other
14  * than those supported by the POV-Ray developers. There are strict rules
15  * regarding how you are permitted to use this file. These rules are contained
16  * in the distribution and derivative versions licenses which should have been
17  * provided with this file.
18  *
19  * These licences may be found online, linked from the end-user license
20  * agreement that is located at http://www.povray.org/povlegal.html
21  *---------------------------------------------------------------------------
22  * This program is based on the popular DKB raytracer version 2.12.
23  * DKBTrace was originally written by David K. Buck.
24  * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
25  *---------------------------------------------------------------------------
26  * $File: //depot/povray/3.6-release/source/vbuffer.cpp $
27  * $Revision: #3 $
28  * $Change: 3032 $
29  * $DateTime: 2004/08/02 18:43:41 $
30  * $Author: chrisc $
31  * $Log$
32  *****************************************************************************/
33 
34 #include "frame.h"
35 #include "vector.h"
36 #include "povray.h"
37 #include "bbox.h"
38 #include "boxes.h"
39 #include "hfield.h"
40 #include "lightgrp.h"
41 #include "lighting.h"
42 #include "matrices.h"
43 #include "objects.h"
44 #include "render.h"
45 #include "triangle.h"
46 #include "vbuffer.h"
47 #include "vlbuffer.h"
48 #include "userio.h"
49 #include "userdisp.h"
50 #include "optout.h"
51 #include "photons.h"
52 #include "povmsend.h"
53 
54 #include <algorithm>
55 
56 BEGIN_POV_NAMESPACE
57 
58 /*****************************************************************************
59 * Global variabls
60 ******************************************************************************/
61 
62 extern PRIORITY_QUEUE *VLBuffer_Queue; // GLOBAL VARIABLE
63 extern PROJECT_QUEUE *Node_Queue; // GLOBAL VARIABLE
64 
65 
66 /*****************************************************************************
67 * Local preprocessor defines
68 ******************************************************************************/
69 
70 
71 
72 /*****************************************************************************
73 * Local typedefs
74 ******************************************************************************/
75 
76 
77 
78 /*****************************************************************************
79 * Local variables
80 ******************************************************************************/
81 
82 static DBL Distance; // GLOBAL VARIABLE
83 static MATRIX WC2VC, WC2VCinv; // GLOBAL VARIABLE
84 static VECTOR gO, gU, gV, gW; // GLOBAL VARIABLE
85 
86 
87 /* Planes for 3d-clipping. */
88 
89 const VECTOR VIEW_VX1 = {-0.8944271910, 0.0, -0.4472135955};
90 const VECTOR VIEW_VX2 = { 0.8944271910, 0.0, -0.4472135955};
91 const VECTOR VIEW_VY1 = {0.0, -0.8944271910, -0.4472135955};
92 const VECTOR VIEW_VY2 = {0.0,  0.8944271910, -0.4472135955};
93 const DBL VIEW_DX1 = 0.4472135955;
94 const DBL VIEW_DX2 = 0.4472135955;
95 const DBL VIEW_DY1 = 0.4472135955;
96 const DBL VIEW_DY2 = 0.4472135955;
97 
98 static PROJECT_TREE_NODE *Root_Vista; // GLOBAL VARIABLE
99 
100 
101 /*****************************************************************************
102 * Static functions
103 ******************************************************************************/
104 
105 static void init_view_coordinates (void);
106 
107 static void project_raw_rectangle (PROJECT *Project, VECTOR P1, VECTOR P2, VECTOR P3, VECTOR P4, int *visible);
108 static void project_raw_triangle (PROJECT *Project, VECTOR P1, VECTOR P2, VECTOR P3, int *visible);
109 
110 static void project_bbox (PROJECT *Project, VECTOR *P, int *visible);
111 static void project_bounds (PROJECT *Project, BBOX *BBox, int *visible);
112 
113 static void get_perspective_projection (OBJECT *Object, PROJECT *Project, int infinite);
114 static void get_orthographic_projection (OBJECT *Object, PROJECT *Project, int infinite);
115 
116 static void project_object (OBJECT *Object, PROJECT *Project);
117 
118 static void project_box (PROJECT *Project, OBJECT *Object, int *visible);
119 static void project_hfield (PROJECT *Project, OBJECT *Object, int *visible);
120 static void project_triangle (PROJECT *Project, OBJECT *Object, int *visible);
121 static void project_smooth_triangle (PROJECT *Project, OBJECT *Object, int *visible);
122 
123 static void transform_point (VECTOR P);
124 
125 static void project_bounding_slab (PROJECT *Project, PROJECT_TREE_NODE **Tree, BBOX_TREE *Node);
126 
127 static int intersect_vista_tree (RAY *Ray, PROJECT_TREE_NODE *Tree, int x, INTERSECTION *Best_Intersection);
128 
129 static void draw_projection (PROJECT *Project, int color, int *BigRed, int *BigBlue);
130 static void draw_vista (PROJECT_TREE_NODE *Tree, int *BigRed, int *BigBlue);
131 
132 /*****************************************************************************
133 *
134 * FUNCTION
135 *
136 *   Prune_Vista_Tree
137 *
138 * INPUT
139 *
140 *   y - Current scanline number
141 *
142 * OUTPUT
143 *
144 * RETURNS
145 *
146 * AUTHOR
147 *
148 *   Dieter Bayer
149 *
150 * DESCRIPTION
151 *
152 *   Prune vista tree, i.e. mark all nodes not on the current line inactive.
153 *
154 * CHANGES
155 *
156 *   May 1994 : Creation.
157 *
158 ******************************************************************************/
159 
Prune_Vista_Tree(int y)160 void Prune_Vista_Tree(int y)
161 {
162   unsigned short i;
163   PROJECT_TREE_NODE *Node, *Sib;
164 
165   /* If there's no vista tree then return. */
166 
167   if (Root_Vista == NULL)
168   {
169     return;
170   }
171 
172   Node_Queue->QSize = 0;
173 
174   Increase_Counter(stats[VBuffer_Tests]);
175 
176   if ((y < Root_Vista->Project.y1) || (y > Root_Vista->Project.y2))
177   {
178     /* Root doesn't lie on current line --> prune root */
179 
180     Root_Vista->is_leaf |= PRUNE_TEMPORARY;
181   }
182   else
183   {
184     /* Root lies on current line --> unprune root */
185 
186     Increase_Counter(stats[VBuffer_Tests_Succeeded]);
187 
188     Root_Vista->is_leaf &= ~PRUNE_TEMPORARY;
189 
190     Node_Queue->Queue[(Node_Queue->QSize)++] = Root_Vista;
191   }
192 
193   while (Node_Queue->QSize > 0)
194   {
195     Node = Node_Queue->Queue[--(Node_Queue->QSize)];
196 
197     if (Node->is_leaf & true)
198     {
199       Increase_Counter(stats[VBuffer_Tests]);
200 
201       if ((y < Node->Project.y1) || (y > Node->Project.y2))
202       {
203         /* Leaf doesn't lie on current line --> prune leaf */
204 
205         Node->is_leaf |= PRUNE_TEMPORARY;
206       }
207       else
208       {
209         /* Leaf lies on current line --> unprune leaf */
210 
211         Increase_Counter(stats[VBuffer_Tests_Succeeded]);
212 
213         Node->is_leaf &= ~PRUNE_TEMPORARY;
214       }
215     }
216     else
217     {
218       /* Check siblings of the node */
219 
220       for (i = 0; i < Node->Entries; i++)
221       {
222         Sib = Node->Entry[i];
223 
224         Increase_Counter(stats[VBuffer_Tests]);
225 
226         if ((y < Sib->Project.y1) || (y > Sib->Project.y2))
227         {
228           /* Sibling doesn't lie on current line --> prune sibling */
229 
230           Sib->is_leaf |= PRUNE_TEMPORARY;
231         }
232         else
233         {
234           /* Sibling lies on current line --> unprune sibling */
235 
236           Increase_Counter(stats[VBuffer_Tests_Succeeded]);
237 
238           Sib->is_leaf &= ~PRUNE_TEMPORARY;
239 
240           /* Add sibling to list */
241 
242           /* Reallocate queue if it's too small. */
243 
244           Reinitialize_VLBuffer_Code();
245 
246           Node_Queue->Queue[(Node_Queue->QSize)++] = Sib;
247         }
248       }
249     }
250   }
251 }
252 
253 
254 
255 /*****************************************************************************
256 *
257 * FUNCTION
258 *
259 *   Trace_Primary_Ray
260 *
261 * INPUT
262 *
263 *   Ray    - Current ray
264 *   Colour - Ray's colour
265 *   x      - Current x-coordinate
266 *
267 * OUTPUT
268 *
269 *   colour
270 *
271 * RETURNS
272 *
273 * AUTHOR
274 *
275 *   Dieter Bayer
276 *
277 * DESCRIPTION
278 *
279 *   Trace a primary ray using the vista tree.
280 *
281 * CHANGES
282 *
283 *   May 1994 : Creation.
284 *
285 *   Nov 1994 : Rearranged calls to Fog, Ranibow and Skyblend.
286 *              Added call to Atmosphere for atmospheric effects. [DB]
287 *
288 *   Jan 1995 : Set intersection depth to Max_Distance for infinte rays. [DB]
289 *   Jul 1995 : Added code to support alpha channel. [DB]
290 *
291 ******************************************************************************/
292 
Trace_Primary_Ray(RAY * Ray,COLOUR Colour,DBL Weight,int x)293 DBL Trace_Primary_Ray (RAY *Ray, COLOUR Colour, DBL Weight, int x)
294 {
295   int i, Intersection_Found, all_hollow;
296   INTERSECTION Best_Intersection;
297 
298   Do_Cooperate(1);
299   Increase_Counter(stats[Number_Of_Rays]);
300 
301   Make_ColourA(Colour, 0.0, 0.0, 0.0, 0.0, 0.0);
302 
303   if ((Trace_Level > Max_Trace_Level) || (Weight < ADC_Bailout))
304   {
305     if (Weight < ADC_Bailout)
306     {
307       Increase_Counter(stats[ADC_Saves]);
308     }
309 
310     return BOUND_HUGE;
311   }
312 
313   if (Trace_Level > Highest_Trace_Level)
314   {
315     Highest_Trace_Level = Trace_Level;
316   }
317 
318   Best_Intersection.Depth = BOUND_HUGE;
319   Best_Intersection.Object = NULL;
320 
321   /* What objects does this ray intersect? */
322 
323   Intersection_Found = intersect_vista_tree(Ray, Root_Vista, x, &Best_Intersection);
324 
325   if (Intersection_Found)
326   {
327     Determine_Apparent_Colour(&Best_Intersection, Colour, Ray, 1.0);
328   }
329   else
330   {
331       /* Infinite ray, set intersection distance. */
332 
333       Best_Intersection.Depth = Max_Distance;
334 
335       Do_Infinite_Atmosphere(Ray, Colour);
336   }
337 
338   /* Test if all contained objects are hollow. */
339 
340   all_hollow = true;
341 
342   if (Ray->Index > -1)
343   {
344     for (i = 0; i <= Ray->Index; i++)
345     {
346       if (!Ray->Interiors[i]->hollow)
347       {
348         all_hollow = false;
349 
350         break;
351       }
352     }
353   }
354 
355   /* Apply finite atmospheric effects. */
356   if (all_hollow && (opts.Quality_Flags & Q_VOLUME))
357   {
358     Do_Finite_Atmosphere(Ray, &Best_Intersection, Colour, false);
359   }
360 
361   return (Best_Intersection.Depth);
362 }
363 
364 
365 
366 /*****************************************************************************
367 *
368 * FUNCTION
369 *
370 *   intersect_vista_tree
371 *
372 * INPUT
373 *
374 *   Ray               - Primary ray
375 *   Tree              - Vista tree's top-node
376 *   x                 - Current x-coordinate
377 *   Best_Intersection - Intersection found
378 *
379 * OUTPUT
380 *
381 *   Best_Intersection
382 *
383 * RETURNS
384 *
385 * AUTHOR
386 *
387 *   Dieter Bayer
388 *
389 * DESCRIPTION
390 *
391 *   Intersect a PRIMARY ray with the vista tree
392 *   (tree pruning is used can be primary ray!!!).
393 *
394 * CHANGES
395 *
396 *   May 1994 : Creation.
397 *   Aug 1997 : Add object-ray options.  Test whether the ray should
398 *              intersect with objects based on "no_image" and
399 *              "no_reflection" tags. [ENB]
400 *
401 ******************************************************************************/
402 
intersect_vista_tree(RAY * Ray,PROJECT_TREE_NODE * Tree,int x,INTERSECTION * Best_Intersection)403 static int intersect_vista_tree(RAY *Ray, PROJECT_TREE_NODE *Tree, int x, INTERSECTION *Best_Intersection)
404 {
405   INTERSECTION New_Intersection;
406   unsigned short i;
407   int Found;
408   RAYINFO rayinfo;
409   DBL key;
410   BBOX_TREE *BBox_Node;
411   PROJECT_TREE_NODE *Node;
412 
413   /* If there's no vista tree then return. */
414 
415   if (Tree == NULL)
416   {
417     return(false);
418   }
419 
420   /* Start with an empty priority queue */
421 
422   New_Intersection.Object = NULL;
423 
424   VLBuffer_Queue->QSize = 0;
425 
426   Found = false;
427 
428 #ifdef BBOX_EXTRA_STATS
429   Increase_Counter(stats[totalQueueResets]);
430 #endif
431 
432   /* Descend tree. */
433 
434   Node_Queue->QSize = 0;
435 
436   /* Create the direction vectors for this ray */
437 
438   Create_Rayinfo(Ray, &rayinfo);
439 
440   /* Fill the priority queue with all possible candidates */
441 
442   /* Check root */
443 
444   Increase_Counter(stats[VBuffer_Tests]);
445 
446   if ((x >= Tree->Project.x1) && (x <= Tree->Project.x2))
447   {
448     Increase_Counter(stats[VBuffer_Tests_Succeeded]);
449 
450     Node_Queue->Queue[(Node_Queue->QSize)++] = Tree;
451   }
452 
453   while (Node_Queue->QSize > 0)
454   {
455     Tree = Node_Queue->Queue[--(Node_Queue->QSize)];
456 
457     switch (Tree->is_leaf)
458     {
459       case false:
460 
461         /* Check siblings of the unpruned node in 2d */
462 
463         for (i = 0; i < Tree->Entries; i++)
464         {
465           Node = Tree->Entry[i];
466 
467           /* Check unpruned siblings only */
468 
469           if (Node->is_leaf < PRUNE_CHECK)
470           {
471             Increase_Counter(stats[VBuffer_Tests]);
472 
473             if ((x >= Node->Project.x1) && (x <= Node->Project.x2))
474             {
475               /* Add node to node queue. */
476 
477               Increase_Counter(stats[VBuffer_Tests_Succeeded]);
478 
479               /* Reallocate queue if it's too small. */
480 
481               Reinitialize_VLBuffer_Code();
482 
483               Node_Queue->Queue[(Node_Queue->QSize)++] = Node;
484             }
485           }
486         }
487 
488     break;
489 
490       case true:
491 
492         /* Unpruned leaf --> test object's bounding box in 3d */
493 
494         Check_And_Enqueue(VLBuffer_Queue,
495           ((PROJECT_TREE_LEAF *)Tree)->Node,
496           &(((PROJECT_TREE_LEAF *)Tree)->Node->BBox),
497           &rayinfo);
498 
499         break;
500 
501    /* default:
502 
503         The node/leaf is pruned and needn't be checked */
504 
505     }
506   }
507 
508   /* Now test the candidates in the priority queue */
509 
510   while (VLBuffer_Queue->QSize > 0)
511   {
512     Priority_Queue_Delete(VLBuffer_Queue, &key, &BBox_Node);
513 
514     if (key > Best_Intersection->Depth)
515       break;
516 
517     /* Add Object-Ray options [ENB 9/97] */
518     if ( TEST_RAY_FLAGS((OBJECT *)BBox_Node->Node) )
519     {
520       if (Intersection(&New_Intersection, (OBJECT *)BBox_Node->Node, Ray))
521       {
522         if (New_Intersection.Depth < Best_Intersection->Depth)
523         {
524           *Best_Intersection = New_Intersection;
525           Found = true;
526         }
527       }
528     }
529   }
530 
531   return(Found);
532 }
533 
534 
535 /*****************************************************************************
536 *
537 * FUNCTION
538 *
539 *   project_raw_triangle
540 *
541 * INPUT
542 *
543 *   Project    - Triangle's projection
544 *   P1, P2, P3 - Triangle's edges
545 *   visible    - Flag if triangle is visible
546 *
547 * OUTPUT
548 *
549 *   Project, visible
550 *
551 * RETURNS
552 *
553 * AUTHOR
554 *
555 *   Dieter Bayer
556 *
557 * DESCRIPTION
558 *
559 *   Project a triangle onto the screen.
560 *
561 * CHANGES
562 *
563 *   May 1994 : Creation.
564 *
565 ******************************************************************************/
566 
project_raw_triangle(PROJECT * Project,VECTOR P1,VECTOR P2,VECTOR P3,int * visible)567 static void project_raw_triangle (PROJECT *Project, VECTOR P1, VECTOR  P2, VECTOR  P3, int *visible)
568 {
569   VECTOR Points[MAX_CLIP_POINTS];
570   int i, number;
571   int x, y;
572 
573   Assign_Vector(Points[0], P1);
574   Assign_Vector(Points[1], P2);
575   Assign_Vector(Points[2], P3);
576 
577   number = 3;
578 
579   /* Clip triangle only if some quick tests say it's necessary.
580      Assuming that only a few triangles need clipping this saves some time.
581      (I don't need to write fabs(1+P?[Z]) since the tests succeed anyway if
582       P?[Z] < -1. Hope the compiler doesn't change the tests' order!) */
583 
584   if ((P1[Z] < -1.0) || (P2[Z] < -1.0) || (P3[Z] < -1.0) ||
585       (fabs(P1[X]) > 0.5*(1.0+P1[Z])) || (fabs(P1[Y]) > 0.5*(1.0+P1[Z])) ||
586       (fabs(P2[X]) > 0.5*(1.0+P2[Z])) || (fabs(P2[Y]) > 0.5*(1.0+P2[Z])) ||
587       (fabs(P3[X]) > 0.5*(1.0+P3[Z])) || (fabs(P3[Y]) > 0.5*(1.0+P3[Z])))
588   {
589     Clip_Polygon(Points, &number, VIEW_VX1, VIEW_VX2, VIEW_VY1, VIEW_VY2,
590                                   VIEW_DX1, VIEW_DX2, VIEW_DY1, VIEW_DY2);
591   }
592 
593   if (number)
594   {
595     for (i = 0; i < number; i++)
596     {
597       if (Points[i][Z] < -1.0 + EPSILON)
598       {
599         Points[i][X] = Points[i][Y] = 0.0;
600       }
601       else
602       {
603         Points[i][X] /= 1.0 + Points[i][Z];
604         Points[i][Y] /= 1.0 + Points[i][Z];
605       }
606 
607       x = Frame.Screen_Width/2  + (int)(Frame.Screen_Width  * Points[i][X]);
608       y = Frame.Screen_Height/2 - (int)(Frame.Screen_Height * Points[i][Y]);
609 
610       if (x < Project->x1) Project->x1 = x;
611       if (x > Project->x2) Project->x2 = x;
612       if (y < Project->y1) Project->y1 = y;
613       if (y > Project->y2) Project->y2 = y;
614     }
615 
616     *visible = true;
617   }
618 }
619 
620 
621 
622 /*****************************************************************************
623 *
624 * FUNCTION
625 *
626 *   project_raw_rectangle
627 *
628 * INPUT
629 *
630 *   Project        - Rectangle's projection
631 *   P1, P2, P3, P4 - Rectangle's edges
632 *   visible        - Flag if rectangle is visible
633 *
634 * OUTPUT
635 *
636 *   Project, visible
637 *
638 * RETURNS
639 *
640 * AUTHOR
641 *
642 *   Dieter Bayer
643 *
644 * DESCRIPTION
645 *
646 *   Project a rectangle onto the screen.
647 *
648 * CHANGES
649 *
650 *   May 1994 : Creation.
651 *
652 ******************************************************************************/
653 
project_raw_rectangle(PROJECT * Project,VECTOR P1,VECTOR P2,VECTOR P3,VECTOR P4,int * visible)654 static void project_raw_rectangle(PROJECT *Project, VECTOR P1, VECTOR  P2, VECTOR  P3, VECTOR  P4, int *visible)
655 {
656   VECTOR Points[MAX_CLIP_POINTS];
657   int i, number;
658   int x, y;
659 
660   Assign_Vector(Points[0], P1);
661   Assign_Vector(Points[1], P2);
662   Assign_Vector(Points[2], P3);
663   Assign_Vector(Points[3], P4);
664 
665   number = 4;
666 
667   Clip_Polygon(Points, &number, VIEW_VX1, VIEW_VX2, VIEW_VY1, VIEW_VY2,
668                                 VIEW_DX1, VIEW_DX2, VIEW_DY1, VIEW_DY2);
669 
670   if (number)
671   {
672     for (i = 0; i < number; i++)
673     {
674       if (Points[i][Z] < -1.0 + EPSILON)
675       {
676         Points[i][X] = Points[i][Y] = 0.0;
677       }
678       else
679       {
680         Points[i][X] /= 1.0 + Points[i][Z];
681         Points[i][Y] /= 1.0 + Points[i][Z];
682       }
683 
684       x = Frame.Screen_Width/2  + (int)(Frame.Screen_Width  * Points[i][X]);
685       y = Frame.Screen_Height/2 - (int)(Frame.Screen_Height * Points[i][Y]);
686 
687       if (x < Project->x1) Project->x1 = x;
688       if (x > Project->x2) Project->x2 = x;
689       if (y < Project->y1) Project->y1 = y;
690       if (y > Project->y2) Project->y2 = y;
691     }
692 
693     *visible = true;
694   }
695 }
696 
697 
698 
699 
700 /*****************************************************************************
701 *
702 * FUNCTION
703 *
704 *   project_bbox
705 *
706 * INPUT
707 *
708 *   Project - Box's projection
709 *   P       - Box's edges
710 *   visible - Flag if box is visible
711 *
712 * OUTPUT
713 *
714 *   Project, visible
715 *
716 * RETURNS
717 *
718 * AUTHOR
719 *
720 *   Dieter Bayer
721 *
722 * DESCRIPTION
723 *
724 *   Project a box onto the screen.
725 *
726 * CHANGES
727 *
728 *   May 1994 : Creation.
729 *
730 ******************************************************************************/
731 
project_bbox(PROJECT * Project,VECTOR * P,int * visible)732 static void project_bbox(PROJECT *Project, VECTOR *P, int *visible)
733 {
734   int vis, i, x, y;
735   PROJECT New;
736 
737   New.x1 = MAX_BUFFER_ENTRY;
738   New.x2 = MIN_BUFFER_ENTRY;
739   New.y1 = MAX_BUFFER_ENTRY;
740   New.y2 = MIN_BUFFER_ENTRY;
741 
742   vis = false;
743 
744   /* Check if all points lie "in front" of the viewer. */
745 
746   if ((P[0][Z] > -1.0) && (P[1][Z] > -1.0) && (P[2][Z] > -1.0) && (P[3][Z] > -1.0) &&
747       (P[4][Z] > -1.0) && (P[5][Z] > -1.0) && (P[6][Z] > -1.0) && (P[7][Z] > -1.0))
748   {
749     /* Check if all points lie inside the "viewing pyramid". */
750 
751     if ((fabs(P[0][X]) <= 0.5*(1.0+P[0][Z])) && (fabs(P[1][X]) <= 0.5*(1.0+P[1][Z])) &&
752         (fabs(P[2][X]) <= 0.5*(1.0+P[2][Z])) && (fabs(P[3][X]) <= 0.5*(1.0+P[3][Z])) &&
753         (fabs(P[4][X]) <= 0.5*(1.0+P[4][Z])) && (fabs(P[5][X]) <= 0.5*(1.0+P[5][Z])) &&
754         (fabs(P[6][X]) <= 0.5*(1.0+P[6][Z])) && (fabs(P[7][X]) <= 0.5*(1.0+P[7][Z])) &&
755         (fabs(P[0][Y]) <= 0.5*(1.0+P[0][Z])) && (fabs(P[1][Y]) <= 0.5*(1.0+P[1][Z])) &&
756         (fabs(P[2][Y]) <= 0.5*(1.0+P[2][Z])) && (fabs(P[3][Y]) <= 0.5*(1.0+P[3][Z])) &&
757         (fabs(P[4][Y]) <= 0.5*(1.0+P[4][Z])) && (fabs(P[5][Y]) <= 0.5*(1.0+P[5][Z])) &&
758         (fabs(P[6][Y]) <= 0.5*(1.0+P[6][Z])) && (fabs(P[7][Y]) <= 0.5*(1.0+P[7][Z])))
759     {
760       /* No clipping is needed. Just project the points. */
761 
762       vis = true;
763 
764       for (i = 0; i < 8; i++)
765       {
766         if (P[i][Z] < -1.0 + EPSILON)
767         {
768           P[i][X] = P[i][Y] = 0.0;
769         }
770         else
771         {
772           P[i][X] /= 1.0 + P[i][Z];
773           P[i][Y] /= 1.0 + P[i][Z];
774         }
775 
776         x = Frame.Screen_Width/2  + (int)(Frame.Screen_Width  * P[i][X]);
777         y = Frame.Screen_Height/2 - (int)(Frame.Screen_Height * P[i][Y]);
778 
779         if (x < New.x1) New.x1 = x;
780         if (x > New.x2) New.x2 = x;
781         if (y < New.y1) New.y1 = y;
782         if (y > New.y2) New.y2 = y;
783       }
784     }
785   }
786 
787   if (!vis)
788   {
789     project_raw_rectangle(&New, P[0], P[1], P[3], P[2], &vis);
790     project_raw_rectangle(&New, P[4], P[5], P[7], P[6], &vis);
791     project_raw_rectangle(&New, P[0], P[1], P[5], P[4], &vis);
792     project_raw_rectangle(&New, P[2], P[3], P[7], P[6], &vis);
793     project_raw_rectangle(&New, P[1], P[3], P[7], P[5], &vis);
794     project_raw_rectangle(&New, P[0], P[2], P[6], P[4], &vis);
795   }
796 
797   if (vis)
798   {
799     if (New.x1 > Project->x1) Project->x1 = New.x1;
800     if (New.x2 < Project->x2) Project->x2 = New.x2;
801     if (New.y1 > Project->y1) Project->y1 = New.y1;
802     if (New.y2 < Project->y2) Project->y2 = New.y2;
803     *visible = true;
804   }
805 }
806 
807 
808 
809 /*****************************************************************************
810 *
811 * FUNCTION
812 *
813 *   project_bounds
814 *
815 * INPUT
816 *
817 *   Project - Bounding box's projection
818 *   BBox    - Bounding box
819 *   visible - Flag if bounding box is visible
820 *
821 * OUTPUT
822 *
823 *   Project, visible
824 *
825 * RETURNS
826 *
827 * AUTHOR
828 *
829 *   Dieter Bayer
830 *
831 * DESCRIPTION
832 *
833 *   Project a bounding box onto the screen.
834 *
835 * CHANGES
836 *
837 *   May 1994 : Creation.
838 *
839 ******************************************************************************/
840 
project_bounds(PROJECT * Project,BBOX * BBox,int * visible)841 static void project_bounds(PROJECT *Project, BBOX *BBox, int *visible)
842 {
843   int i;
844   VECTOR P[8];
845 
846   for (i = 0; i<8; i++)
847   {
848     P[i][X] = ((i & 1) ? BBox->Lengths[X] : 0.0) + BBox->Lower_Left[X];
849     P[i][Y] = ((i & 2) ? BBox->Lengths[Y] : 0.0) + BBox->Lower_Left[Y];
850     P[i][Z] = ((i & 4) ? BBox->Lengths[Z] : 0.0) + BBox->Lower_Left[Z];
851 
852     transform_point(P[i]);
853   }
854 
855   project_bbox(Project, P, visible);
856 }
857 
858 
859 
860 /*****************************************************************************
861 *
862 * FUNCTION
863 *
864 *   project_box
865 *
866 * INPUT
867 *
868 *   Project - Projection
869 *   Object  - Object
870 *   visible - Flag if object is visible
871 *
872 * OUTPUT
873 *
874 *   Project, visible
875 *
876 * RETURNS
877 *
878 * AUTHOR
879 *
880 *   Dieter Bayer
881 *
882 * DESCRIPTION
883 *
884 *   Project a box onto the screen.
885 *
886 * CHANGES
887 *
888 *   May 1994 : Creation.
889 *
890 ******************************************************************************/
891 
project_box(PROJECT * Project,OBJECT * Object,int * visible)892 static void project_box(PROJECT *Project, OBJECT *Object, int *visible)
893 {
894   int i;
895   VECTOR P[8];
896   BOX *box;
897 
898   box = (BOX *)Object;
899 
900   for (i = 0; i<8; i++)
901   {
902     P[i][X] = (i & 1) ? box->bounds[1][X] : box->bounds[0][X];
903     P[i][Y] = (i & 2) ? box->bounds[1][Y] : box->bounds[0][Y];
904     P[i][Z] = (i & 4) ? box->bounds[1][Z] : box->bounds[0][Z];
905 
906     if (box->Trans != NULL)
907     {
908       MTransPoint(P[i], P[i], box->Trans);
909     }
910 
911     transform_point(P[i]);
912   }
913 
914   project_bbox(Project, P, visible);
915 }
916 
917 
918 
919 /*****************************************************************************
920 *
921 * FUNCTION
922 *
923 *   project_hfield
924 *
925 * INPUT
926 *
927 *   Project - Projection
928 *   Object  - Object
929 *   visible - Flag if object is visible
930 *
931 * OUTPUT
932 *
933 *   Project, visible
934 *
935 * RETURNS
936 *
937 * AUTHOR
938 *
939 *   Dieter Bayer
940 *
941 * DESCRIPTION
942 *
943 *   Project the bounding box of a height field onto the screen.
944 *
945 * CHANGES
946 *
947 *   May 1994 : Creation.
948 *
949 ******************************************************************************/
950 
project_hfield(PROJECT * Project,OBJECT * Object,int * visible)951 static void project_hfield(PROJECT *Project, OBJECT *Object, int *visible)
952 {
953   int i;
954   VECTOR P[8];
955   HFIELD *hfield;
956 
957   hfield = (HFIELD *)Object;
958 
959   for (i = 0; i<8; i++)
960   {
961     Assign_Vector(P[i], hfield->bounding_corner1);
962 
963     P[i][X] = (i & 1) ? hfield->bounding_corner2[X] : hfield->bounding_corner1[X];
964     P[i][Y] = (i & 2) ? hfield->bounding_corner2[Y] : hfield->bounding_corner1[Y];
965     P[i][Z] = (i & 4) ? hfield->bounding_corner2[Z] : hfield->bounding_corner1[Z];
966 
967     if (hfield->Trans != NULL)
968     {
969       MTransPoint(P[i], P[i], hfield->Trans);
970     }
971 
972     transform_point(P[i]);
973   }
974 
975   project_bbox(Project, P, visible);
976 }
977 
978 
979 
980 /*****************************************************************************
981 *
982 * FUNCTION
983 *
984 *   project_triangle
985 *
986 * INPUT
987 *
988 *   Project - Projection
989 *   Object  - Object
990 *   visible - Flag if object is visible
991 *
992 * OUTPUT
993 *
994 *   Project, visible
995 *
996 * RETURNS
997 *
998 * AUTHOR
999 *
1000 *   Dieter Bayer
1001 *
1002 * DESCRIPTION
1003 *
1004 *   Project a triangle onto the screen.
1005 *
1006 * CHANGES
1007 *
1008 *   May 1994 : Creation.
1009 *
1010 ******************************************************************************/
1011 
project_triangle(PROJECT * Project,OBJECT * Object,int * visible)1012 static void project_triangle(PROJECT *Project, OBJECT *Object, int *visible)
1013 {
1014   int i, vis;
1015   VECTOR P[3];
1016   PROJECT New;
1017 
1018   New.x1 = MAX_BUFFER_ENTRY;
1019   New.x2 = MIN_BUFFER_ENTRY;
1020   New.y1 = MAX_BUFFER_ENTRY;
1021   New.y2 = MIN_BUFFER_ENTRY;
1022 
1023   Assign_Vector(P[0], ((TRIANGLE *)Object)->P1);
1024   Assign_Vector(P[1], ((TRIANGLE *)Object)->P2);
1025   Assign_Vector(P[2], ((TRIANGLE *)Object)->P3);
1026 
1027   for (i = 0; i < 3; i++)
1028   {
1029     transform_point(P[i]);
1030   }
1031 
1032   vis = false;
1033 
1034   project_raw_triangle(&New, P[0], P[1], P[2], &vis);
1035 
1036   if (vis)
1037   {
1038     if (New.x1 > Project->x1) Project->x1 = New.x1;
1039     if (New.x2 < Project->x2) Project->x2 = New.x2;
1040     if (New.y1 > Project->y1) Project->y1 = New.y1;
1041     if (New.y2 < Project->y2) Project->y2 = New.y2;
1042 
1043     *visible = true;
1044   }
1045 }
1046 
1047 
1048 
1049 /*****************************************************************************
1050 *
1051 * FUNCTION
1052 *
1053 *   project_smooth_triangle
1054 *
1055 * INPUT
1056 *
1057 *   Project - Projection
1058 *   Object  - Object
1059 *   visible - Flag if object is visible
1060 *
1061 * OUTPUT
1062 *
1063 *   Project, visible
1064 *
1065 * RETURNS
1066 *
1067 * AUTHOR
1068 *
1069 *   Dieter Bayer
1070 *
1071 * DESCRIPTION
1072 *
1073 *   Project a smooth triangle onto the screen.
1074 *
1075 * CHANGES
1076 *
1077 *   May 1994 : Creation.
1078 *
1079 ******************************************************************************/
1080 
project_smooth_triangle(PROJECT * Project,OBJECT * Object,int * visible)1081 static void project_smooth_triangle(PROJECT *Project, OBJECT *Object, int *visible)
1082 {
1083   int i, vis;
1084   VECTOR P[3];
1085   PROJECT New;
1086 
1087   New.x1 = MAX_BUFFER_ENTRY;
1088   New.x2 = MIN_BUFFER_ENTRY;
1089   New.y1 = MAX_BUFFER_ENTRY;
1090   New.y2 = MIN_BUFFER_ENTRY;
1091 
1092   Assign_Vector(P[0], ((SMOOTH_TRIANGLE *)Object)->P1);
1093   Assign_Vector(P[1], ((SMOOTH_TRIANGLE *)Object)->P2);
1094   Assign_Vector(P[2], ((SMOOTH_TRIANGLE *)Object)->P3);
1095 
1096   for (i = 0; i < 3; i++)
1097   {
1098     transform_point(P[i]);
1099   }
1100 
1101   vis = false;
1102 
1103   project_raw_triangle(&New, P[0], P[1], P[2], &vis);
1104 
1105   if (vis)
1106   {
1107     if (New.x1 > Project->x1) Project->x1 = New.x1;
1108     if (New.x2 < Project->x2) Project->x2 = New.x2;
1109     if (New.y1 > Project->y1) Project->y1 = New.y1;
1110     if (New.y2 < Project->y2) Project->y2 = New.y2;
1111 
1112     *visible = true;
1113   }
1114 }
1115 
1116 
1117 
1118 /*****************************************************************************
1119 *
1120 * FUNCTION
1121 *
1122 *   transform_point
1123 *
1124 * INPUT
1125 *
1126 *   P - Point to transform
1127 *
1128 * OUTPUT
1129 *
1130 *   P
1131 *
1132 * RETURNS
1133 *
1134 * AUTHOR
1135 *
1136 *   Dieter Bayer
1137 *
1138 * DESCRIPTION
1139 *
1140 *   Transform a point from the world coordinate system to the viewer's
1141 *   coordinate system.
1142 *
1143 * CHANGES
1144 *
1145 *   May 1994 : Creation.
1146 *
1147 ******************************************************************************/
1148 
transform_point(VECTOR P)1149 static void transform_point(VECTOR P)
1150 {
1151   DBL x,y,z;
1152 
1153   x = P[X] - gO[X];
1154   y = P[Y] - gO[Y];
1155   z = P[Z] - gO[Z];
1156 
1157   P[X] = gU[X] * x + gU[Y] * y + gU[Z] * z;
1158   P[Y] = gV[X] * x + gV[Y] * y + gV[Z] * z;
1159   P[Z] = gW[X] * x + gW[Y] * y + gW[Z] * z;
1160 }
1161 
1162 
1163 
1164 /*****************************************************************************
1165 *
1166 * FUNCTION
1167 *
1168 *   Init_View_Coordinates
1169 *
1170 * INPUT
1171 *
1172 * OUTPUT
1173 *
1174 * RETURNS
1175 *
1176 * AUTHOR
1177 *
1178 *   Dieter Bayer
1179 *
1180 * DESCRIPTION
1181 *
1182 *   Init the matrices and vectors used to transform a point from
1183 *   the world coordinate system to the viewer's coordinate system.
1184 *
1185 * CHANGES
1186 *
1187 *   May 1994 : Creation.
1188 *
1189 ******************************************************************************/
1190 
init_view_coordinates()1191 static void init_view_coordinates()
1192 {
1193   DBL k1, k2, k3, up_length, right_length;
1194   MATRIX A, B;
1195 
1196   Assign_Vector(gU, Frame.Camera->Right);
1197   Assign_Vector(gV, Frame.Camera->Up);
1198   Assign_Vector(gW, Frame.Camera->Direction);
1199 
1200   VAdd (gO, Frame.Camera->Location, Frame.Camera->Direction);
1201 
1202   VNormalize(gU,gU);
1203   VNormalize(gV,gV);
1204   VNormalize(gW,gW);
1205 
1206   VDot(k1, gU, gV);
1207   VDot(k2, gU, gW);
1208   VDot(k3, gV, gW);
1209 
1210   if ((fabs(k1) > EPSILON) || (fabs(k2) > EPSILON) || (fabs(k3) > EPSILON))
1211   {
1212     Error("Cannot use non-perpendicular camera vectors with vista buffer.");
1213   }
1214 
1215   VLength (Distance, Frame.Camera->Direction);
1216 
1217   VLength (up_length, Frame.Camera->Up);
1218   VLength (right_length, Frame.Camera->Right);
1219 
1220   VScaleEq (gU, 1.0/right_length);
1221   VScaleEq (gV, 1.0/up_length);
1222   VScaleEq (gW, 1.0/Distance);
1223 
1224   A[0][0] = gU[X]; A[0][1] = gU[Y]; A[0][2] = gU[Z]; A[0][3] = 0.0;
1225   A[1][0] = gV[X]; A[1][1] = gV[Y]; A[1][2] = gV[Z]; A[1][3] = 0.0;
1226   A[2][0] = gW[X]; A[2][1] = gW[Y]; A[2][2] = gW[Z]; A[2][3] = 0.0;
1227   A[3][0] = 0.0;  A[3][1] = 0.0;  A[3][2] = 0.0;  A[3][3] = 1.0;
1228 
1229   B[0][0] = 1.0; B[0][1] = 0.0; B[0][2] = 0.0; B[0][3] = -gO[X];
1230   B[1][0] = 0.0; B[1][1] = 1.0; B[1][2] = 0.0; B[1][3] = -gO[Y];
1231   B[2][0] = 0.0; B[2][1] = 0.0; B[2][2] = 1.0; B[2][3] = -gO[Z];
1232   B[3][0] = 0.0; B[3][1] = 0.0; B[3][2] = 0.0; B[3][3] = 1.0;
1233 
1234   MTimesC(WC2VC, A, B);
1235   MInvers(WC2VCinv, WC2VC);
1236 }
1237 
1238 
1239 
1240 /*****************************************************************************
1241 *
1242 * FUNCTION
1243 *
1244 *   get_perspective_projection
1245 *
1246 * INPUT
1247 *
1248 *   Object   - Object to project
1249 *   Project  - Projection
1250 *   infinite - Flag if object is infinite
1251 *
1252 * OUTPUT
1253 *
1254 *   Project
1255 *
1256 * RETURNS
1257 *
1258 * AUTHOR
1259 *
1260 *   Dieter Bayer
1261 *
1262 * DESCRIPTION
1263 *
1264 *   Get the perspective projection of a single object, i.e.
1265 *   the smallest rectangle enclosing the object's image on the screen.
1266 *
1267 * CHANGES
1268 *
1269 *   May 1994 : Creation.
1270 *
1271 ******************************************************************************/
1272 
get_perspective_projection(OBJECT * Object,PROJECT * Project,int infinite)1273 static void get_perspective_projection(OBJECT *Object, PROJECT *Project, int infinite)
1274 {
1275   int visible;
1276   METHODS *Methods;
1277 
1278   visible = false;
1279 
1280   Methods = Object->Methods;
1281 
1282   /* If the object is infinite, there's no sense of projecting */
1283 
1284   if (!infinite)
1285   {
1286     if ((Methods == &Box_Methods) ||
1287         (Methods == &Smooth_Triangle_Methods) ||
1288         (Methods == &Triangle_Methods) ||
1289         (Methods == &HField_Methods))
1290     {
1291       if (Methods == &Box_Methods)
1292         project_box(Project, Object, &visible);
1293 
1294       if (Methods == &HField_Methods)
1295         project_hfield(Project, Object, &visible);
1296 
1297       if (Methods == &Smooth_Triangle_Methods)
1298         project_smooth_triangle(Project, Object, &visible);
1299 
1300       if (Methods == &Triangle_Methods)
1301         project_triangle(Project, Object, &visible);
1302     }
1303     else
1304     {
1305       project_bounds(Project, &Object->BBox, &visible);
1306     }
1307   }
1308 
1309   if (visible)
1310   {
1311     if (opts.Options & ANTIALIAS)
1312     {
1313       /* Increase the rectangle to make sure that nothing will be missed.
1314          For anti-aliased images increase by a larger amount. */
1315 
1316       Project->x1 = max (0,                     Project->x1 - 2);
1317       Project->x2 = min (Frame.Screen_Width-1,  Project->x2 + 2);
1318       Project->y1 = max (-1,                    Project->y1 - 2);
1319       Project->y2 = min (Frame.Screen_Height-1, Project->y2 + 2);
1320     }
1321     else
1322     {
1323       /* Increase the rectangle to make sure that nothing will be missed. */
1324 
1325       Project->x1 = max (0,                     Project->x1 - 1);
1326       Project->x2 = min (Frame.Screen_Width-1,  Project->x2 + 1);
1327       Project->y1 = max (0,                     Project->y1 - 1);
1328       Project->y2 = min (Frame.Screen_Height-1, Project->y2 + 1);
1329     }
1330   }
1331   else
1332   {
1333     if (!infinite)
1334     {
1335       /* Object is invisible (the camera can't see it) */
1336 
1337       Project->x1 = Project->y1 = MAX_BUFFER_ENTRY;
1338       Project->x2 = Project->y2 = MIN_BUFFER_ENTRY;
1339     }
1340   }
1341 }
1342 
1343 
1344 
1345 /*****************************************************************************
1346 *
1347 * FUNCTION
1348 *
1349 *   get_orthographic_projection
1350 *
1351 * INPUT
1352 *
1353 *   Object   - Object to project
1354 *   Project  - Projection
1355 *   infinite - Flag if object is infinite
1356 *
1357 * OUTPUT
1358 *
1359 *   Project
1360 *
1361 * RETURNS
1362 *
1363 * AUTHOR
1364 *
1365 *   Dieter Bayer
1366 *
1367 * DESCRIPTION
1368 *
1369 *   Get the orthographic projection of a single object, i.e.
1370 *   the smallest rectangle enclosing the object's image on the screen.
1371 *
1372 * CHANGES
1373 *
1374 *   May 1994 : Creation.
1375 *
1376 ******************************************************************************/
1377 
get_orthographic_projection(OBJECT * Object,PROJECT * Project,int infinite)1378 static void get_orthographic_projection(OBJECT *Object, PROJECT *Project, int infinite)
1379 {
1380   int visible, i, x, y;
1381   VECTOR P[8];
1382 
1383   visible = false;
1384 
1385   /* If the object is infinite, there's no sense of projecting */
1386 
1387   if (!infinite)
1388   {
1389     /* The following could be done better but since only a minority of all
1390        objects in a scene are partially visible I don't think it's worth it. */
1391 
1392     for (i = 0; i < 8; i++)
1393     {
1394       P[i][X] = ((i & 1) ? Object->BBox.Lengths[X] : 0.0) + Object->BBox.Lower_Left[X];
1395       P[i][Y] = ((i & 2) ? Object->BBox.Lengths[Y] : 0.0) + Object->BBox.Lower_Left[Y];
1396       P[i][Z] = ((i & 4) ? Object->BBox.Lengths[Z] : 0.0) + Object->BBox.Lower_Left[Z];
1397 
1398       transform_point(P[i]);
1399 
1400       /* Check if bounding box is visible */
1401 
1402       if (P[i][Z] >= 0.0) visible = true;
1403     }
1404 
1405     /* Now get the projection */
1406 
1407     if (visible)
1408     {
1409       Project->x1 = Project->y1 = MAX_BUFFER_ENTRY;
1410       Project->x2 = Project->y2 = MIN_BUFFER_ENTRY;
1411 
1412       for (i = 0; i < 8; i++)
1413       {
1414         /* The visible area is -0.5...+0.5/-0.5...+0.5 */
1415 
1416         if (P[i][X] < -0.5) P[i][X] = -0.5;
1417         if (P[i][X] >  0.5) P[i][X] =  0.5;
1418         if (P[i][Y] < -0.5) P[i][Y] = -0.5;
1419         if (P[i][Y] >  0.5) P[i][Y] =  0.5;
1420 
1421         x = Frame.Screen_Width/2  + (int)(Frame.Screen_Width  * P[i][X]);
1422         y = Frame.Screen_Height/2 - (int)(Frame.Screen_Height * P[i][Y]);
1423 
1424         if (x < Project->x1) Project->x1 = x;
1425         if (x > Project->x2) Project->x2 = x;
1426         if (y < Project->y1) Project->y1 = y;
1427         if (y > Project->y2) Project->y2 = y;
1428       }
1429     }
1430   }
1431 
1432   if (visible)
1433   {
1434     if (opts.Options & ANTIALIAS)
1435     {
1436       /* Increase the rectangle to make sure that nothing will be missed.
1437          For anti-aliased images decrease the lower borders. */
1438 
1439       Project->x1 = max (0,                     Project->x1 - 2);
1440       Project->x2 = min (Frame.Screen_Width-1,  Project->x2 + 1);
1441       Project->y1 = max (-1,                    Project->y1 - 2);
1442       Project->y2 = min (Frame.Screen_Height-1, Project->y2 + 1);
1443     }
1444     else
1445     {
1446       /* Increase the rectangle to make sure that nothing will be missed. */
1447 
1448       Project->x1 = max (0,                     Project->x1 - 1);
1449       Project->x2 = min (Frame.Screen_Width-1,  Project->x2 + 1);
1450       Project->y1 = max (0,                     Project->y1 - 1);
1451       Project->y2 = min (Frame.Screen_Height-1, Project->y2 + 1);
1452     }
1453   }
1454   else
1455   {
1456     if (!infinite)
1457     {
1458       /* Object is invisible (the camera can't see it) */
1459 
1460       Project->x1 = Project->y1 = MAX_BUFFER_ENTRY;
1461       Project->x2 = Project->y2 = MIN_BUFFER_ENTRY;
1462     }
1463   }
1464 }
1465 
1466 
1467 
1468 /*****************************************************************************
1469 *
1470 * FUNCTION
1471 *
1472 *   project_object
1473 *
1474 * INPUT
1475 *
1476 *   Object   - Object to project
1477 *   Project  - Projection
1478 *
1479 * OUTPUT
1480 *
1481 *   Project
1482 *
1483 * RETURNS
1484 *
1485 * AUTHOR
1486 *
1487 *   Dieter Bayer
1488 *
1489 * DESCRIPTION
1490 *
1491 *   Get the projection of a single object depending on the camera
1492 *   used (perspective/orthographic).
1493 *
1494 * CHANGES
1495 *
1496 *   May 1994 : Creation.
1497 *
1498 ******************************************************************************/
1499 
project_object(OBJECT * Object,PROJECT * Project)1500 static void project_object(OBJECT *Object, PROJECT *Project)
1501 {
1502   int infinite;
1503 
1504   /* Init project fields, assuming the object is visible! */
1505 
1506   Project->x1 = Project->y1 = MIN_BUFFER_ENTRY;
1507   Project->x2 = Project->y2 = MAX_BUFFER_ENTRY;
1508 
1509   infinite = Test_Flag(Object, INFINITE_FLAG);
1510 
1511   switch (Frame.Camera->Type)
1512   {
1513     case PERSPECTIVE_CAMERA:
1514 
1515       get_perspective_projection(Object, Project, infinite);
1516       break;
1517 
1518     case ORTHOGRAPHIC_CAMERA:
1519 
1520       get_orthographic_projection(Object, Project, infinite);
1521       break;
1522 
1523     default:
1524 
1525       Error("Wrong camera type in project_object().");
1526       break;
1527   }
1528 }
1529 
1530 
1531 
1532 /*****************************************************************************
1533 *
1534 * FUNCTION
1535 *
1536 *   project_bounding_slab
1537 *
1538 * INPUT
1539 *
1540 *   Project  - Projection
1541 *   Tree     - Current node/leaf
1542 *   Object   - Node/leaf in bounding slab hierarchy
1543 *
1544 * OUTPUT
1545 *
1546 *   Project, Tree
1547 *
1548 * RETURNS
1549 *
1550 * AUTHOR
1551 *
1552 *   Dieter Bayer
1553 *
1554 * DESCRIPTION
1555 *
1556 *   Project the bounding slab hierarchy onto the screen and thus create
1557 *   the vista buffer hierarchy.
1558 *
1559 * CHANGES
1560 *
1561 *   May 1994 : Creation.
1562 *
1563 ******************************************************************************/
1564 
project_bounding_slab(PROJECT * Project,PROJECT_TREE_NODE ** Tree,BBOX_TREE * Node)1565 static void project_bounding_slab(PROJECT *Project, PROJECT_TREE_NODE **Tree, BBOX_TREE *Node)
1566 {
1567   short int i;
1568   PROJECT Temp;
1569   PROJECT_TREE_LEAF *Leaf;
1570   PROJECT_TREE_NODE New;
1571 
1572   if (Node->Entries)
1573   {
1574     /* Current object is a bounding object, i.e. a node in the slab tree. */
1575 
1576     /* First, init new entry. */
1577 
1578     New.Entries = 0;
1579 
1580     New.Node = Node;
1581 
1582     New.Project.x1 = New.Project.y1 = MAX_BUFFER_ENTRY;
1583     New.Project.x2 = New.Project.y2 = MIN_BUFFER_ENTRY;
1584 
1585     /* Allocate temporary memory for node/leaf entries. */
1586 
1587     New.Entry = (PROJECT_TREE_NODE **)POV_MALLOC(Node->Entries*sizeof(PROJECT_TREE_NODE *), "temporary tree entry");
1588 
1589     /* This is no leaf, it's a node. */
1590 
1591     New.is_leaf = false;
1592 
1593     /* Second, get new entry, i.e. project node's entries. */
1594 
1595     for (i = 0; i < Node->Entries; i++)
1596     {
1597       New.Entry[i] = NULL;
1598 
1599       project_bounding_slab(&Temp, &New.Entry[New.Entries], Node->Node[i]);
1600 
1601       /* Use only visible entries. */
1602 
1603       if (New.Entry[New.Entries] != NULL)
1604       {
1605         New.Project.x1 = min(New.Project.x1, Temp.x1);
1606         New.Project.x2 = max(New.Project.x2, Temp.x2);
1607         New.Project.y1 = min(New.Project.y1, Temp.y1);
1608         New.Project.y2 = max(New.Project.y2, Temp.y2);
1609 
1610         New.Entries++;
1611       }
1612     }
1613 
1614     /* If there are any visible entries, we'll use them. */
1615 
1616     if (New.Entries > 0)
1617     {
1618       /* If there's only one entry, we won't need a new node. */
1619 
1620       if (New.Entries == 1)
1621       {
1622         *Tree    = New.Entry[0];
1623         *Project = New.Project;
1624       }
1625       else
1626       {
1627         /* Allocate memory for new node in the vista tree. */
1628 
1629         *Tree = (PROJECT_TREE_NODE *)POV_MALLOC(sizeof(PROJECT_TREE_NODE), "vista tree node");
1630 
1631         **Tree = New;
1632 
1633         /* Allocate memory for node/leaf entries. */
1634 
1635         (*Tree)->Entry = (PROJECT_TREE_NODE **)POV_MALLOC(New.Entries*sizeof(PROJECT_TREE_NODE *), "vista tree node");
1636 
1637         POV_MEMCPY((*Tree)->Entry, New.Entry, New.Entries*sizeof(PROJECT_TREE_NODE *));
1638 
1639         *Project = New.Project;
1640       }
1641     }
1642 
1643     /* Get rid of temporary node/leaf entries. */
1644 
1645     POV_FREE(New.Entry);
1646   }
1647   else
1648   {
1649     Do_Cooperate(0);
1650 
1651     /* Current object is a normal object, i.e. a leaf in the slab tree. */
1652 
1653     /* Get object's projection. */
1654 
1655     project_object((OBJECT *)Node->Node, Project);
1656 
1657     /* Is the object visible? */
1658 
1659     if ((Project->x1 <= Project->x2) && (Project->y1 <= Project->y2))
1660     {
1661       /* Allocate memory for new leaf in the vista tree.  */
1662 
1663       *Tree = (PROJECT_TREE_NODE *)POV_MALLOC(sizeof(PROJECT_TREE_LEAF), "vista tree leaf");
1664 
1665       /* Init new leaf. */
1666 
1667       Leaf = (PROJECT_TREE_LEAF *)(*Tree);
1668 
1669       Leaf->Node = Node;
1670 
1671       Leaf->Project = *Project;
1672 
1673       /* Yes, this is a leaf. */
1674 
1675       Leaf->is_leaf = true;
1676     }
1677   }
1678 }
1679 
1680 
1681 
1682 /*****************************************************************************
1683 *
1684 * FUNCTION
1685 *
1686 *   Build_Vista_Buffer
1687 *
1688 * INPUT
1689 *
1690 * OUTPUT
1691 *
1692 * RETURNS
1693 *
1694 * AUTHOR
1695 *
1696 *   Dieter Bayer
1697 *
1698 * DESCRIPTION
1699 *
1700 *   Build the vista tree, i.e. the 2d representation of the bounding slab
1701 *   hierarchy in image space.
1702 *
1703 *   This only works for perspective and orthographic cameras.
1704 *
1705 * CHANGES
1706 *
1707 *   May 1994 : Creation.
1708 *
1709 ******************************************************************************/
1710 
Build_Vista_Buffer()1711 void Build_Vista_Buffer()
1712 {
1713   PROJECT Project;
1714 
1715   Root_Vista = NULL;
1716 
1717   /* Check if vista buffer can be used. */
1718 
1719   if ((!opts.Use_Slabs) ||
1720       (Frame.Camera->Tnormal != NULL) ||
1721       ((Frame.Camera->Type != PERSPECTIVE_CAMERA) && (Frame.Camera->Type != ORTHOGRAPHIC_CAMERA)) ||
1722       ((Frame.Camera->Aperture != 0.0) && (Frame.Camera->Blur_Samples > 0)))
1723   {
1724     opts.Options &= ~USE_VISTA_BUFFER;
1725   }
1726 
1727   if (opts.Options & USE_VISTA_BUFFER)
1728   {
1729     Send_Progress("Creating vista buffer", PROGRESS_CREATING_VISTA_BUFFER);
1730 
1731     init_view_coordinates();
1732 
1733     project_bounding_slab(&Project, &Root_Vista, Root_Object);
1734   }
1735 }
1736 
1737 
1738 
1739 /*****************************************************************************
1740 *
1741 * FUNCTION
1742 *
1743 *   Destroy_Vista_Buffer
1744 *
1745 * INPUT
1746 *
1747 * OUTPUT
1748 *
1749 * RETURNS
1750 *
1751 * AUTHOR
1752 *
1753 *   Dieter Bayer
1754 *
1755 * DESCRIPTION
1756 *
1757 *   Destroy the vista tree.
1758 *
1759 * CHANGES
1760 *
1761 *   Sep 1994 : Creation.
1762 *
1763 ******************************************************************************/
1764 
Destroy_Vista_Buffer()1765 void Destroy_Vista_Buffer()
1766 {
1767   if ((opts.Options & USE_VISTA_BUFFER) && (Root_Vista != NULL))
1768   {
1769     Destroy_Project_Tree(Root_Vista);
1770 
1771     Root_Vista = NULL;
1772   }
1773 }
1774 
1775 
1776 
1777 /*****************************************************************************
1778 *
1779 * FUNCTION
1780 *
1781 *   draw_projection
1782 *
1783 * INPUT
1784 *
1785 *   Project - projection to draw
1786 *   color   - Color to be used
1787 *
1788 * OUTPUT
1789 *
1790 * RETURNS
1791 *
1792 * AUTHOR
1793 *
1794 *   Dieter Bayer
1795 *
1796 * DESCRIPTION
1797 *
1798 *   Draws a projection in the specified color.
1799 *
1800 * CHANGES
1801 *
1802 *   May 1994 : Creation.
1803 *   Jul 1996 : Draw boxes in white when doing grayscale preview
1804 *
1805 ******************************************************************************/
1806 
draw_projection(PROJECT * Project,int color,int * BigRed,int * BigBlue)1807 static void draw_projection(PROJECT *Project, int color, int  *BigRed, int  *BigBlue)
1808 {
1809   int x1, x2, y1, y2, draw_it;
1810   unsigned char r, g, b, gray;
1811   unsigned char a=0;
1812 
1813   gray = (opts.PaletteOption == GREY) ? 255 : 0;
1814 
1815   switch (color)
1816   {
1817     case pRED   : r = 255; g = b = gray; break;
1818     case pGREEN : g = 255; r = b = gray; break;
1819     case pBLUE  : b = 255; r = g = gray; break;
1820     default    : r = g = b = 255;
1821   }
1822 
1823   x1 = Project->x1;
1824   x2 = Project->x2;
1825   y1 = Project->y1;
1826   y2 = Project->y2;
1827 
1828   if ((x1 <= x2) && (y1 <= y2))
1829   {
1830     if (x1 < 0) x1 = 0;
1831     if (x2 < 0) x2 = 0;
1832     if (y1 < 0) y1 = 0;
1833     if (y2 < 0) y2 = 0;
1834 
1835     if (x1 >= Frame.Screen_Width)  x1 = Frame.Screen_Width - 1;
1836     if (x2 >= Frame.Screen_Width)  x2 = Frame.Screen_Width - 1;
1837     if (y1 >= Frame.Screen_Height) y1 = Frame.Screen_Height - 1;
1838     if (y2 >= Frame.Screen_Height) y2 = Frame.Screen_Height - 1;
1839 
1840     /* Check for full-screen rectangle. */
1841 
1842     draw_it = true;
1843 
1844     if ((x1 == 0) && (x2 == Frame.Screen_Width - 1) &&
1845         (y1 == 0) && (y2 == Frame.Screen_Height - 1))
1846     {
1847       draw_it = false;
1848 
1849       switch (color)
1850       {
1851         case pRED   : if (!(*BigRed))  { *BigRed  = draw_it = true; } break;
1852         case pBLUE  : if (!(*BigBlue)) { *BigBlue = draw_it = true; } break;
1853       }
1854     }
1855 
1856     if (draw_it)
1857     {
1858       if(Display_Started)
1859          POV_DISPLAY_PLOT_BOX(opts.Preview_RefCon, x1, y1, x2, y2, r, g, b, a);
1860     }
1861   }
1862 }
1863 
1864 
1865 
1866 /*****************************************************************************
1867 *
1868 * FUNCTION
1869 *
1870 *   draw_vista
1871 *
1872 * INPUT
1873 *
1874 *   Tree - current node/leaf in the vista tree
1875 *
1876 * OUTPUT
1877 *
1878 * RETURNS
1879 *
1880 * AUTHOR
1881 *
1882 *   Dieter Bayer
1883 *
1884 * DESCRIPTION
1885 *
1886 *   Draws recursively all projections of subnodes in the current node.
1887 *
1888 * CHANGES
1889 *
1890 *   May 1994 : Creation.
1891 *
1892 ******************************************************************************/
1893 
draw_vista(PROJECT_TREE_NODE * Tree,int * BigRed,int * BigBlue)1894 static void draw_vista(PROJECT_TREE_NODE *Tree, int  *BigRed, int *BigBlue)
1895 {
1896   unsigned short i;
1897   PROJECT_TREE_LEAF *Leaf;
1898 
1899   Do_Cooperate(1);
1900 
1901   Check_User_Abort(false);
1902 
1903   if (Tree->is_leaf)
1904   {
1905     Leaf = (PROJECT_TREE_LEAF *)Tree;
1906 
1907     if (((OBJECT *)Leaf->Node->Node)->Type & IS_COMPOUND_OBJECT)
1908     {
1909       draw_projection(&Leaf->Project, pBLUE, BigRed, BigBlue);
1910     }
1911     else
1912     {
1913       draw_projection(&Leaf->Project, pRED, BigRed, BigBlue);
1914     }
1915   }
1916   else
1917   {
1918     for (i = 0; i < Tree->Entries; i++)
1919     {
1920       draw_vista(Tree->Entry[i], BigRed, BigBlue);
1921     }
1922   }
1923 
1924   /* draw bounding object's vista */
1925 
1926 /*
1927   draw_projection(&Tree->Project, pGREEN);
1928 */
1929 }
1930 
1931 
1932 
1933 /*****************************************************************************
1934 *
1935 * FUNCTION
1936 *
1937 *   Draw_Vista_Buffer
1938 *
1939 * INPUT
1940 *
1941 * OUTPUT
1942 *
1943 * RETURNS
1944 *
1945 * AUTHOR
1946 *
1947 *   Dieter Bayer
1948 *
1949 * DESCRIPTION
1950 *
1951 *   Draw the vista tree.
1952 *
1953 * CHANGES
1954 *
1955 *   May 1994 : Creation.
1956 *
1957 ******************************************************************************/
1958 
Draw_Vista_Buffer()1959 void Draw_Vista_Buffer()
1960 {
1961   int BigRed, BigBlue;
1962 
1963   BigRed = BigBlue = false;
1964 
1965   if ((Root_Vista != NULL) && (opts.Options & USE_VISTA_DRAW))
1966   {
1967     draw_vista(Root_Vista, &BigRed, &BigBlue);
1968   }
1969 }
1970 
1971 END_POV_NAMESPACE
1972