1 /*
2 RENDER.C
3
4 Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
5 and the "Aleph One" developers.
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 This license is contained in the file "COPYING",
18 which is included with this source code; it is available online at
19 http://www.gnu.org/licenses/gpl.html
20
21 Thursday, September 8, 1994 1:58:20 PM (Jason')
22
23 Friday, September 9, 1994 1:36:15 PM (Jason')
24 on the quads, in the sun.
25 Sunday, September 11, 1994 7:32:49 PM (Jason')
26 the clock on the 540 was wrong, yesterday was Saturday (not Friday). on quads again, but
27 back home now. something will draw before i go to bed tonight. dinner at the nile?
28 Tuesday, September 13, 1994 2:54:56 AM (Jason')
29 no fair!�� it�s still monday, really. with the aid of some graphical debugging the clipping
30 all works now and i�m trying to have the entire floor/ceiling thing going tonight (the nile
31 was closed by the time i got around to taking a shower and heading out).
32 Friday, September 16, 1994 4:06:17 AM (Jason')
33 walls, floors and ceilings texture, wobble, etc. contemplating objects ... maybe this will
34 work after all.
35 Monday, September 19, 1994 11:03:49 AM (Jason')
36 unified xz_clip_vertical_polygon() and z_clip_horizontal_polygon() to get rid of the last
37 known whitespace problem. can�t wait to see what others i have. objects now respect the
38 clipping windows of all nodes they cross.
39 Monday, October 24, 1994 4:35:38 PM (Jason)
40 fixed render sorting problem with objects of equal depth (i.e., parasitic objects).
41 Tuesday, October 25, 1994 5:14:27 PM (Jason')
42 fixed object sort order with respect to nodes and height.
43 Wednesday, October 26, 1994 3:18:59 PM (Jason)
44 fixed half of the object sort order back so that it worked: in order to correctly handle
45 objects below the viewer projecting into higher polygons we need to sort objects inside
46 nodes (to be draw after their walls and ceilings but before their floors).
47 Wednesday, November 2, 1994 3:49:57 PM (Jason)
48 the bottom panel of short split sides sometimes takes on the ceiling lightsource.
49 Tuesday, November 8, 1994 5:29:12 PM (Jason')
50 implemented new transfer modes: _slide, _wander. _render_effect_earthquake doesn�t work
51 yet because the player can shake behind his own shape.
52 Thursday, December 15, 1994 12:15:55 AM (Jason)
53 the object depth sort order problem ocurrs with multiple objects in the same polygon,
54 is some function of relative depths and overlap, and does not seem to involve objects at
55 the same depth. also, it seems to sort many further objects in front of a single closer
56 one.
57 Monday, January 23, 1995 6:53:26 AM (Jason')
58 the way to fix the render object sorting problem is to ignore overlap and sort everything
59 by depth, regardless. imagine: two, far, non-overlapping objects; by the old algorithm their
60 drawing order is irrelevant. when a closer object which overlaps both of them is sorted, it
61 only attempts to lie in front of the closest of the two (leaving the farthest one in an
62 uncertain position). unfortunately this will cause us to do unnecessary promotion and might
63 look stupid.
64 Sunday, March 26, 1995 12:57:39 AM (Jason')
65 media modifications for marathon2; the object sort order problem still exists (the above
66 solution indeed looked stupid).
67 Thursday, March 30, 1995 11:23:35 PM (Jason')
68 tried to fix object sort problem by attempting to assure that objects are always drawn in
69 depth-order within a node.
70 Monday, June 5, 1995 8:37:42 AM (Jason)
71 blood and fire (baby).
72
73 Jan 30, 2000 (Loren Petrich):
74 Added some typecasts
75 Increased MAXIMUM_NODE_ALIASES to 32
76 Added an "assert" for when DEBUG is off in aliases-building section in sort_render_tree().
77
78 Feb 1, 2000 (Loren Petrich):
79 Added growable list of node aliases; replaced static-list node-alias code
80
81 Feb 4, 2000 (Loren Petrich):
82 Changed halt() to assert(false) for better debugging
83
84 Feb 5, 2000 (Loren Petrich):
85 Added growable lists of nodes and also clips for endpoints and lines.
86
87 Feb 6, 2000 (Loren Petrich):
88 Doing initial allocations of the growable lists of various quantities as a defensive measure
89 against memory leaks that seem to occur.
90
91 Feb 9, 2000 (Loren Petrich):
92 Suppressed ambiguous-clip-flag debugging statement;
93 it gets activated only for excessively distant objects
94
95 Feb 10, 2000 (Loren Petrich):
96 Added dynamic-limits setting of MAXIMUM_RENDER_OBJECTS
97
98 Feb 14, 2000 (Loren Petrich):
99 Added test for other-side polygon to LINE_IS_TRANSPARENT() check in next_polygon_along_line()
100
101 Feb 16, 2000 (Loren Petrich):
102 Put in handling of overflow digits for the purpose of doing long distance correctly;
103 also turning several horizontal-coordinate short integers into long ones.
104
105 Feb 17, 2000 (Loren Petrich):
106 Made the sprites long-distance-friendly; there is a bug where they flip around when they
107 go past half the world size, but that's a short-integer wraparound, and the relevant routine
108 is in map.c.
109
110 Feb 18, 2000 (Loren Petrich):
111 Added support for conditional display of weapons-in-hand; so as to support third-person
112 as well as first-person view.
113
114 Feb 21, 2000 (Loren Petrich):
115 Idiot-proofed next_polygon_along_line(), making it quit a loop if it searches a whole circle.
116
117 Feb 24, 2000 (Loren Petrich):
118 Added animated-texture support
119
120 Mar 3, 2000 (Loren Petrich):
121 Set view to normal in initialize_view_data();
122 squashed persistent-extravision bug.
123
124 Mar 5, 2000 (Loren Petrich):
125 Moved extravision-persistence bug fix out of this file.
126
127 Mar 9, 2000 (Loren Petrich):
128 Sorted the nodes by polygon index in sort_render_tree() and used them to speed up searches
129 for nodes with the same polygon index; maps with slowed-down visibility calculations,
130 such as Desla, can become twice as fast.
131
132 Mar 12, 2000 (Loren Petrich):
133 Added OpenGL support
134
135 Mar 14, 2000 (Loren Petrich):
136 Modified data transmitted to OpenGL renderer; it's now collection/color/frame for
137 both walls and sprites. Also added transmission of view data.
138
139 Mar 24, 2000 (Loren Petrich):
140 Added landscape_yaw calculation; this is the yaw of the landscapes' left edges
141
142 Mar 30, 2000 (Loren Petrich):
143 Inspired by Rhys Hill's work, I've created a second tree to contain the visibility nodes;
144 in addition to their visibility tree, they have a polygon-sort tree.
145 This tree is implemented by setting up some additional members of node_data
146 for indicating its structure; there are members for polygon >, polygon <,
147 and the next member of a chain that shares polygon-index values.
148 As a result, the node-aliases list can now be abolished once and for all.
149
150 Jun 11, 2000 (Loren Petrich):
151 Added support for see-through liquids; this requires several changes.
152 The rendering of each map polygon had to be changed so that there would be a
153 separate liquid surface; it would no longer replace the floor or the ceiling.
154 Next, the inhabitant objects had to be done in two passes, one other side, and one view side.
155 Also, whether there is void on the other side had to be indicated, so that
156 waterfalls and the like may look right.
157
158 Jun 28, 2000 (Loren Petrich):
159 Fixed Aaron Davies bug; if a polygon is completely below a liquid, it will not be rendered
160 if the viewpoint is above the liquid; the bug was that it was not rendered if the viewpoint
161 was below the liquid. This only happened if semitransparent liquid surfaces was turned off.
162
163 Jul 10, 2000 (Loren Petrich):
164 Fixed liquid visibility bug in render_tree() that happens when liquid surfaces are not semitransparent;
165 rendering is skipped if the viewpoint is under a liquid and the polygon is high and dry,
166 or else if the viewpoint is above a liquid and the polygon is submerged.
167
168 Jul 17, 2000 (Loren Petrich):
169 Suppressed view-effect resetting in initialize_view_data(),
170 in order to make teleport view-stretching work correctly.
171
172 Aug 9, 2000 (Loren Petrich):
173 Moved most of the rendering code here into separate files with these classes:
174
175 RenderVisTreeClass --
176 creates the visibility tree (which polygons can be seen from which other ones)
177 RenderSortPolyClass --
178 uses that tree to sort the polygons into appropriate depth order
179 RenderPlaceObjsClass --
180 finds which objects are visible and places them into appropriately sorted order
181 RenderRasterize --
182 handles the clipping of each object and requests those objects' rasterization
183
184 Added a rasterizer class; currently, it does everything from the base class,
185 though what it does will be moved into subclasses.
186
187 Sep 2, 2000 (Loren Petrich):
188 Added some idiot-proofing, since the shapes accessor now returns NULL for nonexistent bitmaps
189
190 Nov 12, 2000 (Loren Petrich):
191 Added automap reset before rendering
192
193 Nov 29, 2000 (Loren Petrich):
194 Made teleport static/fold effect optional
195
196 Jan 17, 2001 (Loren Petrich):
197 Added vertical flipping
198 */
199
200
201 #ifdef QUICKDRAW_DEBUG
202 #include "macintosh_cseries.h"
203 #else
204 #include "cseries.h"
205 #endif
206
207 #include "map.h"
208 #include "render.h"
209 #include "interface.h"
210 #include "lightsource.h"
211 #include "media.h"
212 #include "weapons.h"
213 #include "player.h"
214
215 // LP additions
216 #include "dynamic_limits.h"
217 #include "AnimatedTextures.h"
218 #ifdef HAVE_OPENGL
219 #include "OGL_Render.h"
220 #endif
221
222 #ifdef QUICKDRAW_DEBUG
223 #include "shell.h"
224 extern WindowPtr screen_window;
225 #endif
226
227 #include <math.h>
228 #include <string.h>
229 #include <stdlib.h>
230
231 // LP additions for decomposition of this code:
232 #include "RenderVisTree.h"
233 #include "RenderSortPoly.h"
234 #include "RenderPlaceObjs.h"
235 #include "RenderRasterize.h"
236 #include "Rasterizer_SW.h"
237 #ifdef HAVE_OPENGL
238 #include "Rasterizer_OGL.h"
239 #include "RenderRasterize_Shader.h"
240 #include "Rasterizer_Shader.h"
241 #endif
242 #include "preferences.h"
243 #include "screen.h"
244
245 /* use native alignment */
246 #if defined (powerc) || defined (__powerc)
247 #pragma options align=power
248 #endif
249
250 /*
251 //render transparent walls (if a bit is set or if the transparent texture is non-NULL?)
252 //use side lightsources instead of taking them from their polygons
253 //respect dark side bit (darken light intensity by k)
254 //fix solid/opaque endpoint confusion (solidity does not imply opacity)
255
256 there exists a problem where an object can overlap into a polygon which is clipped by something
257 behind the object but that will clip the object because clip windows are subtractive; how
258 is this solved?
259 it�s still possible to get ambiguous clip flags, usually in very narrow (e.g., 1 pixel) windows
260 the renderer has a maximum range beyond which it shits bricks yet which it allows to be exceeded
261 it�s still possible, especially in high-res full-screen, for points to end up (slightly) off
262 the screen (usually discarding these has no noticable effect on the scene)
263 whitespace results when two adjacent polygons are clipped to different vertical windows. this
264 is not trivially solved with the current implementation, and may be acceptable (?)
265
266 //build_base_polygon_index_list() should discard lower polygons for objects above the viewer and
267 // higher polygons for objects below the viewer because we certainly don�t sort objects
268 // correctly in these cases
269 //in strange cases, objects are sorted out of order. this seems to involve players in some way
270 // (i.e., parasitic objects).
271 */
272
273 /* ---------- constants */
274
275 #define EXPLOSION_EFFECT_RANGE (WORLD_ONE/12)
276
277 /* ---------- clip buffer */
278 // Not used for anything
279 #define CLIP_INDEX_BUFFER_SIZE 4096
280
281 vector<uint16> RenderFlagList;
282
283 // uint16 *render_flags;
284
285 // LP additions: decomposition of the rendering code into various objects
286
287 static RenderVisTreeClass RenderVisTree; // Visibility-tree object
288 static RenderSortPolyClass RenderSortPoly; // Polygon-sorting object
289 static RenderPlaceObjsClass RenderPlaceObjs; // Object-placement object
290 static RenderRasterizerClass Render_Classic; // Clipping and rasterization class
291
292 static Rasterizer_SW_Class Rasterizer_SW; // Software rasterizer
293 #ifdef HAVE_OPENGL
294 static Rasterizer_OGL_Class Rasterizer_OGL; // OpenGL rasterizer
295 static Rasterizer_Shader_Class Rasterizer_Shader; // Shader rasterizer
296 static RenderRasterize_Shader Render_Shader; // Shader clipping and rasterization class
297 #endif
298
299 // In Marathon 1-style exploration missions, we check
300 // each player's view for exploration polygons after
301 // this many ticks have elapsed
302 static const int TICKS_PER_EXPLORE = 4;
303
304 // M1 exploration mission helpers
305 static struct view_data explore_view;
306 static RenderVisTreeClass explore_tree;
307
OGL_Rasterizer_Init()308 void OGL_Rasterizer_Init() {
309
310 #ifdef HAVE_OPENGL
311 if (graphics_preferences->screen_mode.acceleration == _opengl_acceleration) {
312 Rasterizer_Shader.setupGL();
313 Render_Shader.setupGL(Rasterizer_Shader);
314 }
315 #endif
316 }
317
318 /* ---------- private prototypes */
319
320 static void update_view_data(struct view_data *view);
321 static void update_render_effect(struct view_data *view);
322 static void shake_view_origin(struct view_data *view, world_distance delta);
323
324 static void render_viewer_sprite_layer(view_data *view, RasterizerClass *RasPtr);
325 void position_sprite_axis(short *x0, short *x1, short scale_width, short screen_width,
326 short positioning_mode, _fixed position, bool flip, world_distance world_left, world_distance world_right);
327
328
329 #ifdef QUICKDRAW_DEBUG
330 static void debug_flagged_points(flagged_world_point2d *points, short count);
331 static void debug_flagged_points3d(flagged_world_point3d *points, short count);
332 static void debug_vector(world_vector2d *v);
333 static void debug_x_line(world_distance x);
334 #endif
335
336 /* ---------- code */
337
allocate_render_memory(void)338 void allocate_render_memory(
339 void)
340 {
341 assert(NUMBER_OF_RENDER_FLAGS<=16);
342 RenderFlagList.resize(RENDER_FLAGS_BUFFER_SIZE);
343
344 // LP addition: check out pointer-arithmetic hack
345 assert(sizeof(void *) == sizeof(POINTER_DATA));
346
347 // LP change: do max allocation
348 RenderVisTree.Resize(MAXIMUM_ENDPOINTS_PER_MAP,MAXIMUM_LINES_PER_MAP);
349 RenderSortPoly.Resize(MAXIMUM_POLYGONS_PER_MAP);
350
351 // LP change: set up pointers
352 RenderSortPoly.RVPtr = &RenderVisTree;
353 RenderPlaceObjs.RVPtr = &RenderVisTree;
354 RenderPlaceObjs.RSPtr = &RenderSortPoly;
355 #ifdef HAVE_OPENGL
356 Render_Classic.RSPtr = Render_Shader.RSPtr = &RenderSortPoly;
357 #else
358 Render_Classic.RSPtr = &RenderSortPoly;
359 #endif
360 }
361
362 /* just in case anyone was wondering, standard_screen_width will usually be the same as
363 screen_width. the renderer assumes that the given field_of_view matches the standard
364 width provided (so if the actual width provided is larger, you'll be able to see more;
365 if it's smaller you'll be able to see less). this allows the destination bitmap to not
366 only grow and shrink while maintaining a constant aspect ratio, but to also change in
367 geometry without effecting the image being projected onto it. if you don't understand
368 this, pass standard_width==width */
initialize_view_data(struct view_data * view,bool ignore_preferences)369 void initialize_view_data(
370 struct view_data *view,
371 bool ignore_preferences)
372 {
373 double two_pi= 8.0*atan(1.0);
374 double half_cone= view->field_of_view*(two_pi/360.0)/2;
375 /* half_cone needs to be extended for non oblique perspective projection (gluPerspective).
376 this is required because the viewing angle is different for about the same field of view */
377 if (!ignore_preferences && graphics_preferences->screen_mode.acceleration == _opengl_acceleration)
378 half_cone= (view->field_of_view * 1.3)*(two_pi/360.0)/2;
379
380 double adjusted_half_cone= (ignore_preferences || View_FOV_FixHorizontalNotVertical()) ?
381 half_cone :
382 atan(view->screen_width*tan(half_cone)/view->standard_screen_width);
383 double world_to_screen;
384
385 view->half_screen_width= view->screen_width/2;
386 view->half_screen_height= view->screen_height/2;
387
388 /* if there�s a round-off error in half_cone, we want to make the cone too big (so when we clip
389 lines �to the edge of the screen� they�re actually off the screen, thus +1.0) */
390 view->half_cone= (angle) (adjusted_half_cone*((double)NUMBER_OF_ANGLES)/two_pi+1.0);
391
392 // LP change: find the adjusted yaw for the landscapes;
393 // this is the effective yaw value for the left edge.
394 // A landscape rotation can also be added if desired.
395 view->landscape_yaw = view->yaw - view->half_cone;
396
397 /* calculate world_to_screen; we could calculate this with standard_screen_width/2 and
398 the old half_cone and get the same result */
399 world_to_screen= view->half_screen_width/tan(adjusted_half_cone);
400 view->world_to_screen_x= view->real_world_to_screen_x= (short) ((world_to_screen/view->horizontal_scale)+0.5);
401 view->world_to_screen_y= view->real_world_to_screen_y= (short) ((world_to_screen/view->vertical_scale)+0.5);
402
403 /* calculate the vertical cone angle; again, overflow instead of underflow when rounding */
404 view->half_vertical_cone= (angle) (NUMBER_OF_ANGLES*atan(((double)view->half_screen_height*view->vertical_scale)/world_to_screen)/two_pi+1.0);
405
406 /* calculate left edge vector */
407 view->untransformed_left_edge.i= view->world_to_screen_x;
408 view->untransformed_left_edge.j= - view->half_screen_width;
409
410 /* calculate right edge vector (negative, so it clips in the right direction) */
411 view->untransformed_right_edge.i= - view->world_to_screen_x;
412 view->untransformed_right_edge.j= - view->half_screen_width;
413
414 /* view needs to know if OpenGL renderer should mimic software's pitch */
415 if (!ignore_preferences && graphics_preferences->screen_mode.acceleration == _opengl_acceleration)
416 view->mimic_sw_perspective = TEST_FLAG(Get_OGL_ConfigureData().Flags, OGL_Flag_MimicSW);
417
418 /* reset any active effects */
419 // LP: this is now called in render_screen(), so we need to disable the initializing
420 }
421
422 /* origin,origin_polygon_index,yaw,pitch,roll,etc. have probably changed since last call */
render_view(struct view_data * view,struct bitmap_definition * destination)423 void render_view(
424 struct view_data *view,
425 struct bitmap_definition *destination)
426 {
427 update_view_data(view);
428
429 /* clear the render flags */
430 objlist_clear(render_flags, RENDER_FLAGS_BUFFER_SIZE);
431
432 ResetOverheadMap();
433 /*
434 #ifdef AUTOMAP_DEBUG
435 memset(automap_lines, 0, (dynamic_world->line_count/8+((dynamic_world->line_count%8)?1:0)*sizeof(byte)));
436 memset(automap_polygons, 0, (dynamic_world->polygon_count/8+((dynamic_world->polygon_count%8)?1:0)*sizeof(byte)));
437 #endif
438 */
439
440 if(view->terminal_mode_active)
441 {
442 /* Render the computer interface. */
443 render_computer_interface(view);
444 }
445 else
446 {
447 // LP: the render objects have a pointer to the current view in them,
448 // so that one can get rid of redundant references to it in them.
449
450 // LP: now from the visibility-tree class
451 /* build the render tree, regardless of map mode, so the automap updates while active */
452 RenderVisTree.view = view;
453 RenderVisTree.build_render_tree();
454
455 /* do something complicated and difficult to explain */
456 if (!view->overhead_map_active || map_is_translucent())
457 {
458 // LP: now from the polygon-sorter class
459 /* sort the render tree (so we have a depth-ordering of polygons) and accumulate
460 clipping information for each polygon */
461 RenderSortPoly.view = view;
462 RenderSortPoly.sort_render_tree();
463
464 // LP: now from the object-placement class
465 /* build the render object list by looking at the sorted render tree */
466 RenderPlaceObjs.view = view;
467 RenderPlaceObjs.build_render_object_list();
468
469 // LP addition: set the current rasterizer to whichever is appropriate here
470 RasterizerClass *RasPtr;
471 #ifdef HAVE_OPENGL
472 if (OGL_IsActive())
473 RasPtr = &Rasterizer_Shader;
474 else
475 {
476 #endif
477 // The software renderer needs this but the OpenGL one doesn't...
478 Rasterizer_SW.screen = destination;
479 RasPtr = &Rasterizer_SW;
480 #ifdef HAVE_OPENGL
481 }
482 #endif
483
484 // Set its view:
485 RasPtr->SetView(*view);
486
487 // Start rendering main view
488 RasPtr->Begin();
489
490 // LP: now from the clipping/rasterizer class
491 #ifdef HAVE_OPENGL
492 RenderRasterizerClass *RenPtr = (graphics_preferences->screen_mode.acceleration == _opengl_acceleration) ? &Render_Shader : &Render_Classic;
493 #else
494 RenderRasterizerClass *RenPtr = &Render_Classic;
495 #endif
496 /* render the object list, back to front, doing clipping on each surface before passing
497 it to the texture-mapping code */
498 RenPtr->view = view;
499 RenPtr->RasPtr = RasPtr;
500 RenPtr->render_tree();
501
502 // LP: won't put this into a separate class
503 /* render the player�s weapons, etc. */
504 if (!RenPtr->renders_viewer_sprites_in_tree()) {
505 render_viewer_sprite_layer(view, RasPtr);
506 }
507
508 // Finish rendering main view
509 RasPtr->End();
510 }
511
512 if (view->overhead_map_active)
513 {
514 /* if the overhead map is active, render it */
515 render_overhead_map(view);
516 }
517 }
518 }
519
start_render_effect(struct view_data * view,short effect)520 void start_render_effect(
521 struct view_data *view,
522 short effect)
523 {
524 view->effect= effect;
525 view->effect_phase= NONE;
526 }
527
check_m1_exploration(void)528 void check_m1_exploration(void)
529 {
530 // Are we even on an exploration mission?
531 if (!(static_world->mission_flags & _mission_exploration_m1))
532 return;
533
534 // Are there still polygons to explore?
535 bool need_exploring = false;
536 short polygon_index;
537 struct polygon_data *polygon;
538 for (polygon_index = 0, polygon = map_polygons;
539 polygon_index < dynamic_world->polygon_count;
540 ++polygon_index, ++polygon)
541 {
542 if (polygon->type == _polygon_must_be_explored)
543 {
544 need_exploring = true;
545 break;
546 }
547 }
548 if (!need_exploring)
549 return;
550
551 // All right, we need to do something.
552 // First, make sure our data is set up.
553 if (!explore_tree.view)
554 {
555 explore_view.overhead_map_active = false;
556 explore_view.terminal_mode_active = false;
557 explore_view.tunnel_vision_active = false;
558 explore_view.effect = NONE;
559 explore_view.horizontal_scale = 1;
560 explore_view.vertical_scale = 1;
561
562 // For cross-player stability, we don't leave any view settings
563 // up to the preferences or MML.
564 explore_view.field_of_view = explore_view.target_field_of_view = 80;
565 explore_view.screen_width = explore_view.standard_screen_width = 640;
566 explore_view.screen_height = 320;
567
568 // We only need to initialize once, since nothing
569 // that we use changes.
570 initialize_view_data(&explore_view, true);
571
572 explore_tree.view = &explore_view;
573 explore_tree.add_to_automap = false;
574 explore_tree.mark_as_explored = true;
575 explore_tree.Resize(MAXIMUM_ENDPOINTS_PER_MAP, MAXIMUM_LINES_PER_MAP);
576 }
577
578 // Check the relevant players' views for exploration polygons.
579 // We check every TICKS_PER_EXLORE ticks, staggered by index.
580 for (int i = (dynamic_world->tick_count % TICKS_PER_EXPLORE);
581 i < dynamic_world->player_count;
582 i += TICKS_PER_EXPLORE)
583 {
584 struct player_data *explore_player = &players[i];
585 explore_view.yaw = explore_player->facing;
586 explore_view.pitch = explore_player->elevation;
587 explore_view.origin = explore_player->camera_location;
588 explore_view.origin_polygon_index = explore_player->camera_polygon_index;
589
590 update_view_data(&explore_view);
591 objlist_clear(render_flags, RENDER_FLAGS_BUFFER_SIZE);
592 // build_render_tree() actually marks the polygons
593 explore_tree.build_render_tree();
594 }
595 }
596
597
598 /* ---------- private code */
599
update_view_data(struct view_data * view)600 static void update_view_data(
601 struct view_data *view)
602 {
603 angle theta;
604
605 // LP change: doing all the FOV changes here:
606 View_AdjustFOV(view->field_of_view,view->target_field_of_view);
607
608 if (view->effect==NONE)
609 {
610 view->world_to_screen_x= view->real_world_to_screen_x;
611 view->world_to_screen_y= view->real_world_to_screen_y;
612 }
613 else
614 {
615 update_render_effect(view);
616 }
617
618 view->untransformed_left_edge.i= view->world_to_screen_x;
619 view->untransformed_right_edge.i= - view->world_to_screen_x;
620
621 /* calculate world_to_screen_y*tan(pitch) */
622 view->dtanpitch= (view->world_to_screen_y*sine_table[view->pitch])/cosine_table[view->pitch];
623
624 /* calculate left cone vector */
625 theta= NORMALIZE_ANGLE(view->yaw-view->half_cone);
626 view->left_edge.i= cosine_table[theta], view->left_edge.j= sine_table[theta];
627
628 /* calculate right cone vector */
629 theta= NORMALIZE_ANGLE(view->yaw+view->half_cone);
630 view->right_edge.i= cosine_table[theta], view->right_edge.j= sine_table[theta];
631
632 /* calculate top cone vector (negative to clip the right direction) */
633 view->top_edge.i= - view->world_to_screen_y;
634 view->top_edge.j= - (view->half_screen_height + view->dtanpitch); /* ==k */
635
636 /* calculate bottom cone vector */
637 view->bottom_edge.i= view->world_to_screen_y;
638 view->bottom_edge.j= - view->half_screen_height + view->dtanpitch; /* ==k */
639
640 /* if we�re sitting on one of the endpoints in our origin polygon, move us back slightly (�1) into
641 that polygon. when we split rays we�re assuming that we�ll never pass through a given
642 vertex in different directions (because if we do the tree becomes a graph) but when
643 we start on a vertex this can happen. this is a destructive modification of the origin. */
644 {
645 short i;
646 struct polygon_data *polygon= get_polygon_data(view->origin_polygon_index);
647
648 for (i= 0;i<polygon->vertex_count;++i)
649 {
650 struct world_point2d *vertex= &get_endpoint_data(polygon->endpoint_indexes[i])->vertex;
651
652 if (vertex->x==view->origin.x && vertex->y==view->origin.y)
653 {
654 world_point2d *ccw_vertex= &get_endpoint_data(polygon->endpoint_indexes[WRAP_LOW(i, polygon->vertex_count-1)])->vertex;
655 world_point2d *cw_vertex= &get_endpoint_data(polygon->endpoint_indexes[WRAP_HIGH(i, polygon->vertex_count-1)])->vertex;
656 world_vector2d inset_vector;
657
658 inset_vector.i= (ccw_vertex->x-vertex->x) + (cw_vertex->x-vertex->x);
659 inset_vector.j= (ccw_vertex->y-vertex->y) + (cw_vertex->y-vertex->y);
660 view->origin.x+= SGN(inset_vector.i);
661 view->origin.y+= SGN(inset_vector.j);
662
663 break;
664 }
665 }
666
667 /* determine whether we are under or over the media boundary of our polygon; we will see all
668 other media boundaries from this orientation (above or below) or fail to draw them. */
669 if (polygon->media_index==NONE)
670 {
671 view->under_media_boundary= false;
672 }
673 else
674 {
675 struct media_data *media= get_media_data(polygon->media_index);
676
677 // LP change: idiot-proofing
678 if (media)
679 {
680 view->under_media_boundary= UNDER_MEDIA(media, view->origin.z);
681 view->under_media_index= polygon->media_index;
682 } else {
683 view->under_media_boundary= false;
684 }
685 }
686 }
687 }
688
update_render_effect(struct view_data * view)689 static void update_render_effect(
690 struct view_data *view)
691 {
692 short effect= view->effect;
693 short phase= view->effect_phase==NONE ? 0 : (view->effect_phase+view->ticks_elapsed);
694 short period;
695
696 view->effect_phase= phase;
697
698 switch (effect)
699 {
700 // LP change: suppressed all the FOV changes
701 case _render_effect_fold_in: case _render_effect_fold_out: period= TICKS_PER_SECOND/2; break;
702 case _render_effect_explosion: period= TICKS_PER_SECOND; break;
703 default:
704 assert(false);
705 break;
706 }
707
708 if (phase>period)
709 {
710 view->effect= NONE;
711 }
712 else
713 {
714 switch (effect)
715 {
716 case _render_effect_explosion:
717 shake_view_origin(view, EXPLOSION_EFFECT_RANGE - ((EXPLOSION_EFFECT_RANGE/2)*phase)/period);
718 break;
719
720 case _render_effect_fold_in:
721 phase= period-phase;
722 case _render_effect_fold_out:
723 /* calculate world_to_screen based on phase */
724 view->world_to_screen_x= view->real_world_to_screen_x + (4*view->real_world_to_screen_x*phase)/period;
725 view->world_to_screen_y= view->real_world_to_screen_y - (view->real_world_to_screen_y*phase)/(period+period/4);
726 break;
727 }
728 }
729 }
730
731
732 /* ---------- transfer modes */
733
734 /* given a transfer mode and phase, cause whatever changes it should cause to a rectangle_definition
735 structure */
instantiate_rectangle_transfer_mode(view_data * view,rectangle_definition * rectangle,short transfer_mode,_fixed transfer_phase)736 void instantiate_rectangle_transfer_mode(
737 view_data *view,
738 rectangle_definition *rectangle,
739 short transfer_mode,
740 _fixed transfer_phase)
741 {
742 // For the 3D-model code
743 rectangle->HorizScale = 1;
744
745 switch (transfer_mode)
746 {
747 case _xfer_invisibility:
748 case _xfer_subtle_invisibility:
749 if (view->shading_mode!=_shading_infravision)
750 {
751 rectangle->transfer_mode= _tinted_transfer;
752 rectangle->shading_tables= get_global_shading_table();
753 rectangle->transfer_data= (transfer_mode==_xfer_invisibility) ? 0x000f : 0x0018;
754 break;
755 }
756 /* if we have infravision, fall through to _textured_transfer (i see you...) */
757 case _xfer_normal:
758 rectangle->transfer_mode= _textured_transfer;
759 break;
760
761 case _xfer_static:
762 case _xfer_50percent_static:
763 rectangle->transfer_mode= _static_transfer;
764 rectangle->transfer_data= (transfer_mode==_xfer_static) ? 0x0000 : 0x8000;
765 break;
766
767 case _xfer_fade_out_static:
768 rectangle->transfer_mode= _static_transfer;
769 rectangle->transfer_data= transfer_phase;
770 break;
771
772 case _xfer_pulsating_static:
773 rectangle->transfer_mode= _static_transfer;
774 rectangle->transfer_data= 0x8000+((0x6000*sine_table[FIXED_INTEGERAL_PART(transfer_phase*NUMBER_OF_ANGLES)])>>TRIG_SHIFT);
775 break;
776
777 case _xfer_fold_in:
778 transfer_phase= FIXED_ONE-transfer_phase; /* do everything backwards */
779 case _xfer_fold_out:
780 if (View_DoStaticEffect())
781 {
782 // Corrected the teleport shrinkage so that the sprite/object
783 // shrinks to its object position and not to its sprite center
784 short delta0 = FIXED_INTEGERAL_PART(int32(((1LL*rectangle->xc - rectangle->x0) - 1) * transfer_phase));
785 short delta1 = FIXED_INTEGERAL_PART(int32(((1LL*rectangle->x1 - rectangle->xc) - 1) * transfer_phase));
786 // short delta= FIXED_INTEGERAL_PART((((rectangle->x1-rectangle->x0)>>1)-1)*transfer_phase);
787
788 rectangle->transfer_mode= _static_transfer;
789 rectangle->transfer_data= (transfer_phase>>1);
790 rectangle->x0+= delta0;
791 rectangle->x1-= delta1;
792 rectangle->HorizScale = 1 - float(transfer_phase)/float(FIXED_ONE);
793 }
794 else
795 rectangle->transfer_mode= _textured_transfer;
796 break;
797
798 #if 0
799 case _xfer_fade_out_to_black:
800 rectangle->shading_tables= get_global_shading_table();
801 if (transfer_phase<FIXED_ONE_HALF)
802 {
803 /* fade to black */
804 rectangle->ambient_shade= (rectangle->ambient_shade*(transfer_phase-FIXED_ONE_HALF))>>(FIXED_FRACTIONAL_BITS-1);
805 rectangle->transfer_mode= _textured_transfer;
806 }
807 else
808 {
809 /* vanish */
810 rectangle->transfer_mode= _tinted_transfer;
811 rectangle->transfer_data= 0x1f - ((0x1f*(FIXED_ONE_HALF-transfer_phase))>>(FIXED_FRACTIONAL_BITS-1));
812 }
813 break;
814 #endif
815
816 // LP change: made an unrecognized mode act like normal
817 default:
818 rectangle->transfer_mode= _textured_transfer;
819 break;
820 }
821 }
822
823 /* given a transfer mode and phase, cause whatever changes it should cause to a polygon_definition
824 structure (unfortunately we need to know whether this is a horizontal or vertical polygon) */
instantiate_polygon_transfer_mode(struct view_data * view,struct polygon_definition * polygon,short transfer_mode,bool horizontal)825 void instantiate_polygon_transfer_mode(
826 struct view_data *view,
827 struct polygon_definition *polygon,
828 short transfer_mode,
829 bool horizontal)
830 {
831 world_distance x0, y0;
832 world_distance vector_magnitude;
833 short alternate_transfer_phase;
834 short transfer_phase = view->tick_count;
835
836 polygon->transfer_mode= _textured_transfer;
837 switch (transfer_mode)
838 {
839 case _xfer_fast_horizontal_slide:
840 case _xfer_horizontal_slide:
841 case _xfer_vertical_slide:
842 case _xfer_fast_vertical_slide:
843 case _xfer_wander:
844 case _xfer_fast_wander:
845 x0= y0= 0;
846 switch (transfer_mode)
847 {
848 case _xfer_fast_horizontal_slide: transfer_phase<<= 1;
849 case _xfer_horizontal_slide: x0= (transfer_phase<<2)&(WORLD_ONE-1); break;
850
851 case _xfer_fast_vertical_slide: transfer_phase<<= 1;
852 case _xfer_vertical_slide: y0= (transfer_phase<<2)&(WORLD_ONE-1); break;
853
854 case _xfer_fast_wander: transfer_phase<<= 1;
855 case _xfer_wander:
856 alternate_transfer_phase= transfer_phase%(10*FULL_CIRCLE);
857 transfer_phase= transfer_phase%(6*FULL_CIRCLE);
858 x0= (cosine_table[NORMALIZE_ANGLE(alternate_transfer_phase)] +
859 (cosine_table[NORMALIZE_ANGLE(2*alternate_transfer_phase)]>>1) +
860 (cosine_table[NORMALIZE_ANGLE(5*alternate_transfer_phase)]>>1))>>(WORLD_FRACTIONAL_BITS-TRIG_SHIFT+2);
861 y0= (sine_table[NORMALIZE_ANGLE(transfer_phase)] +
862 (sine_table[NORMALIZE_ANGLE(2*transfer_phase)]>>1) +
863 (sine_table[NORMALIZE_ANGLE(3*transfer_phase)]>>1))>>(WORLD_FRACTIONAL_BITS-TRIG_SHIFT+2);
864 break;
865 }
866 if (horizontal)
867 {
868 polygon->origin.x+= x0;
869 polygon->origin.y+= y0;
870 }
871 else
872 {
873 vector_magnitude= isqrt(polygon->vector.i*polygon->vector.i + polygon->vector.j*polygon->vector.j);
874 polygon->origin.x+= (polygon->vector.i*x0)/vector_magnitude;
875 polygon->origin.y+= (polygon->vector.j*x0)/vector_magnitude;
876 polygon->origin.z-= y0;
877 }
878 break;
879
880 case _xfer_pulsate:
881 case _xfer_wobble:
882 case _xfer_fast_wobble:
883 if (transfer_mode==_xfer_fast_wobble) transfer_phase*= 15;
884 transfer_phase&= WORLD_ONE/16-1;
885 transfer_phase= (transfer_phase>=WORLD_ONE/32) ? (WORLD_ONE/32+WORLD_ONE/64 - transfer_phase) : (transfer_phase - WORLD_ONE/64);
886 if (horizontal)
887 {
888 polygon->origin.z+= transfer_phase;
889 }
890 else
891 {
892 if (transfer_mode==_xfer_pulsate) /* translate .origin perpendicular to .vector */
893 {
894 world_vector2d offset;
895 world_distance vector_magnitude= isqrt(polygon->vector.i*polygon->vector.i + polygon->vector.j*polygon->vector.j);
896
897 offset.i= (polygon->vector.j*transfer_phase)/vector_magnitude;
898 offset.j= (polygon->vector.i*transfer_phase)/vector_magnitude;
899
900 polygon->origin.x+= offset.i;
901 polygon->origin.y+= offset.j;
902 }
903 else /* ==_xfer_wobble, wobble .vector */
904 {
905 polygon->vector.i+= transfer_phase;
906 polygon->vector.j+= transfer_phase;
907 }
908 }
909 break;
910
911 case _xfer_normal:
912 break;
913
914 case _xfer_smear:
915 polygon->transfer_mode= _solid_transfer;
916 break;
917
918 case _xfer_static:
919 polygon->transfer_mode= _static_transfer;
920 polygon->transfer_data= 0x0000;
921 break;
922
923 case _xfer_landscape:
924 polygon->transfer_mode= _big_landscaped_transfer;
925 break;
926 // case _xfer_big_landscape:
927 // polygon->transfer_mode= _big_landscaped_transfer;
928 // break;
929
930 default:
931 // LP change: made an unrecognized mode act like normal
932 break;
933 }
934 }
935
936 /* ---------- viewer sprite layer (i.e., weapons) */
937
render_viewer_sprite_layer(view_data * view,RasterizerClass * RasPtr)938 static void render_viewer_sprite_layer(view_data *view, RasterizerClass *RasPtr)
939 {
940 rectangle_definition textured_rectangle;
941 weapon_display_information display_data;
942 shape_information_data *shape_information;
943 short count;
944
945 // LP change: bug out if weapons-in-hand are not to be displayed
946 if (!view->show_weapons_in_hand) return;
947
948 // Need to set this...
949 RasPtr->SetForeground();
950
951 // No models here, and completely opaque
952 textured_rectangle.ModelPtr = NULL;
953 textured_rectangle.Opacity = 1;
954
955 /* get_weapon_display_information() returns true if there is a weapon to be drawn. it
956 should initially be passed a count of zero. it returns the weapon�s texture and
957 enough information to draw it correctly. */
958 count= 0;
959 while (get_weapon_display_information(&count, &display_data))
960 {
961 /* fetch relevant shape data */
962 // LP: model-setup code is cribbed from
963 // RenderPlaceObjsClass::build_render_object() in RenderPlaceObjs.cpp
964 #ifdef HAVE_OPENGL
965 // Find which 3D model will take the place of this sprite, if any
966 short ModelSequence;
967 OGL_ModelData *ModelPtr =
968 OGL_GetModelData(GET_COLLECTION(display_data.collection),display_data.shape_index,ModelSequence);
969 #endif
970 shape_information= extended_get_shape_information(display_data.collection, display_data.low_level_shape_index);
971 // Nonexistent frame: skip
972 if (!shape_information) continue;
973 // No need for a fake sprite rectangle, since models are foreground objects
974
975 // LP change: for the convenience of the OpenGL renderer
976 textured_rectangle.ShapeDesc = BUILD_DESCRIPTOR(display_data.collection,0);
977 textured_rectangle.LowLevelShape = display_data.low_level_shape_index;
978 #ifdef HAVE_OPENGL
979 textured_rectangle.ModelPtr = ModelPtr;
980 if (ModelPtr)
981 {
982 textured_rectangle.ModelSequence = ModelSequence;
983 textured_rectangle.ModelFrame = display_data.Frame;
984 textured_rectangle.NextModelFrame = display_data.NextFrame;
985 textured_rectangle.MixFrac = display_data.Ticks > 0 ?
986 float(display_data.Phase)/float(display_data.Ticks) : 0;
987 const world_point3d Zero = {0, 0, 0};
988 textured_rectangle.Position = Zero;
989 textured_rectangle.Azimuth = 0;
990 textured_rectangle.Scale = 1;
991 textured_rectangle.LightDepth = 0;
992 const GLfloat LightDirection[3] = {0, 1, 0}; // y is forward
993 objlist_copy(textured_rectangle.LightDirection,LightDirection,3);
994 RasPtr->SetForegroundView(display_data.flip_horizontal);
995 }
996 #endif
997
998 if (shape_information->flags&_X_MIRRORED_BIT) display_data.flip_horizontal= !display_data.flip_horizontal;
999 if (shape_information->flags&_Y_MIRRORED_BIT) display_data.flip_vertical= !display_data.flip_vertical;
1000
1001 /* calculate shape rectangle */
1002 position_sprite_axis(&textured_rectangle.x0, &textured_rectangle.x1, view->screen_height, view->screen_width, display_data.horizontal_positioning_mode,
1003 display_data.horizontal_position, display_data.flip_horizontal, shape_information->world_left, shape_information->world_right);
1004 position_sprite_axis(&textured_rectangle.y0, &textured_rectangle.y1, view->screen_height, view->screen_height, display_data.vertical_positioning_mode,
1005 display_data.vertical_position, display_data.flip_vertical, -shape_information->world_top, -shape_information->world_bottom);
1006
1007 /* set rectangle bitmap and shading table */
1008 extended_get_shape_bitmap_and_shading_table(display_data.collection, display_data.low_level_shape_index, &textured_rectangle.texture, &textured_rectangle.shading_tables, view->shading_mode);
1009 if (!textured_rectangle.texture) continue;
1010
1011 textured_rectangle.flags= 0;
1012
1013 /* initialize clipping window to full screen */
1014 textured_rectangle.clip_left= 0;
1015 textured_rectangle.clip_right= view->screen_width;
1016 textured_rectangle.clip_top= 0;
1017 textured_rectangle.clip_bottom= view->screen_height;
1018
1019 /* copy mirror flags */
1020 textured_rectangle.flip_horizontal= display_data.flip_horizontal;
1021 textured_rectangle.flip_vertical= display_data.flip_vertical;
1022
1023 /* lighting: depth of zero in the camera�s polygon index */
1024 textured_rectangle.depth= 0;
1025 textured_rectangle.ambient_shade= get_light_intensity(get_polygon_data(view->origin_polygon_index)->floor_lightsource_index);
1026 textured_rectangle.ambient_shade= MAX(shape_information->minimum_light_intensity, textured_rectangle.ambient_shade);
1027 if (view->shading_mode==_shading_infravision) textured_rectangle.flags|= _SHADELESS_BIT;
1028
1029 // Calculate the object's horizontal position
1030 // for the convenience of doing teleport-in/teleport-out
1031 textured_rectangle.xc = (textured_rectangle.x0 + textured_rectangle.x1) >> 1;
1032
1033 /* make the weapon reflect the owner�s transfer mode */
1034 instantiate_rectangle_transfer_mode(view, &textured_rectangle, display_data.transfer_mode, display_data.transfer_phase);
1035
1036 /* and draw it */
1037 // LP: added OpenGL support
1038 RasPtr->texture_rectangle(textured_rectangle);
1039 }
1040 }
1041
position_sprite_axis(short * x0,short * x1,short scale_width,short screen_width,short positioning_mode,_fixed position,bool flip,world_distance world_left,world_distance world_right)1042 void position_sprite_axis(
1043 short *x0,
1044 short *x1,
1045 short scale_width,
1046 short screen_width,
1047 short positioning_mode,
1048 _fixed position,
1049 bool flip,
1050 world_distance world_left,
1051 world_distance world_right)
1052 {
1053 short origin;
1054
1055 /* if this shape is mirrored, reverse the left/right world coordinates */
1056 if (flip)
1057 {
1058 world_distance swap= world_left;
1059 world_left= -world_right, world_right= -swap;
1060 }
1061
1062 switch (positioning_mode)
1063 {
1064 case _position_center:
1065 /* origin is the screen coordinate where the logical center of the shape will be drawn */
1066 origin= (screen_width*position)>>FIXED_FRACTIONAL_BITS;
1067 break;
1068 case _position_low:
1069 case _position_high:
1070 /* origin is in [0,WORLD_ONE] and represents the amount of the weapon visible off the side */
1071 origin= ((world_right-world_left)*position)>>FIXED_FRACTIONAL_BITS;
1072 break;
1073
1074 default:
1075 assert(false);
1076 break;
1077 }
1078
1079 switch (positioning_mode)
1080 {
1081 case _position_high:
1082 *x0= screen_width - ((origin*scale_width)>>WORLD_FRACTIONAL_BITS);
1083 *x1= *x0 + (((world_right-world_left)*scale_width)>>WORLD_FRACTIONAL_BITS);
1084 break;
1085 case _position_low:
1086 *x1= ((origin*scale_width)>>WORLD_FRACTIONAL_BITS);
1087 *x0= *x1 - (((world_right-world_left)*scale_width)>>WORLD_FRACTIONAL_BITS);
1088 break;
1089
1090 case _position_center:
1091 *x0= origin + ((world_left*scale_width)>>WORLD_FRACTIONAL_BITS);
1092 *x1= origin + ((world_right*scale_width)>>WORLD_FRACTIONAL_BITS);
1093 break;
1094
1095 default:
1096 assert(false);
1097 break;
1098 }
1099 }
1100
shake_view_origin(struct view_data * view,world_distance delta)1101 static void shake_view_origin(
1102 struct view_data *view,
1103 world_distance delta)
1104 {
1105 world_point3d new_origin= view->origin;
1106 short half_delta= delta>>1;
1107
1108 new_origin.x+= half_delta - ((delta*sine_table[NORMALIZE_ANGLE((view->tick_count&~3)*(7*FULL_CIRCLE))])>>TRIG_SHIFT);
1109 new_origin.y+= half_delta - ((delta*sine_table[NORMALIZE_ANGLE(((view->tick_count+5*TICKS_PER_SECOND)&~3)*(7*FULL_CIRCLE))])>>TRIG_SHIFT);
1110 new_origin.z+= half_delta - ((delta*sine_table[NORMALIZE_ANGLE(((view->tick_count+7*TICKS_PER_SECOND)&~3)*(7*FULL_CIRCLE))])>>TRIG_SHIFT);
1111
1112 /* only use the new origin if we didn�t cross a polygon boundary */
1113 if (find_line_crossed_leaving_polygon(view->origin_polygon_index, (world_point2d *) &view->origin,
1114 (world_point2d *) &new_origin)==NONE)
1115 {
1116 view->origin= new_origin;
1117 }
1118 }
1119