1 /****************************************************************************
4 * This module implements functions that implement the vista buffer.
6 * This module was written by Dieter Bayer [DB].
8 * from Persistence of Vision(tm) Ray Tracer
9 * Copyright 1996,1999 Persistence of Vision Team
10 *---------------------------------------------------------------------------
11 * NOTICE: This source code file is provided so that users may experiment
12 * with enhancements to POV-Ray and to port the software to platforms other
13 * than those supported by the POV-Ray Team. There are strict rules under
14 * which you are permitted to use this file. The rules are in the file
15 * named POVLEGAL.DOC which should be distributed with this file.
16 * If POVLEGAL.DOC is not available or for more info please contact the POV-Ray
17 * Team Coordinator by email to team-coord@povray.org or visit us on the web at
18 * http://www.povray.org. The latest version of POV-Ray may be found at this site.
20 * This program is based on the popular DKB raytracer version 2.12.
21 * DKBTrace was originally written by David K. Buck.
22 * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
24 * Modifications by Thomas Willhalm, March 1999, used with permission
26 *****************************************************************************/
28 /****************************************************************************
36 * Mar 1994 : Creation.
38 *****************************************************************************/
58 /*****************************************************************************
59 * Local preprocessor defines
60 ******************************************************************************/
64 /*****************************************************************************
66 ******************************************************************************/
70 /*****************************************************************************
72 ******************************************************************************/
75 static MATRIX WC2VC
, WC2VCinv
;
76 static VECTOR gO
, gU
, gV
, gW
;
79 /* Planes for 3d-clipping. */
81 static VECTOR VIEW_VX1
= {-0.8944271910, 0.0, -0.4472135955};
82 static VECTOR VIEW_VX2
= { 0.8944271910, 0.0, -0.4472135955};
83 static VECTOR VIEW_VY1
= {0.0, -0.8944271910, -0.4472135955};
84 static VECTOR VIEW_VY2
= {0.0, 0.8944271910, -0.4472135955};
85 static DBL VIEW_DX1
= 0.4472135955;
86 static DBL VIEW_DX2
= 0.4472135955;
87 static DBL VIEW_DY1
= 0.4472135955;
88 static DBL VIEW_DY2
= 0.4472135955;
90 static PROJECT_TREE_NODE
*Root_Vista
;
94 /*****************************************************************************
96 ******************************************************************************/
98 static void init_view_coordinates (void);
100 static void project_raw_rectangle (PROJECT
*Project
, VECTOR P1
, VECTOR P2
, VECTOR P3
, VECTOR P4
, int *visible
);
101 static void project_raw_triangle (PROJECT
*Project
, VECTOR P1
, VECTOR P2
, VECTOR P3
, int *visible
);
103 static void project_bbox (PROJECT
*Project
, VECTOR
*P
, int *visible
);
104 static void project_bounds (PROJECT
*Project
, BBOX
*BBox
, int *visible
);
106 static void get_perspective_projection (OBJECT
*Object
, PROJECT
*Project
, int infinite
);
107 static void get_orthographic_projection (OBJECT
*Object
, PROJECT
*Project
, int infinite
);
109 static void project_object (OBJECT
*Object
, PROJECT
*Project
);
111 static void project_box (PROJECT
*Project
, OBJECT
*Object
, int *visible
);
112 static void project_hfield (PROJECT
*Project
, OBJECT
*Object
, int *visible
);
113 static void project_triangle (PROJECT
*Project
, OBJECT
*Object
, int *visible
);
114 static void project_smooth_triangle (PROJECT
*Project
, OBJECT
*Object
, int *visible
);
116 static void transform_point (VECTOR P
);
118 static void project_bounding_slab (PROJECT
*Project
, PROJECT_TREE_NODE
**Tree
, BBOX_TREE
*Node
);
120 static int intersect_vista_tree (RAY
*Ray
, PROJECT_TREE_NODE
*Tree
, int x
, INTERSECTION
*Best_Intersection
);
122 static void draw_projection (PROJECT
*Project
, int color
, int *BigRed
, int *BigBlue
);
123 static void draw_vista (PROJECT_TREE_NODE
*Tree
, int *BigRed
, int *BigBlue
);
125 /*****************************************************************************
133 * y - Current scanline number
145 * Prune vista tree, i.e. mark all nodes not on the current line inactive.
149 * May 1994 : Creation.
151 ******************************************************************************/
153 void Prune_Vista_Tree(int y
)
156 PROJECT_TREE_NODE
*Node
, *Sib
;
158 /* If there's no vista tree then return. */
160 if (Root_Vista
== NULL
)
165 Node_Queue
->QSize
= 0;
167 Increase_Counter(stats
[VBuffer_Tests
]);
169 if ((y
< Root_Vista
->Project
.y1
) || (y
> Root_Vista
->Project
.y2
))
171 /* Root doesn't lie on current line --> prune root */
173 Root_Vista
->is_leaf
|= PRUNE_TEMPORARY
;
177 /* Root lies on current line --> unprune root */
179 Increase_Counter(stats
[VBuffer_Tests_Succeeded
]);
181 Root_Vista
->is_leaf
&= ~PRUNE_TEMPORARY
;
183 Node_Queue
->Queue
[(Node_Queue
->QSize
)++] = Root_Vista
;
186 while (Node_Queue
->QSize
> 0)
188 Node
= Node_Queue
->Queue
[--(Node_Queue
->QSize
)];
190 if (Node
->is_leaf
& TRUE
)
192 Increase_Counter(stats
[VBuffer_Tests
]);
194 if ((y
< Node
->Project
.y1
) || (y
> Node
->Project
.y2
))
196 /* Leaf doesn't lie on current line --> prune leaf */
198 Node
->is_leaf
|= PRUNE_TEMPORARY
;
202 /* Leaf lies on current line --> unprune leaf */
204 Increase_Counter(stats
[VBuffer_Tests_Succeeded
]);
206 Node
->is_leaf
&= ~PRUNE_TEMPORARY
;
211 /* Check siblings of the node */
213 for (i
= 0; i
< Node
->Entries
; i
++)
215 Sib
= Node
->Entry
[i
];
217 Increase_Counter(stats
[VBuffer_Tests
]);
219 if ((y
< Sib
->Project
.y1
) || (y
> Sib
->Project
.y2
))
221 /* Sibling doesn't lie on current line --> prune sibling */
223 Sib
->is_leaf
|= PRUNE_TEMPORARY
;
227 /* Sibling lies on current line --> unprune sibling */
229 Increase_Counter(stats
[VBuffer_Tests_Succeeded
]);
231 Sib
->is_leaf
&= ~PRUNE_TEMPORARY
;
233 /* Add sibling to list */
235 /* Reallocate queue if it's too small. */
237 Reinitialize_VLBuffer_Code();
239 Node_Queue
->Queue
[(Node_Queue
->QSize
)++] = Sib
;
248 /*****************************************************************************
257 * Colour - Ray's colour
258 * x - Current x-coordinate
272 * Trace a primary ray using the vista tree.
276 * May 1994 : Creation.
278 * Nov 1994 : Rearranged calls to Fog, Ranibow and Skyblend.
279 * Added call to Atmosphere for atmospheric effects. [DB]
281 * Jan 1995 : Set intersection depth to Max_Distance for infinte rays. [DB]
282 * Jul 1995 : Added code to support alpha channel. [DB]
284 ******************************************************************************/
286 void Trace_Primary_Ray (RAY
*Ray
, COLOUR Colour
, DBL Weight
, int x
)
288 int i
, Intersection_Found
, all_hollow
;
289 INTERSECTION Best_Intersection
;
292 Increase_Counter(stats
[Number_Of_Rays
]);
294 /* Transmittance has to be 1 to make alpha channel output to work. [DB] */
296 Make_ColourA(Colour
, 0.0, 0.0, 0.0, 0.0, 1.0);
298 if ((Trace_Level
> Max_Trace_Level
) || (Weight
< ADC_Bailout
))
300 if (Weight
< ADC_Bailout
)
302 Increase_Counter(stats
[ADC_Saves
]);
308 if (Trace_Level
> Highest_Trace_Level
)
310 Highest_Trace_Level
= Trace_Level
;
313 Best_Intersection
.Depth
= BOUND_HUGE
;
315 /* What objects does this ray intersect? */
317 Intersection_Found
= intersect_vista_tree(Ray
, Root_Vista
, x
, &Best_Intersection
);
319 if (Intersection_Found
)
321 Determine_Apparent_Colour(&Best_Intersection
, Colour
, Ray
, 1.0);
325 /* Infinite ray, set intersection distance. */
327 Best_Intersection
.Depth
= Max_Distance
;
329 Do_Infinite_Atmosphere(Ray
, Colour
);
332 /* Test if all contained objects are hollow. */
338 for (i
= 0; i
<= Ray
->Index
; i
++)
340 if (!Ray
->Interiors
[i
]->hollow
)
349 /* Apply finite atmospheric effects. */
350 if (all_hollow
&& (opts
.Quality_Flags
& Q_VOLUME
))
352 Do_Finite_Atmosphere(Ray
, &Best_Intersection
, Colour
, FALSE
);
358 /*****************************************************************************
362 * intersect_vista_tree
367 * Tree - Vista tree's top-node
368 * x - Current x-coordinate
369 * Best_Intersection - Intersection found
383 * Intersect a PRIMARY ray with the vista tree
384 * (tree pruning is used can be primary ray!!!).
388 * May 1994 : Creation.
390 ******************************************************************************/
392 static int intersect_vista_tree(RAY
*Ray
, PROJECT_TREE_NODE
*Tree
, int x
, INTERSECTION
*Best_Intersection
)
394 INTERSECTION New_Intersection
;
396 /* unsigned size; */ /* tw, mtg */
400 BBOX_TREE
*BBox_Node
;
401 PROJECT_TREE_NODE
*Node
;
403 /* If there's no vista tree then return. */
410 /* Start with an empty priority queue */
412 VLBuffer_Queue
->QSize
= 0;
416 #ifdef BBOX_EXTRA_STATS
417 Increase_Counter(stats
[totalQueueResets
]);
422 Node_Queue
->QSize
= 0;
424 /* Create the direction vectors for this ray */
426 Create_Rayinfo(Ray
, &rayinfo
);
428 /* Fill the priority queue with all possible candidates */
432 Increase_Counter(stats
[VBuffer_Tests
]);
434 if ((x
>= Tree
->Project
.x1
) && (x
<= Tree
->Project
.x2
))
436 Increase_Counter(stats
[VBuffer_Tests_Succeeded
]);
438 Node_Queue
->Queue
[(Node_Queue
->QSize
)++] = Tree
;
441 while (Node_Queue
->QSize
> 0)
443 Tree
= Node_Queue
->Queue
[--(Node_Queue
->QSize
)];
445 switch (Tree
->is_leaf
)
449 /* Check siblings of the unpruned node in 2d */
451 for (i
= 0; i
< Tree
->Entries
; i
++)
453 Node
= Tree
->Entry
[i
];
455 /* Check unpruned siblings only */
457 if (Node
->is_leaf
< PRUNE_CHECK
)
459 Increase_Counter(stats
[VBuffer_Tests
]);
461 if ((x
>= Node
->Project
.x1
) && (x
<= Node
->Project
.x2
))
463 /* Add node to node queue. */
465 Increase_Counter(stats
[VBuffer_Tests_Succeeded
]);
467 /* Reallocate queue if it's too small. */
469 Reinitialize_VLBuffer_Code();
471 Node_Queue
->Queue
[(Node_Queue
->QSize
)++] = Node
;
480 /* Unpruned leaf --> test object's bounding box in 3d */
482 Check_And_Enqueue(VLBuffer_Queue
,
483 ((PROJECT_TREE_LEAF
*)Tree
)->Node
,
484 &(((PROJECT_TREE_LEAF
*)Tree
)->Node
->BBox
),
491 The node/leaf is pruned and needn't be checked */
496 /* Now test the candidates in the priority queue */
498 while (VLBuffer_Queue
->QSize
> 0)
500 Priority_Queue_Delete(VLBuffer_Queue
, &key
, &BBox_Node
);
502 if (key
> Best_Intersection
->Depth
)
505 if (Intersection(&New_Intersection
, (OBJECT
*)BBox_Node
->Node
, Ray
))
507 if (New_Intersection
.Depth
< Best_Intersection
->Depth
)
509 *Best_Intersection
= New_Intersection
;
520 /*****************************************************************************
524 * project_raw_triangle
528 * Project - Triangle's projection
529 * P1, P2, P3 - Triangle's edges
530 * visible - Flag if triangle is visible
544 * Project a triangle onto the screen.
548 * May 1994 : Creation.
550 ******************************************************************************/
552 static void project_raw_triangle (PROJECT
*Project
, VECTOR P1
, VECTOR P2
, VECTOR P3
, int *visible
)
554 VECTOR Points
[MAX_CLIP_POINTS
];
558 Assign_Vector(Points
[0], P1
);
559 Assign_Vector(Points
[1], P2
);
560 Assign_Vector(Points
[2], P3
);
564 /* Clip triangle only if some quick tests say it's necessary.
565 Assuming that only a few triangles need clipping this saves some time.
566 (I don't need to write fabs(1+P?[Z]) since the tests succeed anyway if
567 P?[Z] < -1. Hope the compiler doesn't change the tests' order!) */
569 if ((P1
[Z
] < -1.0) || (P2
[Z
] < -1.0) || (P3
[Z
] < -1.0) ||
570 (fabs(P1
[X
]) > 0.5*(1.0+P1
[Z
])) || (fabs(P1
[Y
]) > 0.5*(1.0+P1
[Z
])) ||
571 (fabs(P2
[X
]) > 0.5*(1.0+P2
[Z
])) || (fabs(P2
[Y
]) > 0.5*(1.0+P2
[Z
])) ||
572 (fabs(P3
[X
]) > 0.5*(1.0+P3
[Z
])) || (fabs(P3
[Y
]) > 0.5*(1.0+P3
[Z
])))
574 Clip_Polygon(Points
, &number
, VIEW_VX1
, VIEW_VX2
, VIEW_VY1
, VIEW_VY2
,
575 VIEW_DX1
, VIEW_DX2
, VIEW_DY1
, VIEW_DY2
);
580 for (i
= 0; i
< number
; i
++)
582 if (Points
[i
][Z
] < -1.0 + EPSILON
)
584 Points
[i
][X
] = Points
[i
][Y
] = 0.0;
588 Points
[i
][X
] /= 1.0 + Points
[i
][Z
];
589 Points
[i
][Y
] /= 1.0 + Points
[i
][Z
];
592 x
= Frame
.Screen_Width
/2 + (int)(Frame
.Screen_Width
* Points
[i
][X
]);
593 y
= Frame
.Screen_Height
/2 - (int)(Frame
.Screen_Height
* Points
[i
][Y
]);
595 if (x
< Project
->x1
) Project
->x1
= x
;
596 if (x
> Project
->x2
) Project
->x2
= x
;
597 if (y
< Project
->y1
) Project
->y1
= y
;
598 if (y
> Project
->y2
) Project
->y2
= y
;
607 /*****************************************************************************
611 * project_raw_rectangle
615 * Project - Rectangle's projection
616 * P1, P2, P3, P4 - Rectangle's edges
617 * visible - Flag if rectangle is visible
631 * Project a rectangle onto the screen.
635 * May 1994 : Creation.
637 ******************************************************************************/
639 static void project_raw_rectangle(PROJECT
*Project
, VECTOR P1
, VECTOR P2
, VECTOR P3
, VECTOR P4
, int *visible
)
641 VECTOR Points
[MAX_CLIP_POINTS
];
645 Assign_Vector(Points
[0], P1
);
646 Assign_Vector(Points
[1], P2
);
647 Assign_Vector(Points
[2], P3
);
648 Assign_Vector(Points
[3], P4
);
652 Clip_Polygon(Points
, &number
, VIEW_VX1
, VIEW_VX2
, VIEW_VY1
, VIEW_VY2
,
653 VIEW_DX1
, VIEW_DX2
, VIEW_DY1
, VIEW_DY2
);
657 for (i
= 0; i
< number
; i
++)
659 if (Points
[i
][Z
] < -1.0 + EPSILON
)
661 Points
[i
][X
] = Points
[i
][Y
] = 0.0;
665 Points
[i
][X
] /= 1.0 + Points
[i
][Z
];
666 Points
[i
][Y
] /= 1.0 + Points
[i
][Z
];
669 x
= Frame
.Screen_Width
/2 + (int)(Frame
.Screen_Width
* Points
[i
][X
]);
670 y
= Frame
.Screen_Height
/2 - (int)(Frame
.Screen_Height
* Points
[i
][Y
]);
672 if (x
< Project
->x1
) Project
->x1
= x
;
673 if (x
> Project
->x2
) Project
->x2
= x
;
674 if (y
< Project
->y1
) Project
->y1
= y
;
675 if (y
> Project
->y2
) Project
->y2
= y
;
685 /*****************************************************************************
693 * Project - Box's projection
695 * visible - Flag if box is visible
709 * Project a box onto the screen.
713 * May 1994 : Creation.
715 ******************************************************************************/
717 static void project_bbox(PROJECT
*Project
, VECTOR
*P
, int *visible
)
722 New
.x1
= MAX_BUFFER_ENTRY
;
723 New
.x2
= MIN_BUFFER_ENTRY
;
724 New
.y1
= MAX_BUFFER_ENTRY
;
725 New
.y2
= MIN_BUFFER_ENTRY
;
729 /* Check if all points lie "in front" of the viewer. */
731 if ((P
[0][Z
] > -1.0) && (P
[1][Z
] > -1.0) && (P
[2][Z
] > -1.0) && (P
[3][Z
] > -1.0) &&
732 (P
[4][Z
] > -1.0) && (P
[5][Z
] > -1.0) && (P
[6][Z
] > -1.0) && (P
[7][Z
] > -1.0))
734 /* Check if all points lie inside the "viewing pyramid". */
736 if ((fabs(P
[0][X
]) <= 0.5*(1.0+P
[0][Z
])) && (fabs(P
[1][X
]) <= 0.5*(1.0+P
[1][Z
])) &&
737 (fabs(P
[2][X
]) <= 0.5*(1.0+P
[2][Z
])) && (fabs(P
[3][X
]) <= 0.5*(1.0+P
[3][Z
])) &&
738 (fabs(P
[4][X
]) <= 0.5*(1.0+P
[4][Z
])) && (fabs(P
[5][X
]) <= 0.5*(1.0+P
[5][Z
])) &&
739 (fabs(P
[6][X
]) <= 0.5*(1.0+P
[6][Z
])) && (fabs(P
[7][X
]) <= 0.5*(1.0+P
[7][Z
])) &&
740 (fabs(P
[0][Y
]) <= 0.5*(1.0+P
[0][Z
])) && (fabs(P
[1][Y
]) <= 0.5*(1.0+P
[1][Z
])) &&
741 (fabs(P
[2][Y
]) <= 0.5*(1.0+P
[2][Z
])) && (fabs(P
[3][Y
]) <= 0.5*(1.0+P
[3][Z
])) &&
742 (fabs(P
[4][Y
]) <= 0.5*(1.0+P
[4][Z
])) && (fabs(P
[5][Y
]) <= 0.5*(1.0+P
[5][Z
])) &&
743 (fabs(P
[6][Y
]) <= 0.5*(1.0+P
[6][Z
])) && (fabs(P
[7][Y
]) <= 0.5*(1.0+P
[7][Z
])))
745 /* No clipping is needed. Just project the points. */
749 for (i
= 0; i
< 8; i
++)
751 if (P
[i
][Z
] < -1.0 + EPSILON
)
753 P
[i
][X
] = P
[i
][Y
] = 0.0;
757 P
[i
][X
] /= 1.0 + P
[i
][Z
];
758 P
[i
][Y
] /= 1.0 + P
[i
][Z
];
761 x
= Frame
.Screen_Width
/2 + (int)(Frame
.Screen_Width
* P
[i
][X
]);
762 y
= Frame
.Screen_Height
/2 - (int)(Frame
.Screen_Height
* P
[i
][Y
]);
764 if (x
< New
.x1
) New
.x1
= x
;
765 if (x
> New
.x2
) New
.x2
= x
;
766 if (y
< New
.y1
) New
.y1
= y
;
767 if (y
> New
.y2
) New
.y2
= y
;
774 project_raw_rectangle(&New
, P
[0], P
[1], P
[3], P
[2], &vis
);
775 project_raw_rectangle(&New
, P
[4], P
[5], P
[7], P
[6], &vis
);
776 project_raw_rectangle(&New
, P
[0], P
[1], P
[5], P
[4], &vis
);
777 project_raw_rectangle(&New
, P
[2], P
[3], P
[7], P
[6], &vis
);
778 project_raw_rectangle(&New
, P
[1], P
[3], P
[7], P
[5], &vis
);
779 project_raw_rectangle(&New
, P
[0], P
[2], P
[6], P
[4], &vis
);
784 if (New
.x1
> Project
->x1
) Project
->x1
= New
.x1
;
785 if (New
.x2
< Project
->x2
) Project
->x2
= New
.x2
;
786 if (New
.y1
> Project
->y1
) Project
->y1
= New
.y1
;
787 if (New
.y2
< Project
->y2
) Project
->y2
= New
.y2
;
794 /*****************************************************************************
802 * Project - Bounding box's projection
803 * BBox - Bounding box
804 * visible - Flag if bounding box is visible
818 * Project a bounding box onto the screen.
822 * May 1994 : Creation.
824 ******************************************************************************/
826 static void project_bounds(PROJECT
*Project
, BBOX
*BBox
, int *visible
)
831 for (i
= 0; i
<8; i
++)
833 P
[i
][X
] = ((i
& 1) ? BBox
->Lengths
[X
] : 0.0) + BBox
->Lower_Left
[X
];
834 P
[i
][Y
] = ((i
& 2) ? BBox
->Lengths
[Y
] : 0.0) + BBox
->Lower_Left
[Y
];
835 P
[i
][Z
] = ((i
& 4) ? BBox
->Lengths
[Z
] : 0.0) + BBox
->Lower_Left
[Z
];
837 transform_point(P
[i
]);
840 project_bbox(Project
, P
, visible
);
845 /*****************************************************************************
853 * Project - Projection
855 * visible - Flag if object is visible
869 * Project a box onto the screen.
873 * May 1994 : Creation.
875 ******************************************************************************/
877 static void project_box(PROJECT
*Project
, OBJECT
*Object
, int *visible
)
885 for (i
= 0; i
<8; i
++)
887 P
[i
][X
] = (i
& 1) ? box
->bounds
[1][X
] : box
->bounds
[0][X
];
888 P
[i
][Y
] = (i
& 2) ? box
->bounds
[1][Y
] : box
->bounds
[0][Y
];
889 P
[i
][Z
] = (i
& 4) ? box
->bounds
[1][Z
] : box
->bounds
[0][Z
];
891 if (box
->Trans
!= NULL
)
893 MTransPoint(P
[i
], P
[i
], box
->Trans
);
896 transform_point(P
[i
]);
899 project_bbox(Project
, P
, visible
);
904 /*****************************************************************************
912 * Project - Projection
914 * visible - Flag if object is visible
928 * Project the bounding box of a height field onto the screen.
932 * May 1994 : Creation.
934 ******************************************************************************/
936 static void project_hfield(PROJECT
*Project
, OBJECT
*Object
, int *visible
)
942 hfield
= (HFIELD
*)Object
;
944 for (i
= 0; i
<8; i
++)
946 Assign_Vector(P
[i
], hfield
->bounding_box
->bounds
[0]);
948 P
[i
][X
] = (i
& 1) ? hfield
->bounding_box
->bounds
[1][X
] : hfield
->bounding_box
->bounds
[0][X
];
949 P
[i
][Y
] = (i
& 2) ? hfield
->bounding_box
->bounds
[1][Y
] : hfield
->bounding_box
->bounds
[0][Y
];
950 P
[i
][Z
] = (i
& 4) ? hfield
->bounding_box
->bounds
[1][Z
] : hfield
->bounding_box
->bounds
[0][Z
];
952 if (hfield
->Trans
!= NULL
)
954 MTransPoint(P
[i
], P
[i
], hfield
->Trans
);
957 transform_point(P
[i
]);
960 project_bbox(Project
, P
, visible
);
965 /*****************************************************************************
973 * Project - Projection
975 * visible - Flag if object is visible
989 * Project a triangle onto the screen.
993 * May 1994 : Creation.
995 ******************************************************************************/
997 static void project_triangle(PROJECT
*Project
, OBJECT
*Object
, int *visible
)
1003 New
.x1
= MAX_BUFFER_ENTRY
;
1004 New
.x2
= MIN_BUFFER_ENTRY
;
1005 New
.y1
= MAX_BUFFER_ENTRY
;
1006 New
.y2
= MIN_BUFFER_ENTRY
;
1008 Assign_Vector(P
[0], ((TRIANGLE
*)Object
)->P1
);
1009 Assign_Vector(P
[1], ((TRIANGLE
*)Object
)->P2
);
1010 Assign_Vector(P
[2], ((TRIANGLE
*)Object
)->P3
);
1012 for (i
= 0; i
< 3; i
++)
1014 transform_point(P
[i
]);
1019 project_raw_triangle(&New
, P
[0], P
[1], P
[2], &vis
);
1023 if (New
.x1
> Project
->x1
) Project
->x1
= New
.x1
;
1024 if (New
.x2
< Project
->x2
) Project
->x2
= New
.x2
;
1025 if (New
.y1
> Project
->y1
) Project
->y1
= New
.y1
;
1026 if (New
.y2
< Project
->y2
) Project
->y2
= New
.y2
;
1034 /*****************************************************************************
1038 * project_smooth_triangle
1042 * Project - Projection
1044 * visible - Flag if object is visible
1058 * Project a smooth triangle onto the screen.
1062 * May 1994 : Creation.
1064 ******************************************************************************/
1066 static void project_smooth_triangle(PROJECT
*Project
, OBJECT
*Object
, int *visible
)
1072 New
.x1
= MAX_BUFFER_ENTRY
;
1073 New
.x2
= MIN_BUFFER_ENTRY
;
1074 New
.y1
= MAX_BUFFER_ENTRY
;
1075 New
.y2
= MIN_BUFFER_ENTRY
;
1077 Assign_Vector(P
[0], ((SMOOTH_TRIANGLE
*)Object
)->P1
);
1078 Assign_Vector(P
[1], ((SMOOTH_TRIANGLE
*)Object
)->P2
);
1079 Assign_Vector(P
[2], ((SMOOTH_TRIANGLE
*)Object
)->P3
);
1081 for (i
= 0; i
< 3; i
++)
1083 transform_point(P
[i
]);
1088 project_raw_triangle(&New
, P
[0], P
[1], P
[2], &vis
);
1092 if (New
.x1
> Project
->x1
) Project
->x1
= New
.x1
;
1093 if (New
.x2
< Project
->x2
) Project
->x2
= New
.x2
;
1094 if (New
.y1
> Project
->y1
) Project
->y1
= New
.y1
;
1095 if (New
.y2
< Project
->y2
) Project
->y2
= New
.y2
;
1103 /*****************************************************************************
1111 * P - Point to transform
1125 * Transform a point from the world coordinate system to the viewer's
1126 * coordinate system.
1130 * May 1994 : Creation.
1132 ******************************************************************************/
1134 static void transform_point(VECTOR P
)
1142 P
[X
] = gU
[X
] * x
+ gU
[Y
] * y
+ gU
[Z
] * z
;
1143 P
[Y
] = gV
[X
] * x
+ gV
[Y
] * y
+ gV
[Z
] * z
;
1144 P
[Z
] = gW
[X
] * x
+ gW
[Y
] * y
+ gW
[Z
] * z
;
1149 /*****************************************************************************
1153 * Init_View_Coordinates
1167 * Init the matrices and vectors used to transform a point from
1168 * the world coordinate system to the viewer's coordinate system.
1172 * May 1994 : Creation.
1174 ******************************************************************************/
1176 static void init_view_coordinates()
1178 DBL k1
, k2
, k3
, up_length
, right_length
;
1181 Assign_Vector(gU
, Frame
.Camera
->Right
);
1182 Assign_Vector(gV
, Frame
.Camera
->Up
);
1183 Assign_Vector(gW
, Frame
.Camera
->Direction
);
1185 VAdd (gO
, Frame
.Camera
->Location
, Frame
.Camera
->Direction
);
1195 if ((fabs(k1
) > EPSILON
) || (fabs(k2
) > EPSILON
) || (fabs(k3
) > EPSILON
))
1197 Error("Cannot use non-perpendicular camera vectors with vista buffer.\n");
1200 VLength (Distance
, Frame
.Camera
->Direction
);
1202 VLength (up_length
, Frame
.Camera
->Up
);
1203 VLength (right_length
, Frame
.Camera
->Right
);
1205 VScaleEq (gU
, 1.0/right_length
);
1206 VScaleEq (gV
, 1.0/up_length
);
1207 VScaleEq (gW
, 1.0/Distance
);
1209 A
[0][0] = gU
[X
]; A
[0][1] = gU
[Y
]; A
[0][2] = gU
[Z
]; A
[0][3] = 0.0;
1210 A
[1][0] = gV
[X
]; A
[1][1] = gV
[Y
]; A
[1][2] = gV
[Z
]; A
[1][3] = 0.0;
1211 A
[2][0] = gW
[X
]; A
[2][1] = gW
[Y
]; A
[2][2] = gW
[Z
]; A
[2][3] = 0.0;
1212 A
[3][0] = 0.0; A
[3][1] = 0.0; A
[3][2] = 0.0; A
[3][3] = 1.0;
1214 B
[0][0] = 1.0; B
[0][1] = 0.0; B
[0][2] = 0.0; B
[0][3] = -gO
[X
];
1215 B
[1][0] = 0.0; B
[1][1] = 1.0; B
[1][2] = 0.0; B
[1][3] = -gO
[Y
];
1216 B
[2][0] = 0.0; B
[2][1] = 0.0; B
[2][2] = 1.0; B
[2][3] = -gO
[Z
];
1217 B
[3][0] = 0.0; B
[3][1] = 0.0; B
[3][2] = 0.0; B
[3][3] = 1.0;
1219 MTimes(WC2VC
, A
, B
);
1220 MInvers(WC2VCinv
, WC2VC
);
1225 /*****************************************************************************
1229 * get_perspective_projection
1233 * Object - Object to project
1234 * Project - Projection
1235 * infinite - Flag if object is infinite
1249 * Get the perspective projection of a single object, i.e.
1250 * the smallest rectangle enclosing the object's image on the screen.
1254 * May 1994 : Creation.
1256 ******************************************************************************/
1258 static void get_perspective_projection(OBJECT
*Object
, PROJECT
*Project
, int infinite
)
1265 Methods
= Object
->Methods
;
1267 /* If the object is infinite, there's no sense of projecting */
1271 if ((Methods
== &Box_Methods
) ||
1272 (Methods
== &Smooth_Triangle_Methods
) ||
1273 (Methods
== &Triangle_Methods
) ||
1274 (Methods
== &HField_Methods
))
1276 if (Methods
== &Box_Methods
)
1277 project_box(Project
, Object
, &visible
);
1279 if (Methods
== &HField_Methods
)
1280 project_hfield(Project
, Object
, &visible
);
1282 if (Methods
== &Smooth_Triangle_Methods
)
1283 project_smooth_triangle(Project
, Object
, &visible
);
1285 if (Methods
== &Triangle_Methods
)
1286 project_triangle(Project
, Object
, &visible
);
1290 project_bounds(Project
, &Object
->BBox
, &visible
);
1296 if (opts
.Options
& ANTIALIAS
)
1298 /* Increase the rectangle to make sure that nothing will be missed.
1299 For anti-aliased images increase by a larger amount. */
1301 Project
->x1
= max (0, Project
->x1
- 2);
1302 Project
->x2
= min (Frame
.Screen_Width
-1, Project
->x2
+ 2);
1303 Project
->y1
= max (-1, Project
->y1
- 2);
1304 Project
->y2
= min (Frame
.Screen_Height
-1, Project
->y2
+ 2);
1308 /* Increase the rectangle to make sure that nothing will be missed. */
1310 Project
->x1
= max (0, Project
->x1
- 1);
1311 Project
->x2
= min (Frame
.Screen_Width
-1, Project
->x2
+ 1);
1312 Project
->y1
= max (0, Project
->y1
- 1);
1313 Project
->y2
= min (Frame
.Screen_Height
-1, Project
->y2
+ 1);
1320 /* Object is invisible (the camera can't see it) */
1322 Project
->x1
= Project
->y1
= MAX_BUFFER_ENTRY
;
1323 Project
->x2
= Project
->y2
= MIN_BUFFER_ENTRY
;
1330 /*****************************************************************************
1334 * get_orthographic_projection
1338 * Object - Object to project
1339 * Project - Projection
1340 * infinite - Flag if object is infinite
1354 * Get the orthographic projection of a single object, i.e.
1355 * the smallest rectangle enclosing the object's image on the screen.
1359 * May 1994 : Creation.
1361 ******************************************************************************/
1363 static void get_orthographic_projection(OBJECT
*Object
, PROJECT
*Project
, int infinite
)
1365 int visible
, i
, x
, y
;
1370 /* If the object is infinite, there's no sense of projecting */
1374 /* The following could be done better but since only a minority of all
1375 objects in a scene are partially visible I don't think it's worth it. */
1377 for (i
= 0; i
< 8; i
++)
1379 P
[i
][X
] = ((i
& 1) ? Object
->BBox
.Lengths
[X
] : 0.0) + Object
->BBox
.Lower_Left
[X
];
1380 P
[i
][Y
] = ((i
& 2) ? Object
->BBox
.Lengths
[Y
] : 0.0) + Object
->BBox
.Lower_Left
[Y
];
1381 P
[i
][Z
] = ((i
& 4) ? Object
->BBox
.Lengths
[Z
] : 0.0) + Object
->BBox
.Lower_Left
[Z
];
1383 transform_point(P
[i
]);
1385 /* Check if bounding box is visible */
1387 if (P
[i
][Z
] >= 0.0) visible
= TRUE
;
1390 /* Now get the projection */
1394 Project
->x1
= Project
->y1
= MAX_BUFFER_ENTRY
;
1395 Project
->x2
= Project
->y2
= MIN_BUFFER_ENTRY
;
1397 for (i
= 0; i
< 8; i
++)
1399 /* The visible area is -0.5...+0.5/-0.5...+0.5 */
1401 if (P
[i
][X
] < -0.5) P
[i
][X
] = -0.5;
1402 if (P
[i
][X
] > 0.5) P
[i
][X
] = 0.5;
1403 if (P
[i
][Y
] < -0.5) P
[i
][Y
] = -0.5;
1404 if (P
[i
][Y
] > 0.5) P
[i
][Y
] = 0.5;
1406 x
= Frame
.Screen_Width
/2 + (int)(Frame
.Screen_Width
* P
[i
][X
]);
1407 y
= Frame
.Screen_Height
/2 - (int)(Frame
.Screen_Height
* P
[i
][Y
]);
1409 if (x
< Project
->x1
) Project
->x1
= x
;
1410 if (x
> Project
->x2
) Project
->x2
= x
;
1411 if (y
< Project
->y1
) Project
->y1
= y
;
1412 if (y
> Project
->y2
) Project
->y2
= y
;
1419 if (opts
.Options
& ANTIALIAS
)
1421 /* Increase the rectangle to make sure that nothing will be missed.
1422 For anti-aliased images decrease the lower borders. */
1424 Project
->x1
= max (0, Project
->x1
- 2);
1425 Project
->x2
= min (Frame
.Screen_Width
-1, Project
->x2
+ 1);
1426 Project
->y1
= max (-1, Project
->y1
- 2);
1427 Project
->y2
= min (Frame
.Screen_Height
-1, Project
->y2
+ 1);
1431 /* Increase the rectangle to make sure that nothing will be missed. */
1433 Project
->x1
= max (0, Project
->x1
- 1);
1434 Project
->x2
= min (Frame
.Screen_Width
-1, Project
->x2
+ 1);
1435 Project
->y1
= max (0, Project
->y1
- 1);
1436 Project
->y2
= min (Frame
.Screen_Height
-1, Project
->y2
+ 1);
1443 /* Object is invisible (the camera can't see it) */
1445 Project
->x1
= Project
->y1
= MAX_BUFFER_ENTRY
;
1446 Project
->x2
= Project
->y2
= MIN_BUFFER_ENTRY
;
1453 /*****************************************************************************
1461 * Object - Object to project
1462 * Project - Projection
1476 * Get the projection of a single object depending on the camera
1477 * used (perspective/orthographic).
1481 * May 1994 : Creation.
1483 ******************************************************************************/
1485 static void project_object(OBJECT
*Object
, PROJECT
*Project
)
1489 /* Init project fields, assuming the object is visible! */
1491 Project
->x1
= Project
->y1
= MIN_BUFFER_ENTRY
;
1492 Project
->x2
= Project
->y2
= MAX_BUFFER_ENTRY
;
1494 infinite
= Test_Flag(Object
, INFINITE_FLAG
);
1496 switch (Frame
.Camera
->Type
)
1498 case PERSPECTIVE_CAMERA
:
1500 get_perspective_projection(Object
, Project
, infinite
);
1503 case ORTHOGRAPHIC_CAMERA
:
1505 get_orthographic_projection(Object
, Project
, infinite
);
1510 Error("Wrong camera type in project_object().\n");
1517 /*****************************************************************************
1521 * project_bounding_slab
1525 * Project - Projection
1526 * Tree - Current node/leaf
1527 * Object - Node/leaf in bounding slab hierarchy
1541 * Project the bounding slab hierarchy onto the screen and thus create
1542 * the vista buffer hierarchy.
1546 * May 1994 : Creation.
1548 ******************************************************************************/
1550 static void project_bounding_slab(PROJECT
*Project
, PROJECT_TREE_NODE
**Tree
, BBOX_TREE
*Node
)
1554 PROJECT_TREE_LEAF
*Leaf
;
1555 PROJECT_TREE_NODE New
;
1559 /* Current object is a bounding object, i.e. a node in the slab tree. */
1561 /* First, init new entry. */
1567 New
.Project
.x1
= New
.Project
.y1
= MAX_BUFFER_ENTRY
;
1568 New
.Project
.x2
= New
.Project
.y2
= MIN_BUFFER_ENTRY
;
1570 /* Allocate temporary memory for node/leaf entries. */
1572 New
.Entry
= (PROJECT_TREE_NODE
**)POV_MALLOC(Node
->Entries
*sizeof(PROJECT_TREE_NODE
*), "temporary tree entry");
1574 /* This is no leaf, it's a node. */
1576 New
.is_leaf
= FALSE
;
1578 /* Second, get new entry, i.e. project node's entries. */
1580 for (i
= 0; i
< Node
->Entries
; i
++)
1582 New
.Entry
[i
] = NULL
;
1584 project_bounding_slab(&Temp
, &New
.Entry
[New
.Entries
], Node
->Node
[i
]);
1586 /* Use only visible entries. */
1588 if (New
.Entry
[New
.Entries
] != NULL
)
1590 New
.Project
.x1
= min(New
.Project
.x1
, Temp
.x1
);
1591 New
.Project
.x2
= max(New
.Project
.x2
, Temp
.x2
);
1592 New
.Project
.y1
= min(New
.Project
.y1
, Temp
.y1
);
1593 New
.Project
.y2
= max(New
.Project
.y2
, Temp
.y2
);
1599 /* If there are any visible entries, we'll use them. */
1601 if (New
.Entries
> 0)
1603 /* If there's only one entry, we won't need a new node. */
1605 if (New
.Entries
== 1)
1607 *Tree
= New
.Entry
[0];
1608 *Project
= New
.Project
;
1612 /* Allocate memory for new node in the vista tree. */
1614 *Tree
= (PROJECT_TREE_NODE
*)POV_MALLOC(sizeof(PROJECT_TREE_NODE
), "vista tree node");
1618 /* Allocate memory for node/leaf entries. */
1620 (*Tree
)->Entry
= (PROJECT_TREE_NODE
**)POV_MALLOC(New
.Entries
*sizeof(PROJECT_TREE_NODE
*), "vista tree node");
1622 memcpy((*Tree
)->Entry
, New
.Entry
, New
.Entries
*sizeof(PROJECT_TREE_NODE
*));
1624 *Project
= New
.Project
;
1628 /* Get rid of temporary node/leaf entries. */
1630 POV_FREE(New
.Entry
);
1636 /* Current object is a normal object, i.e. a leaf in the slab tree. */
1638 /* Get object's projection. */
1640 project_object((OBJECT
*)Node
->Node
, Project
);
1642 /* Is the object visible? */
1644 if ((Project
->x1
<= Project
->x2
) && (Project
->y1
<= Project
->y2
))
1646 /* Allocate memory for new leaf in the vista tree. */
1648 *Tree
= (PROJECT_TREE_NODE
*)POV_MALLOC(sizeof(PROJECT_TREE_LEAF
), "vista tree leaf");
1650 /* Init new leaf. */
1652 Leaf
= (PROJECT_TREE_LEAF
*)(*Tree
);
1656 Leaf
->Project
= *Project
;
1658 /* Yes, this is a leaf. */
1660 Leaf
->is_leaf
= TRUE
;
1667 /*****************************************************************************
1671 * Build_Vista_Buffer
1685 * Build the vista tree, i.e. the 2d representation of the bounding slab
1686 * hierarchy in image space.
1688 * This only works for perspective and orthographic cameras.
1692 * May 1994 : Creation.
1694 ******************************************************************************/
1696 void Build_Vista_Buffer()
1702 /* Check if vista buffer can be used. */
1704 if ((!opts
.Use_Slabs
) ||
1705 (Frame
.Camera
->Tnormal
!= NULL
) ||
1706 ((Frame
.Camera
->Type
!= PERSPECTIVE_CAMERA
) && (Frame
.Camera
->Type
!= ORTHOGRAPHIC_CAMERA
)) ||
1707 ((Frame
.Camera
->Aperture
!= 0.0) && (Frame
.Camera
->Blur_Samples
> 0)))
1709 opts
.Options
&= ~USE_VISTA_BUFFER
;
1712 if (opts
.Options
& USE_VISTA_BUFFER
)
1714 Status_Info("\nCreating vista buffer.");
1716 init_view_coordinates();
1718 project_bounding_slab(&Project
, &Root_Vista
, Root_Object
);
1724 /*****************************************************************************
1728 * Destroy_Vista_Buffer
1742 * Destroy the vista tree.
1746 * Sep 1994 : Creation.
1748 ******************************************************************************/
1750 void Destroy_Vista_Buffer()
1752 if ((opts
.Options
& USE_VISTA_BUFFER
) && (Root_Vista
!= NULL
))
1754 Destroy_Project_Tree(Root_Vista
);
1762 /*****************************************************************************
1770 * Project - projection to draw
1771 * color - Color to be used
1783 * Draws a projection in the specified color.
1787 * May 1994 : Creation.
1788 * Jul 1996 : Draw boxes in white when doing grayscale preview
1790 ******************************************************************************/
1792 static void draw_projection(PROJECT
*Project
, int color
, int *BigRed
, int *BigBlue
)
1794 int x1
, x2
, y1
, y2
, draw_it
;
1795 unsigned char r
, g
, b
, gray
;
1798 gray
= (opts
.PaletteOption
== GREY
) ? 255 : 0;
1802 case RED
: r
= 255; g
= b
= gray
; break;
1803 case GREEN
: g
= 255; r
= b
= gray
; break;
1804 case BLUE
: b
= 255; r
= g
= gray
; break;
1805 default : r
= g
= b
= 255;
1813 if ((x1
<= x2
) && (y1
<= y2
))
1820 if (x1
>= Frame
.Screen_Width
) x1
= Frame
.Screen_Width
- 1;
1821 if (x2
>= Frame
.Screen_Width
) x2
= Frame
.Screen_Width
- 1;
1822 if (y1
>= Frame
.Screen_Height
) y1
= Frame
.Screen_Height
- 1;
1823 if (y2
>= Frame
.Screen_Height
) y2
= Frame
.Screen_Height
- 1;
1825 /* Check for full-screen rectangle. */
1829 if ((x1
== 0) && (x2
== Frame
.Screen_Width
- 1) &&
1830 (y1
== 0) && (y2
== Frame
.Screen_Height
- 1))
1836 case RED
: if (!(*BigRed
)) { *BigRed
= draw_it
= TRUE
; } break;
1837 case BLUE
: if (!(*BigBlue
)) { *BigBlue
= draw_it
= TRUE
; } break;
1843 POV_DISPLAY_PLOT_BOX(x1
,y1
,x2
,y2
,r
,g
,b
,a
);
1850 /*****************************************************************************
1858 * Tree - current node/leaf in the vista tree
1870 * Draws recursively all projections of subnodes in the current node.
1874 * May 1994 : Creation.
1876 ******************************************************************************/
1878 static void draw_vista(PROJECT_TREE_NODE
*Tree
, int *BigRed
, int *BigBlue
)
1881 PROJECT_TREE_LEAF
*Leaf
;
1885 Leaf
= (PROJECT_TREE_LEAF
*)Tree
;
1889 if (((OBJECT
*)Leaf
->Node
->Node
)->Type
& COMPOUND_OBJECT
)
1891 draw_projection(&Leaf
->Project
, BLUE
, BigRed
, BigBlue
);
1895 draw_projection(&Leaf
->Project
, RED
, BigRed
, BigBlue
);
1900 for (i
= 0; i
< Tree
->Entries
; i
++)
1902 draw_vista(Tree
->Entry
[i
], BigRed
, BigBlue
);
1906 /* draw bounding object's vista */
1909 draw_projection(&Tree->Project, GREEN);
1915 /*****************************************************************************
1933 * Draw the vista tree.
1937 * May 1994 : Creation.
1939 ******************************************************************************/
1941 void Draw_Vista_Buffer()
1943 int BigRed
, BigBlue
;
1945 BigRed
= BigBlue
= FALSE
;
1947 if ((Root_Vista
!= NULL
) && (opts
.Options
& USE_VISTA_DRAW
))
1949 draw_vista(Root_Vista
, &BigRed
, &BigBlue
);