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