1 /*
2 
3 	Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
4 	and the "Aleph One" developers.
5 
6 	This program is free software; you can redistribute it and/or modify
7 	it under the terms of the GNU General Public License as published by
8 	the Free Software Foundation; either version 3 of the License, or
9 	(at your option) any later version.
10 
11 	This program is distributed in the hope that it will be useful,
12 	but WITHOUT ANY WARRANTY; without even the implied warranty of
13 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 	GNU General Public License for more details.
15 
16 	This license is contained in the file "COPYING",
17 	which is included with this source code; it is available online at
18 	http://www.gnu.org/licenses/gpl.html
19 
20 	OpenGL Renderer,
21 	by Loren Petrich,
22 	March 12, 2000
23 
24 	This contains functions intended to interface OpenGL 3D-rendering code
25 	with the rest of the Marathon source code.
26 
27 	Much of the setup code is cribbed from the Apple GLUT code, or at least inspired by it.
28 
29 	Late April, 2000:
30 
31 	Moved texture stuff out to OGL_Textures.c/h
32 
33 	Added wall-texture glow mapping.
34 
35 	May 14, 2000:
36 
37 	Added George Marsaglia's random-number generator
38 
39 	May 24, 2000:
40 
41 	Added view-control landscape-options support;
42 	also fixed a bug in the landscape scaling -- it is now close to the software-rendering
43 	scaling.
44 
45 	May 27, 2000:
46 
47 	Added vertical-wall-texture idiot-proofing for texture vectors -- don't render
48 	if horizontal or vertical texture vectors have zero length.
49 
50 	Added support for flat static effect
51 
52 	Added partial transparency of textures (IsBlended)
53 
54 June 11, 2000:
55 
56 	Added support of IsSeeThrough flag for polygons
57 	a texture overlaid on other visible textures is see-through,
58 	while one overlaid on the void is not
59 
60 	Removed TRANSPARENT_BIT test as irrelevant
61 
62 	Made semitransparency optional if the void is on one side of the texture
63 
64 July 7, 2000:
65 
66 	Calculated center correctly in OGL_RenderCrosshairs()
67 
68 Jul 8, 2000:
69 
70 	Modified OGL_SetView() so that one can control whether to allocate a back buffer to draw in
71 	Modified OGL_Copy2D() so that one can control which buffer (front or back)
72 
73 Jul 9, 2000:
74 
75 	Turned BeginFrame() and EndFrame() into OGL_StartMain() and OGL_EndMain()
76 
77 	Also, grabbed some display list ID's for the fonts with glGenLists();
78 	this makes it unnecessary to hardcode their ID's. Also, grabbed a display list ID
79 	for a text string; this makes it easier to repeat its rendering.
80 	Calling OGL_ResetMapFonts() near there -- it resets the font-info cache for the overhead map
81 
82 Jul 17, 2000:
83 	Reorganized the fog setting a bit; now it's set at the beginning of ecah frame.
84 	That ought to make it easier for stuff like Pfhortran to change it.
85 
86 Aug 10, 2000:
87 	Changed the fog handling so that the fog preferences get consulted only once,
88 	when an OpenGL context is created. This will make it easier to change
89 	the fog color and depth on the fly. Also, the presence flag, the depth, and the color
90 	were made nonstatic so that Pfhortran can see them.
91 
92 Sep 21, 2000:
93 	Added partial transparency to static mode
94 
95 Oct 13, 2000 (Loren Petrich)
96 	Converted the animated-texture accounting into Standard Template Library vectors
97 
98 Nov 18, 2000 (Loren Petrich):
99 	Added support for landscape vertical repeats
100 
101 Dec 17, 2000 (Loren Petrich):
102 	Moved fog parameters into OGL_Setup.cpp;
103 	changed "fog is on" in preferences to "fog is allowed"
104 	Added "current fog color" so that landscapes will be correctly colored
105 	in infravision mode.
106 
107 Jan 31, 2002 (Br'fin (Jeremy Parsons)):
108 	Added TARGET_API_MAC_CARBON for AGL.h
109 	Added accessors for datafields now opaque in Carbon
110 	Added a check to make sure AGL_SWAP_RECT is enabled before we try to disable it, trying to squash a bug that occasionally pops up
111 
112 Feb 3, 2002 (Br'fin (Jeremy Parsons) and Loren Petrich):
113 	Centered OpenGL displays under Carbon OS X
114 	Fixed AGL_SWAP_RECT spamming of OS X console
115 
116 Dec 13, 2002 (Loren Petrich):
117 	Added initial preloading of textures to avoid lazy loading of wall textures
118 	on start/restore of level
119 
120 Feb 1, 2003 (Woody Zenfell):
121         Trying to reduce texture-preloading time by eliminating redundant processing
122 
123 April 22, 2003 (Woody Zenfell):
124         Macs can try using aglSetFullScreen() rather than aglSetDrawable() (experimental_rendering)
125 
126 May 3, 2003 (Br'fin (Jeremy Parsons))
127 	Added LowLevelShape workaround for passing LowLevelShape info of sprites
128 	instead of abusing/overflowing shape_descriptors
129 */
130 
131 #include <vector>
132 #include <string.h>
133 #include <stdlib.h>
134 #include <math.h>
135 #include <set>
136 #include <algorithm>	// pair<>, for_each()
137 
138 #include "cseries.h"
139 #include "world.h"
140 #include "shell.h"
141 #include "preferences.h"
142 
143 #ifdef HAVE_OPENGL
144 
145 #include "OGL_Headers.h"
146 
147 #include "interface.h"
148 #include "render.h"
149 #include "map.h"
150 #include "player.h"
151 #include "OGL_Render.h"
152 #include "OGL_Textures.h"
153 #include "OGL_Blitter.h"
154 #include "AnimatedTextures.h"
155 #include "Crosshairs.h"
156 #include "VecOps.h"
157 #include "Random.h"
158 #include "ViewControl.h"
159 #include "OGL_Faders.h"
160 #include "ModelRenderer.h"
161 #include "Logging.h"
162 #include "screen.h"
163 #include "OGL_Shader.h"
164 
165 #include <cmath>
166 
167 extern bool use_lua_hud_crosshairs;
168 
169 // Whether or not OpenGL is active for rendering
170 static bool _OGL_IsActive = false;
171 
172 
173 // Reads off of the current map;
174 // call it to avoid lazy loading of textures
175 typedef std::pair<shape_descriptor,int16> TextureWithTransferMode;
176 static void PreloadTextures();
177 static void PreloadWallTexture(const TextureWithTransferMode& inTexture);
178 
179 
180 // Was OpenGL just inited? If so, then some state may need changing
181 static bool JustInited = false;
182 
183 // The various boundary rectangles (all of the screen, and the view)
184 static Rect SavedScreenBounds = {0,0,0,0};
185 static Rect SavedViewBounds = {0,0,0,0};
186 
187 // For fixing some of the vertices
188 short ViewWidth, ViewHeight;
189 
190 // Adjust the coordinates because those exactly on the right and bottom edges
191 // are sometimes troublesome; they can cause surface polygons to drop out
Adjust_X(short x)192 inline short Adjust_X(short x) {return PIN(x,1,ViewWidth-1);}
Adjust_Y(short y)193 inline short Adjust_Y(short y) {return PIN(y,1,ViewHeight-1);}
194 
195 /*
196 	Coordinate systems: there are several that we must deal with here.
197 
198 	Marathon world coordinates: x and y are horizontal; z is vertical
199 	Origin is map origin; orientation is world;
200 	extent is biggest short
201 
202 	Marathon leveled-world coordinates: x and y are horizontal; z is vertical
203 	Origin is intended map origin (horizontal) and viewpoint location(vertical);
204 	due to an engine bug, the origin's horizontal location is in error;
205 	orientation is world;
206 	extent is biggest short
207 
208 	Marathon centered-world coordinates: x and y are horizontal; z is vertical
209 	Origin is viewpoint location;
210 	orientation is world;
211 	extent is biggest short
212 
213 	Marathon-style eye coordinates: x is outward, y is rightward, and z is upward
214 	Origin is viewpoint location;
215 	orientation is viewpoint;
216 	extent is biggest short
217 
218 	OpenGL-style eye coordinates: x is rightward, y is upward, and z is inward
219 	Origin is viewpoint location; extent is biggest short
220 
221 	Screen coordinates: x is rightward, y is downward, and z is inward
222 	Origin is top left corner of the screen; extent is screen, z between -1 and 1
223 
224 	OpenGL fundamental (clip) coordinates: x is rightward, y is upward, and z is inward
225 	Everything is clipped to a cube that is -1 to +1 in all the coordinates.
226 */
227 
228 // Marathon centered world -> Marathon eye
229 static GLdouble CenteredWorld_2_MaraEye[16];
230 // Marathon world -> Marathon eye
231 static GLdouble World_2_MaraEye[16];
232 // Marathon eye -> OpenGL eye (good for handling vertical-surface data)
233 static const GLdouble MaraEye_2_OGLEye[16] =
234 {	// Correct OpenGL arrangement: transpose to get usual arrangement
235 	0,	0,	-1,	0,
236 	1,	0,	0,	0,
237 	0,	1,	0,	0,
238 	0,	0,	0,	1
239 };
240 
241 // World -> OpenGL eye (good modelview matrix for 3D-model inhabitants)
242 static GLdouble World_2_OGLEye[16];
243 // Centered world -> OpenGL eye (good modelview matrix for 3D-model skyboxes)
244 // (also good for handling horizontal-surface data)
245 static GLdouble CenteredWorld_2_OGLEye[16];
246 
247 // Screen -> clip (good starter matrix; assumes distance is already projected)
248 GLdouble Screen_2_Clip[16];
249 // OpenGL eye -> clip (good projection matrix for 3D models)
250 static GLdouble OGLEye_2_Clip[16];
251 // OpenGL eye -> screen
252 static GLdouble OGLEye_2_Screen[16];
253 
254 
255 // Projection-matrix management: select the appropriate one for what to render
256 enum {
257 	Projection_NONE,
258 	Projection_OpenGL_Eye,	// Appropriate for anything with depth
259 	Projection_Screen		// Appropriate for anything that's flat on the screen
260 };
261 static int ProjectionType = Projection_NONE;
262 
SetProjectionType(int NewProjectionType)263 static void SetProjectionType(int NewProjectionType)
264 {
265 	if (NewProjectionType == ProjectionType) return;
266 
267 	switch(NewProjectionType)
268 	{
269 	case Projection_OpenGL_Eye:
270 		glMatrixMode(GL_PROJECTION);
271 		glLoadMatrixd(OGLEye_2_Clip);
272 		ProjectionType = NewProjectionType;
273 		break;
274 
275 	case Projection_Screen:
276 		glMatrixMode(GL_PROJECTION);
277 		glLoadMatrixd(Screen_2_Clip);
278 		ProjectionType = NewProjectionType;
279 		break;
280 	}
281 }
282 
283 
284 // Rendering depth extent: minimum and maximum z
285 const GLdouble Z_Near = 50;
286 const GLdouble Z_Far = 1.5*64*WORLD_ONE;
287 
288 // Projection coefficients for depth
289 const GLdouble Z_Proj0 = (Z_Far + Z_Near)/(Z_Far - Z_Near);
290 const GLdouble Z_Proj1 = 2*Z_Far*Z_Near/(Z_Far - Z_Near);
291 
292 // Screen <-> world conversion factors and functions
293 GLdouble XScale, YScale, XScaleRecip, YScaleRecip, XOffset, YOffset;
294 
295 // This adjusts a point position in place, using Adjust_X and Adjust_Y
296 // (intended to correct for exactly-on-edge bug)
AdjustPoint(point2d & Pt)297 inline void AdjustPoint(point2d& Pt)
298 {
299 	Pt.x = Adjust_X(Pt.x);
300 	Pt.y = Adjust_Y(Pt.y);
301 }
302 
303 // This produces a ray in OpenGL eye coordinates (z increasing inward);
304 // it sets the point position to its adjusted value
Screen2Ray(point2d & Pt,GLdouble * Ray)305 inline void Screen2Ray(point2d& Pt, GLdouble* Ray)
306 {
307 	AdjustPoint(Pt);
308 	Ray[0] = XScaleRecip*(Pt.x - XOffset);
309 	Ray[1] = YScaleRecip*(Pt.y - YOffset);
310 	Ray[2] = -1;
311 }
312 
313 
314 // Surface-coordinate management;
315 // does all necessary setup tasks for finding where a ray hits a surface
316 struct SurfaceCoords
317 {
318 	// Vectors for increase in a texture coordinate by 1:
319 	// these have a 4th coordinate, for the convenience of the OpenGL-matrix-multiply routines
320 	// that are used to create them. It is, however, ignored here.
321 	// U (along scanlines):
322 	GLdouble U_Vec[4];
323 	// V (scanline-to-scanline):
324 	GLdouble V_Vec[4];
325 
326 	// Complement vectors: (vector).(complement vector) = 1 if for the same quantity, 0 otherwise
327 	// U (along scanlines):
328 	GLdouble U_CmplVec[3];
329 	// V (scanline-to-scanline):
330 	GLdouble V_CmplVec[3];
331 	// W (perpendicular to both)
332 	GLdouble W_CmplVec[3];
333 
334 	// Find complement vectors; return whether the two input vectors were noncollinear
335 	bool FindComplements();
336 };
337 
338 static SurfaceCoords HorizCoords, VertCoords;
339 
340 
341 // Circle constants
342 const double TWO_PI = 8*atan(1.0);
343 const double Radian2Circle = 1/TWO_PI;			// A circle is 2*pi radians
344 const double FullCircleReciprocal = 1/double(FULL_CIRCLE);
345 
346 
347 // Number of static-effect rendering passes
348 #define USE_STIPPLE_STATIC_EFFECT
349 #ifdef USE_STIPPLE_STATIC_EFFECT
350 // For stippling
351 const int StaticEffectPasses = 4;
352 const int SeparableStaticEffectPasses = 4;	// Because of all-or-nothing for all passes
353 #else
354 // for stenciling
355 const int StaticEffectPasses = 3;
356 const int SeparableStaticEffectPasses = 0;	// Do every model triangle separately for safety
357 #endif
358 
359 // Yaw angle (full circle = 1)
360 static double Yaw;
361 
362 // Landscape rescaling to get closer to software-rendering scale
363 static double LandscapeRescale;
364 
365 
366 // Self-luminosity (the "miner's light" effect and weapons flare)
367 static _fixed SelfLuminosity;
368 
369 // Pointer to current fog data:
370 OGL_FogData *CurrFog = NULL;
371 
FogActive()372 inline bool FogActive()
373 {
374 	if (!CurrFog) return false;
375 	bool FogAllowed = TEST_FLAG(Get_OGL_ConfigureData().Flags,OGL_Flag_Fog);
376 	return CurrFog->IsPresent && FogAllowed;
377 }
378 
379 // Current fog color; may be different from the fog color above because of infravision being on
380 static GLfloat CurrFogColor[4] = {0,0,0,0};
381 
382 #ifndef USE_STIPPLE_STATIC_EFFECT
383 // For doing static effects with stenciling
384 static float StencilTxtrOpacity;
385 #endif
386 
387 // Stipple patterns for that static look
388 // (3 color channels + 1 alpha channel) * 32*32 array of bits
389 const int StatPatLen = 32;
390 static GLuint StaticPatterns[4][StatPatLen];
391 
392 // Alternative: partially-transparent flat static
393 static bool UseFlatStatic;
394 static uint16 FlatStaticColor[4];
395 
396 // The randomizer for the static-effect pixels
397 static GM_Random StaticRandom;
398 
399 
400 // Function for setting up the rendering of a 3D model: scaling, clipping, etc.;
401 // returns whether or not the model could be rendered
402 static bool RenderModelSetup(rectangle_definition& RenderRectangle);
403 
404 // Function for rendering a 3D model
405 // Returns whether or not the model could be rendered
406 // (lack of a skin appropriate for the CLUT, for example)
407 static bool RenderModel(rectangle_definition& RenderRectangle, short Collection, short CLUT);
408 
409 // Does the lighting and blending setup;
410 // returns whether or not the texture can be glowmapped
411 // (not the case for infravision, invisible, static)
412 // it gets "IsBlended" off of the texture definition
413 // It returns in args
414 // the "true" blending (invisibility is blended)
415 // the color to use,
416 // and whether the object will be externally lit
417 static bool DoLightingAndBlending(rectangle_definition& RenderRectangle, bool& IsBlended,
418 	GLfloat *Color, bool& ExternallyLit);
419 
420 // Setup and teardown for the static-effect mode
421 static void SetupStaticMode(int16 transfer_data);
422 static void TeardownStaticMode();
423 
424 // Renderer object and its "base" view direction
425 static ModelRenderer ModelRenderObject;
426 GLfloat ViewDir[2];
427 
428 // Shader lists for the object renderer
429 static ModelRenderShader StandardShaders[2];
430 static ModelRenderShader StaticModeShaders[4];
431 
432 // Data for static-mode shader callback: which one in sequence
433 static int SequenceNumbers[4] = {0, 1, 2, 3};
434 
435 // Contains everything that the shader callbacks will need
436 struct ShaderDataStruct
437 {
438 	OGL_ModelData *ModelPtr;
439 	OGL_SkinData *SkinPtr;
440 	GLfloat Color[4];
441 	short Collection, CLUT;
442 };
443 static ShaderDataStruct ShaderData;
444 
445 // Shader callbacks for textures
446 void NormalShader(void *Data);
447 void GlowingShader(void *Data);
448 void StaticModeIndivSetup(int SeqNo);
449 void StaticModeShader(void *Data);
450 
451 // External-lighting data (given to callback)
452 struct LightingDataStruct
453 {
454 	short Type;
455 	short ProjDistance;
456 	GLfloat *Dir;		// Direction to the "light point"
457 	GLfloat AvgLight;	// Average from all directions
458 	GLfloat LightDiff;	// Top-to-bottom difference
459 	GLfloat Opacity;	// For overall-semitransparent models
460 
461 	// This is in 3 sets of 4 values:
462 	// R0, R1, R2, R3, G0, G1, G2, G3, B0, B1, B2, B3;
463 	// the color components are calculated with
464 	// R0*N0 + R1*N1 + R2*N2 + R3
465 	// G0*N0 + G1*N1 + G2*N2 + G3
466 	// B0*N0 + B1*N1 + B2*N2 + B3
467 	GLfloat Colors[3][4];
468 };
469 static LightingDataStruct LightingData;
470 
471 // Shader callback for lighting
472 static void LightingCallback(void *Data, size_t NumVerts, GLfloat *Normals, GLfloat *Positions, GLfloat *Colors);
473 
474 // Set up the shader data
475 static void SetupShaders();
476 
477 
478 // Remember the last blend set so as to avoid redundant blend resettings
479 static short BlendType = OGL_BlendType_Crossfade;
480 
481 // Set the blend, being sure to remember the blend type set to
482 static void SetBlend(short _BlendType);
483 
484 
485 // This function returns whether OpenGL is active;
486 // if OpenGL is not present, it will never be active.
487 
488 // Test for activity;
OGL_IsActive()489 bool OGL_IsActive() { return MainScreenIsOpenGL(); }
490 
491 
492 // It will be black; whether OpenGL is active will be returned
OGL_ClearScreen()493 bool OGL_ClearScreen()
494 {
495 	if (OGL_IsActive())
496 	{
497 		glClearColor(0,0,0,0);
498 		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
499 		return true;
500 	}
501 	else return false;
502 }
503 
504 void OGL_Rasterizer_Init();
505 
506 // Start an OpenGL run (creates a rendering context)
OGL_StartRun()507 bool OGL_StartRun()
508 {
509 	logContext("starting up OpenGL rendering");
510 
511 	if (!OGL_IsPresent()) return false;
512 
513 	// Will stop previous run if it had been active
514 	if (OGL_IsActive()) OGL_StopRun();
515 
516 #ifdef __WIN32__
517 	glewInit();
518 #endif
519 
520 	Wanting_sRGB = false;
521 	if(graphics_preferences->OGL_Configure.Use_sRGB) {
522 	  if(!OGL_CheckExtension("GL_EXT_framebuffer_sRGB") || !OGL_CheckExtension("GL_EXT_texture_sRGB"))
523 	  {
524 	    graphics_preferences->OGL_Configure.Use_sRGB = false;
525 	    logWarning("Gamma corrected blending is not available");
526 	  }
527 	  else
528 	    Wanting_sRGB = true;
529 	}
530 
531 	npotTextures = false;
532 	if (graphics_preferences->OGL_Configure.Use_NPOT)
533 	{
534 	  if (!OGL_CheckExtension("GL_ARB_texture_non_power_of_two"))
535 	  {
536 	    graphics_preferences->OGL_Configure.Use_NPOT = false;
537 	    logWarning("Non-power-of-two textures are not available");
538 	  }
539 	  else
540 	    npotTextures = true;
541 	}
542 
543 	FBO_Allowed = false;
544 	if (!OGL_CheckExtension("GL_EXT_framebuffer_object"))
545 	{
546 		logWarning("Framebuffer Objects not available");
547 		return false;
548 	}
549 	else
550 	{
551 		FBO_Allowed = true;
552 	}
553 
554 	Bloom_sRGB = false;
555 	if (TEST_FLAG(graphics_preferences->OGL_Configure.Flags, OGL_Flag_Blur))
556 	{
557 	  if (!FBO_Allowed)
558 	  {
559 	    SET_FLAG(graphics_preferences->OGL_Configure.Flags, OGL_Flag_Blur, false);
560 	    logWarning("Bloom effects are not available");
561 	  }
562 	  else if(!OGL_CheckExtension("GL_EXT_framebuffer_sRGB") || !OGL_CheckExtension("GL_EXT_texture_sRGB"))
563 	  {
564 	    logWarning("sRGB framebuffer is not available for bloom effects");
565 	  }
566 	  else
567 	    Bloom_sRGB = true;
568 	}
569 
570 	_OGL_IsActive = true;
571 	OGL_StartProgress(count_replacement_collections() + 2);
572 
573 	// Set up some OpenGL stuff: these will be the defaults for this rendering context
574 
575 	// Set up for Z-buffering
576 	glEnable(GL_DEPTH_TEST);
577 	glDepthFunc(GL_LEQUAL);
578 	glDepthRange(0,1);
579 
580 	// Prevent wrong-side polygons from being rendered;
581 	// this works because the engine's visibility routines make all world-geometry
582 	// polygons have the same sidedness when they are viewed from inside.
583 	// [DEFAULT]
584 	glEnable(GL_CULL_FACE);
585 	glCullFace(GL_BACK);
586 	glFrontFace(GL_CW);
587 
588 	// Note: GL_BLEND and GL_ALPHA_TEST do not have defaults; these are to be set
589 	// if some new pixels cannot be assumed to be always 100% opaque.
590 
591 	// [DEFAULT]
592 	// Set standard alpha-test function; cut off at halfway point (for sharp edges)
593 	glAlphaFunc(GL_GREATER,0.5);
594 
595 	// [DEFAULT]
596 	// Set standard crossfade blending function (for smooth transitions)
597 	glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
598 
599 	// Switch on use of vertex and texture-coordinate arrays
600 	glEnableClientState(GL_VERTEX_ARRAY);
601 	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
602 
603 	OGL_Rasterizer_Init();
604 
605 	OGL_ResetForceSpriteDepth();
606 	load_replacement_collections();
607 
608 	// Initialize the texture accounting
609 	OGL_StartTextures();
610 
611 	// Reset the font info for OpenGL rendering
612 	FontSpecifier::OGL_ResetFonts(true);
613 
614 	// Since an OpenGL context has just been created, don't try to clear any OpenGL textures
615 	OGL_ResetModelSkins(false);
616 
617 	// Setup for 3D-model rendering
618 	ModelRenderObject.Clear();
619 	SetupShaders();
620 	OGL_ProgressCallback(1);
621 
622 	// Avoid lazy initial texture loading
623 	PreloadTextures();
624 	OGL_ProgressCallback(1);
625 	OGL_StopProgress();
626 
627 	// Success!
628 	JustInited = true;
629 	return (_OGL_IsActive = true);
630 }
631 
632 // Stop an OpenGL run (destroys a rendering context)
OGL_StopRun()633 bool OGL_StopRun()
634 {
635 	if (!OGL_IsActive() || !_OGL_IsActive) return false;
636 
637 	OGL_StopTextures();
638 	Shader::unloadAll();
639 
640 	Wanting_sRGB = false;
641 
642 	_OGL_IsActive = false;
643 	return true;
644 }
645 
646 
647 // Reads off of the current map;
648 // call it to avoid lazy loading of textures
649 // ZZZ: changes to try to do less redundant work (using a set of pairs etc.)
PreloadTextures()650 void PreloadTextures()
651 {
652 	typedef std::set<TextureWithTransferMode> TextureWithTransferModeSet;
653 
654 	TextureWithTransferModeSet theSetOfTexturesUsed;
655 
656 	// Loop through the map polygons
657 	for (int n=0; n<dynamic_world->polygon_count; n++)
658 	{
659 		polygon_data *polygon = map_polygons + n;
660 
661 		theSetOfTexturesUsed.insert(TextureWithTransferMode(polygon->floor_texture,polygon->floor_transfer_mode));
662 		theSetOfTexturesUsed.insert(TextureWithTransferMode(polygon->ceiling_texture,polygon->ceiling_transfer_mode));
663 
664 		for (int i=0; i<polygon->vertex_count; i++)
665 		{
666 			short side_index= polygon->side_indexes[i];
667 			if (side_index == NONE) continue;
668 			side_data *side= get_side_data(side_index);
669 			switch (side->type)
670 			{
671 			case _full_side:
672 				theSetOfTexturesUsed.insert(TextureWithTransferMode(side->primary_texture.texture,side->primary_transfer_mode));
673 				break;
674 			case _split_side:
675 				theSetOfTexturesUsed.insert(TextureWithTransferMode(side->secondary_texture.texture,side->secondary_transfer_mode));
676 				// Fall through to the high-side case
677 			case _high_side:
678 				theSetOfTexturesUsed.insert(TextureWithTransferMode(side->primary_texture.texture,side->primary_transfer_mode));
679 				break;
680 			case _low_side:
681 				theSetOfTexturesUsed.insert(TextureWithTransferMode(side->primary_texture.texture,side->primary_transfer_mode));
682 				break;
683 			}
684 
685 			theSetOfTexturesUsed.insert(TextureWithTransferMode(side->transparent_texture.texture,side->transparent_transfer_mode));
686 		}
687 	}
688 
689 	// May want to preload the liquid texture also
690 	// Sprites will have the problem of guessing which ones to preload
691 
692 	// ZZZ: now we have a fairly (we hope) minimal set of texture stuffs, let's load them in.
693 	for_each(theSetOfTexturesUsed.begin(), theSetOfTexturesUsed.end(), PreloadWallTexture);
694 }
695 
PreloadWallTexture(const TextureWithTransferMode & inTexture)696 void PreloadWallTexture(const TextureWithTransferMode& inTexture)
697 {
698 	shape_descriptor texture = inTexture.first;
699 	int16 transfer_mode = inTexture.second;
700 
701 	// In case of an empty side
702 	if (texture == UNONE) return;
703 
704 	// Infravision is usually inactive when entering or restoring a level.
705 	bool IsInfravision = false;
706 
707 	TextureManager TMgr;
708 	TMgr.ShapeDesc = AnimTxtr_Translate(texture);
709 	if(TMgr.ShapeDesc == UNONE) return;
710 
711 	get_shape_bitmap_and_shading_table(
712 		TMgr.ShapeDesc,
713 		&TMgr.Texture,
714 		&TMgr.ShadingTables,
715 		IsInfravision ? _shading_infravision : _shading_normal);
716 	if (!TMgr.Texture) return;
717 
718 	TMgr.IsShadeless = IsInfravision;
719 
720 	// Cribbed from instantiate_polygon_transfer_mode() in render.cpp;
721 	// translate the transfer mode
722 	int16 TMgr_TransferMode = _textured_transfer;
723 	switch (transfer_mode)
724 	{
725 	case _xfer_smear:
726 		TMgr_TransferMode = _solid_transfer;
727 		break;
728 
729 	case _xfer_static:
730 		TMgr_TransferMode = _static_transfer;
731 		break;
732 
733 	case _xfer_landscape:
734 		TMgr_TransferMode = _big_landscaped_transfer;
735 		break;
736 	}
737 
738 	TMgr.TransferMode = TMgr_TransferMode;
739 	TMgr.TransferData = 0;
740 
741 	// As in the code below, landscapes get special treatment
742 	bool IsLandscape = TMgr_TransferMode == _big_landscaped_transfer;
743 	TMgr.TextureType = IsLandscape ? OGL_Txtr_Landscape : OGL_Txtr_Wall;
744 
745 	if (IsLandscape)
746 	{
747 		// Get the landscape-texturing options
748 		LandscapeOptions *LandOpts = View_GetLandscapeOptions(TMgr.ShapeDesc);
749 		TMgr.LandscapeVertRepeat = LandOpts->VertRepeat;
750 		TMgr.Landscape_AspRatExp = LandOpts->OGL_AspRatExp;
751 	}
752 
753 	// After all this setting up, now use it!
754 	if (TMgr.Setup()) {
755 		TMgr.RenderNormal();
756 		if (TMgr.IsGlowMapped()) TMgr.RenderGlowing();
757 		if (TEST_FLAG(Get_OGL_ConfigureData().Flags, OGL_Flag_BumpMap))
758 			TMgr.RenderBump();
759 	}
760 }
761 
762 
RectsEqual(Rect & R1,Rect & R2)763 inline bool RectsEqual(Rect &R1, Rect &R2)
764 {
765 	return (R1.top == R2.top) && (R1.left == R2.left) &&
766 		(R1.bottom == R2.bottom) && (R1.right == R2.right);
767 }
768 
DebugRect(Rect & R,char * Label)769 inline void DebugRect(Rect &R, char *Label)
770 {
771 	dprintf("%s (L,R,T,B): %d %d %d %d",Label,R.left,R.right,R.top,R.bottom);
772 }
773 
774 // Set OpenGL rendering-window bounds;
775 // these are calculated using the following boundary Rects:
776 // The screen (gotten from its portRect)
777 // The view (here, the main rendering view)
778 // Whether to allocate a back buffer
OGL_SetWindow(Rect & ScreenBounds,Rect & ViewBounds,bool UseBackBuffer)779 bool OGL_SetWindow(Rect &ScreenBounds, Rect &ViewBounds, bool UseBackBuffer)
780 {
781 	if (!OGL_IsActive()) return false;
782 
783 	// Check whether to do update -- only if the bounds had changed
784 	// or if the view had been inited
785 	bool DoUpdate = false;
786 	if (JustInited) {JustInited = false; DoUpdate = true;}
787 	else if (!RectsEqual(ScreenBounds,SavedScreenBounds)) DoUpdate = true;
788 	else if (!RectsEqual(ViewBounds,SavedViewBounds)) DoUpdate = true;
789 	else DoUpdate = true;
790 
791 	if (!DoUpdate) return true;
792 
793 	SavedScreenBounds = ScreenBounds;
794 	SavedViewBounds = ViewBounds;
795 
796 	// Viewport setup is now done by the caller.
797 	ViewWidth = ViewBounds.right - ViewBounds.left;
798 	ViewHeight = ViewBounds.bottom - ViewBounds.top;
799 
800 	// Create the screen -> clip (fundamental) matrix; this will be needed
801 	// for all the other projections
802 	glMatrixMode(GL_PROJECTION);
803 	glGetDoublev(GL_PROJECTION_MATRIX,Screen_2_Clip);
804 
805 	// Set projection type to initially none (force load of first one)
806 	ProjectionType = Projection_NONE;
807 
808 	return true;
809 }
810 
811 
OGL_StartMain()812 bool OGL_StartMain()
813 {
814 	if (!OGL_IsActive()) return false;
815 
816 	// One-sidedness necessary for correct rendering
817 	glEnable(GL_CULL_FACE);
818 	glCullFace(GL_BACK);
819 	glFrontFace(GL_CW);
820 
821 	// Set the Z-buffering for this go-around
822 	glEnable(GL_DEPTH_TEST);
823 
824 	// Moved this test down here for convenience; the overhead map won't have fog,
825 	// so be sure to turn it on when leaving the overhead map
826 	// Also, added support for changing fog parameters on the fly,
827 	// by moving the setting of initial values to where the context gets created.
828 	int FogType = (local_player->variables.flags&_HEAD_BELOW_MEDIA_BIT) ?
829 		OGL_Fog_BelowLiquid : OGL_Fog_AboveLiquid;
830 	CurrFog = OGL_GetFogData(FogType);
831 	if (FogActive())
832 	{
833 		glEnable(GL_FOG);
834 		Using_sRGB = Wanting_sRGB;
835 		CurrFogColor[0] = sRGB_frob(CurrFog->Color.red/65535.0F);
836 		CurrFogColor[1] = sRGB_frob(CurrFog->Color.green/65535.0F);
837 		CurrFogColor[2] = sRGB_frob(CurrFog->Color.blue/65535.0F);
838 		CurrFogColor[3] = 0;
839 		Using_sRGB = false;
840 		if (IsInfravisionActive())
841 		{
842 			if (LandscapesLoaded)
843 				FindInfravisionVersionRGBA(_collection_landscape1+static_world->song_index,CurrFogColor);
844 			else
845 				FindInfravisionVersionRGBA(LoadedWallTexture,CurrFogColor);
846 		}
847 		glFogfv(GL_FOG_COLOR,CurrFogColor);
848 		glFogf(GL_FOG_DENSITY,1.0F/MAX(1,WORLD_ONE*CurrFog->Depth));
849 	}
850 	else
851 	{
852 		glFogf(GL_FOG_DENSITY,0.0F);
853 		glDisable(GL_FOG);
854 	}
855 
856 	// Set the color of the void
857 	OGL_ConfigureData& ConfigureData = Get_OGL_ConfigureData();
858 	if (TEST_FLAG(ConfigureData.Flags,OGL_Flag_VoidColor))
859 	{
860 		RGBColor& VoidColor = ConfigureData.VoidColor;
861 		GLfloat Red = VoidColor.red/65535.0F;
862 		GLfloat Green = VoidColor.green/65535.0F;
863 		GLfloat Blue = VoidColor.blue/65535.0F;
864 
865 		// The color of the void will be the color of fog
866 		if (FogActive())
867 		{
868 			Red = CurrFogColor[0];
869 			Green = CurrFogColor[1];
870 			Blue = CurrFogColor[2];
871 		}
872 
873 		glClearColor(Red,Green,Blue,0);
874 		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
875 	}
876 	// Have to clear the Z-buffer before rendering, no matter what
877 	else glClear(GL_DEPTH_BUFFER_BIT);
878 
879 	// Static patterns; randomize all digits; the various offsets
880 	// are to ensure that all the bits overlap.
881 	// Also do flat static if requested;
882 	// done once per frame to avoid visual inconsistencies
883 	UseFlatStatic = TEST_FLAG(ConfigureData.Flags,OGL_Flag_FlatStatic);
884 
885 	if (Wanting_sRGB)
886 	{
887 		glEnable(GL_FRAMEBUFFER_SRGB_EXT);
888 		Using_sRGB = true;
889 	}
890 
891 	return true;
892 }
893 
894 
OGL_EndMain()895 bool OGL_EndMain()
896 {
897 	if (!OGL_IsActive()) return false;
898 
899 	if (Wanting_sRGB)
900 	{
901 		glDisable(GL_FRAMEBUFFER_SRGB_EXT);
902 		Using_sRGB = false;
903 	}
904 
905 	// Proper projection
906 	SetProjectionType(Projection_Screen);
907 
908 	// Reset modelview matrix
909 	glMatrixMode(GL_MODELVIEW);
910 	glLoadIdentity();
911 
912 	// No texture mapping now
913 	glDisable(GL_TEXTURE_2D);
914 
915 	// And no Z buffer
916 	glDisable(GL_DEPTH_TEST);
917 
918 	// Render OpenGL faders, if in use
919 	OGL_DoFades(0,0,ViewWidth,ViewHeight);
920 
921 	return true;
922 }
923 
924 // Swap buffers (reveal rendered image)
OGL_SwapBuffers()925 bool OGL_SwapBuffers()
926 {
927 	if (!OGL_IsActive()) return false;
928 	MainScreenSwap();
929 	return true;
930 }
931 
932 
933 // Find complement vectors; return whether the two input vectors were noncollinear
FindComplements()934 bool SurfaceCoords::FindComplements()
935 {
936 	// Compose the complements of the two texture vectors;
937 	// this code is designed to be general, and is probably overkill for the Marathon engine,
938 	// where the texture vectors are always orthogonal.
939 	GLdouble P_U2 = ScalarProd(U_Vec,U_Vec);
940 	GLdouble P_UV = ScalarProd(U_Vec,V_Vec);
941 	GLdouble P_V2 = ScalarProd(V_Vec,V_Vec);
942 	GLdouble P_Den = P_U2*P_V2 - P_UV*P_UV;
943 
944 	// Will return here if the vectors are collinear
945 	if (P_Den == 0) return false;
946 
947 	GLdouble Norm = 1/P_Den;
948 	GLdouble C_UU = Norm*P_V2;
949 	GLdouble C_UV = - Norm*P_UV;
950 	GLdouble C_VV = Norm*P_U2;
951 
952 	GLdouble TempU[3], TempV[3];
953 
954 	VecScalarMult(U_Vec,C_UU,TempU);
955 	VecScalarMult(V_Vec,C_UV,TempV);
956 	VecAdd(TempU,TempV,U_CmplVec);
957 
958 	VecScalarMult(U_Vec,C_UV,TempU);
959 	VecScalarMult(V_Vec,C_VV,TempV);
960 	VecAdd(TempU,TempV,V_CmplVec);
961 
962 	// Compose the third complement; don't bother to normalize it
963 	VectorProd(U_Vec,V_Vec,W_CmplVec);
964 
965 	// Success!
966 	return true;
967 }
968 
969 
970 // Multiply a vector by an OpenGL matrix
GL_MatrixTimesVector(const GLdouble * Matrix,const GLdouble * Vector,GLdouble * ResVec)971 inline void GL_MatrixTimesVector(const GLdouble *Matrix, const GLdouble *Vector, GLdouble *ResVec)
972 {
973 	for (int k = 0; k < 4; k++)
974 		ResVec[k] =
975 			Matrix[k]*Vector[0] +
976 			Matrix[4+k]*Vector[1] +
977 			Matrix[4*2+k]*Vector[2] +
978 			Matrix[4*3+k]*Vector[3];
979 }
980 
981 
982 // Set view parameters; this is for proper perspective rendering
OGL_SetView(view_data & View)983 bool OGL_SetView(view_data &View)
984 {
985 	if (!OGL_IsActive()) return false;
986 
987 	// Use the modelview matrix as storage; set the matrix back when done
988 	glMatrixMode(GL_MODELVIEW);
989 
990 	// World coordinates to Marathon eye coordinates
991 	glLoadIdentity();
992 	glGetDoublev(GL_MODELVIEW_MATRIX,CenteredWorld_2_MaraEye);
993 
994 	// Do rotation first:
995 	const double TrigMagReciprocal = 1/double(TRIG_MAGNITUDE);
996 	double Cosine = TrigMagReciprocal*double(cosine_table[View.yaw]);
997 	double Sine = TrigMagReciprocal*double(sine_table[View.yaw]);
998 	CenteredWorld_2_MaraEye[0] = Cosine;
999 	CenteredWorld_2_MaraEye[1] = - Sine;
1000 	CenteredWorld_2_MaraEye[4] = Sine;
1001 	CenteredWorld_2_MaraEye[4+1] = Cosine;
1002 	glLoadMatrixd(CenteredWorld_2_MaraEye);
1003 
1004 	// Set the view direction
1005 	ViewDir[0] = (float)Cosine;
1006 	ViewDir[1] = (float)Sine;
1007 	ModelRenderObject.ViewDirection[2] = 0;	// Always stays the same
1008 
1009 	// Do a translation and then save;
1010 	glTranslated(-View.origin.x,-View.origin.y,-View.origin.z);
1011 	glGetDoublev(GL_MODELVIEW_MATRIX,World_2_MaraEye);
1012 
1013 	// Find the appropriate modelview matrix for 3D-model inhabitant rendering
1014 	glLoadMatrixd(MaraEye_2_OGLEye);
1015 	glMultMatrixd(World_2_MaraEye);
1016 	glGetDoublev(GL_MODELVIEW_MATRIX,World_2_OGLEye);
1017 
1018 	// Find the appropriate modelview matrix for 3D-model skybox rendering
1019 	glLoadMatrixd(MaraEye_2_OGLEye);
1020 	glMultMatrixd(CenteredWorld_2_MaraEye);
1021 	glGetDoublev(GL_MODELVIEW_MATRIX,CenteredWorld_2_OGLEye);
1022 
1023 	// Find world-to-screen and screen-to-world conversion factors;
1024 	// be sure to have some fallbacks in case of zero
1025 	XScale = View.world_to_screen_x;
1026 	if (XScale == 0) XScale = 1;
1027 	XScaleRecip = 1/XScale;
1028 	YScale = - View.world_to_screen_y;
1029 	if (YScale == 0) YScale = -1;
1030 	YScaleRecip = 1/YScale;
1031 	XOffset = View.half_screen_width;
1032 	YOffset = View.half_screen_height + View.dtanpitch;
1033 
1034 	// Find the OGL-eye-to-screen matrix
1035 	// Remember that z is small negative to large negative (OpenGL style)
1036 	glLoadIdentity();
1037 	glGetDoublev(GL_MODELVIEW_MATRIX,OGLEye_2_Screen);
1038 	OGLEye_2_Screen[0] = XScale;
1039 	OGLEye_2_Screen[4+1] = YScale;
1040 	OGLEye_2_Screen[4*2] = - XOffset;
1041 	OGLEye_2_Screen[4*2+1] = - YOffset;
1042 	OGLEye_2_Screen[4*2+2] = Z_Proj0;
1043 	OGLEye_2_Screen[4*2+3] = -1;
1044 	OGLEye_2_Screen[4*3+2] = Z_Proj1;
1045 	OGLEye_2_Screen[4*3+3] = 0;
1046 
1047 	// Find the OGL-eye-to-clip matrix:
1048 	glLoadMatrixd(Screen_2_Clip);
1049 	glMultMatrixd(OGLEye_2_Screen);
1050 	glGetDoublev(GL_MODELVIEW_MATRIX,OGLEye_2_Clip);
1051 
1052 	// Restore the default modelview matrix
1053 	glLoadIdentity();
1054 
1055 	// Calculate the horizontal-surface projected-texture vectors
1056 	GLdouble OrigVec[4];
1057 
1058 	// Horizontal U
1059 	OrigVec[0] = WORLD_ONE;
1060 	OrigVec[1] = 0;
1061 	OrigVec[2] = 0;
1062 	OrigVec[3] = 0;
1063 	GL_MatrixTimesVector(CenteredWorld_2_OGLEye,OrigVec,HorizCoords.U_Vec);
1064 
1065 	// Horizontal V
1066 	OrigVec[0] = 0;
1067 	OrigVec[1] = WORLD_ONE;
1068 	OrigVec[2] = 0;
1069 	OrigVec[3] = 0;
1070 	GL_MatrixTimesVector(CenteredWorld_2_OGLEye,OrigVec,HorizCoords.V_Vec);
1071 
1072 	bool found_complements= HorizCoords.FindComplements();
1073 	if(!found_complements) assert(found_complements);
1074 
1075 	// Get the yaw angle as a value from 0 to 1
1076 	Yaw = FullCircleReciprocal*View.yaw;
1077 
1078 	// Set up landscape rescaling:
1079 	// Find view angle in radians, then find the rescaling
1080 	double ViewAngle = (TWO_PI*FullCircleReciprocal)*View.half_cone;
1081 	LandscapeRescale = ViewAngle/tan(ViewAngle);
1082 
1083 	// Is infravision active?
1084 	IsInfravisionActive() = (View.shading_mode == _shading_infravision);
1085 
1086 	// Finally...
1087 	SelfLuminosity = View.maximum_depth_intensity;
1088 
1089 	return true;
1090 }
1091 
1092 
1093 // Sets the view to what's suitable for rendering foreground objects
1094 // like weapons in hand
OGL_SetForeground()1095 bool OGL_SetForeground()
1096 {
1097 	// Foreground objects are to be in front of all the other ones
1098 	glClear(GL_DEPTH_BUFFER_BIT);
1099 
1100 	// New renderer needs modelview reset
1101 	glMatrixMode(GL_MODELVIEW);
1102 	glLoadIdentity();
1103 
1104 	// Disable sRGB mode
1105 	if (Wanting_sRGB)
1106 	{
1107 		glDisable(GL_FRAMEBUFFER_SRGB_EXT);
1108 		Using_sRGB = false;
1109 	}
1110 
1111 	return true;
1112 }
1113 
1114 
1115 // Sets whether a foreground object is horizontally reflected
OGL_SetForegroundView(bool HorizReflect)1116 bool OGL_SetForegroundView(bool HorizReflect)
1117 {
1118 	// x is rightward (OpenGL: x is rightward)
1119 	// y is forward (OpenGL: y is upward)
1120 	// z is upward (OpenGL: z is backward)
1121 	const GLdouble Foreground_2_OGLEye[16] =
1122 	{	// Correct OpenGL arrangement: transpose to get usual arrangement
1123 		1,	0,	0,	0,
1124 		0,	0,	1,	0,
1125 		0,	-1,	0,	0,
1126 		0,	0,	0,	1
1127 	};
1128 
1129 	// Find the appropriate modelview matrix for 3D-model inhabitant rendering
1130 	glLoadMatrixd(Foreground_2_OGLEye);
1131 	glGetDoublev(GL_MODELVIEW_MATRIX,World_2_OGLEye);
1132 
1133 	// Perform the reflection if desired; refer to above definition of Foreground_2_OGLEye
1134 	if (HorizReflect) World_2_OGLEye[0] = -1;
1135 
1136 	// Restore the default modelview matrix
1137 	glLoadIdentity();
1138 
1139 	return true;
1140 }
1141 
1142 
1143 // Self-luminosity calculations;
1144 // cribbed from scottish_textures.c (CALCULATE_SHADING_TABLE):
1145 
1146 // This finds the intensity-slope crossover depth for splitting polygon lines;
1147 // it takes the shading value from the render object
FindCrossoverDepth(_fixed Shading)1148 inline GLdouble FindCrossoverDepth(_fixed Shading)
1149 {
1150 	return ((8*GLdouble(WORLD_ONE))/GLdouble(FIXED_ONE))*(SelfLuminosity - Shading);
1151 }
1152 
1153 
1154 // This finds the color value for lighting from the render object's shading value
FindShadingColor(GLdouble Depth,_fixed Shading,GLfloat * Color)1155 void FindShadingColor(GLdouble Depth, _fixed Shading, GLfloat *Color)
1156 {
1157 	GLdouble SelfIllumShading =
1158 		PIN(SelfLuminosity - (GLdouble(FIXED_ONE)/(8*GLdouble(WORLD_ONE)))*Depth,0,FIXED_ONE);
1159 
1160 	GLdouble CombinedShading = (Shading>SelfIllumShading) ? (Shading + 0.5*SelfIllumShading) : (SelfIllumShading + 0.5*Shading);
1161 
1162 	Color[0] = Color[1] = Color[2] = sRGB_frob(PIN(static_cast<GLfloat>(CombinedShading/FIXED_ONE),0,1));
1163 }
1164 
1165 
1166 // Stuff for doing OpenGL rendering of various objects
1167 
1168 
1169 // Storage of intermediate results for mass render with glDrawArrays
1170 struct ExtendedVertexData
1171 {
1172 	GLdouble Vertex[4];
1173 	GLdouble TexCoord[2];
1174 	GLfloat Color[3];
1175 	GLfloat GlowColor[3];
1176 };
1177 
1178 
1179 // Wraparound increment and decrementfunctions
IncrementAndWrap(int n,int Limit)1180 inline int IncrementAndWrap(int n, int Limit)
1181 	{int m = n + 1; if (m >= Limit) m -= Limit; return m;}
DecrementAndWrap(int n,int Limit)1182 inline int DecrementAndWrap(int n, int Limit)
1183 	{int m = n - 1; if (m < 0) m += Limit; return m;}
1184 
1185 
1186 // The depth must be in OpenGL form (increasing inward);
1187 // the other arguments are: the two source and one destination extended vertex
InterpolateByDepth(GLdouble Depth,ExtendedVertexData & EV0,ExtendedVertexData & EV1,ExtendedVertexData & EVRes)1188 static void InterpolateByDepth(GLdouble Depth,
1189 	ExtendedVertexData& EV0,
1190 	ExtendedVertexData& EV1,
1191 	ExtendedVertexData& EVRes)
1192 {
1193 	GLdouble Denom = EV1.Vertex[2] - EV0.Vertex[2];
1194 	assert(Denom != 0);
1195 
1196 	GLdouble IntFac = (Depth - EV0.Vertex[2])/Denom;
1197 
1198 	for (int k=0; k<4; k++)
1199 		EVRes.Vertex[k] = EV0.Vertex[k] + IntFac*(EV1.Vertex[k] - EV0.Vertex[k]);
1200 
1201 	for (int k=0; k<2; k++)
1202 		EVRes.TexCoord[k] = EV0.TexCoord[k] + IntFac*(EV1.TexCoord[k] - EV0.TexCoord[k]);
1203 }
1204 
1205 
1206 // Render the wall texture as a "real" wall;
1207 // it returns whether or not the texture is a legitimate wall texture
RenderAsRealWall(polygon_definition & RenderPolygon,bool IsVertical)1208 static bool RenderAsRealWall(polygon_definition& RenderPolygon, bool IsVertical)
1209 {
1210 
1211 	// Set up the texture manager with the input manager
1212 	TextureManager TMgr;
1213 	TMgr.ShapeDesc = RenderPolygon.ShapeDesc;
1214 	TMgr.ShadingTables = RenderPolygon.shading_tables;
1215 	TMgr.Texture = RenderPolygon.texture;
1216 	TMgr.TransferMode = RenderPolygon.transfer_mode;
1217 	TMgr.TransferData = RenderPolygon.transfer_data;
1218 	TMgr.IsShadeless = (RenderPolygon.flags&_SHADELESS_BIT) != 0;
1219 	if (RenderPolygon.transfer_mode == _static_transfer)
1220 		TMgr.IsShadeless = 1;
1221 	TMgr.TextureType = OGL_Txtr_Wall;
1222 
1223 	// Use that texture
1224 	if (!TMgr.Setup()) return false;
1225 
1226 	// The currently-used surface-coordinate object
1227 	SurfaceCoords* SCPtr;
1228 
1229 	// A workspace vector
1230 	GLdouble OrigVec[4];
1231 
1232 	// Calculate the projected origin and texture coordinates
1233 	if (IsVertical)
1234 	{
1235 		// Set to vertical ones
1236 		SCPtr = &VertCoords;
1237 
1238 		// Calculate its texture vectors
1239 		world_vector3d& Vec = RenderPolygon.vector;
1240 
1241 		// Idiot-proofing; don't render a polygon its vertical texture vector
1242 		// has either horizontal or vertical parts being zero.
1243 
1244 		// Vertical U
1245 		if (Vec.k == 0) return false;
1246 		OrigVec[0] = 0;
1247 		OrigVec[1] = 0;
1248 		OrigVec[2] = Vec.k;
1249 		OrigVec[3] = 0;
1250 		GL_MatrixTimesVector(MaraEye_2_OGLEye,OrigVec,VertCoords.U_Vec);
1251 
1252 		// Vertical V
1253 		if (Vec.i == 0 && Vec.j == 0) return false;
1254 		OrigVec[0] = Vec.i;
1255 		OrigVec[1] = Vec.j;
1256 		OrigVec[2] = 0;
1257 		OrigVec[3] = 0;
1258 		GL_MatrixTimesVector(MaraEye_2_OGLEye,OrigVec,VertCoords.V_Vec);
1259 
1260 		bool found_complements= VertCoords.FindComplements();
1261 		if(!found_complements) assert(found_complements);
1262 
1263 	} else {
1264 		// Set to horizontal ones
1265 		SCPtr = &HorizCoords;
1266 	}
1267 
1268 	// Find the texture origin in OpenGL eye coordinates
1269 	long_point3d& Origin = RenderPolygon.origin;
1270 	OrigVec[0] = Origin.x;
1271 	OrigVec[1] = Origin.y;
1272 	OrigVec[2] = Origin.z;
1273 	OrigVec[3] = 1;
1274 	GLdouble TexOrigin[4];
1275 	if (IsVertical)
1276 		GL_MatrixTimesVector(MaraEye_2_OGLEye,OrigVec,TexOrigin);
1277 	else
1278 	{
1279 		// The inversion is a kludge for getting around an engine bug
1280 		OrigVec[0] *= -1;
1281 		OrigVec[1] *= -1;
1282 		GL_MatrixTimesVector(CenteredWorld_2_OGLEye,OrigVec,TexOrigin);
1283 	}
1284 
1285 	// Project it onto the coordinate vectors
1286 	GLdouble TexOrigin_U = ScalarProd(SCPtr->U_CmplVec,TexOrigin);
1287 	GLdouble TexOrigin_V = ScalarProd(SCPtr->V_CmplVec,TexOrigin);
1288 	GLdouble TexOrigin_W = ScalarProd(SCPtr->W_CmplVec,TexOrigin);
1289 
1290 	// Storage of intermediate results for mass render;
1291 	// take into account the fact that the polygon might get split in both ascending
1292 	// and descending directions along three lines
1293 	const int MAXIMUM_VERTICES_OF_SPLIT_POLYGON = MAXIMUM_VERTICES_PER_SCREEN_POLYGON + 6;
1294 	ExtendedVertexData ExtendedVertexList[MAXIMUM_VERTICES_OF_SPLIT_POLYGON];
1295 
1296 	short NumVertices = RenderPolygon.vertex_count;
1297 	for (int k=0; k<NumVertices; k++)
1298 	{
1299 		// Create some convenient references
1300 		point2d& Vertex = RenderPolygon.vertices[k];
1301 		ExtendedVertexData& EVData = ExtendedVertexList[k];
1302 
1303 		// Emit a ray from the vertex in OpenGL eye coords;
1304 		// it had been specified in screen coordinates
1305 		GLdouble VertexRay[3];
1306 		Screen2Ray(Vertex,VertexRay);
1307 
1308 		// Project it:
1309 		GLdouble VertexRay_U = ScalarProd(SCPtr->U_CmplVec,VertexRay);
1310 		GLdouble VertexRay_V = ScalarProd(SCPtr->V_CmplVec,VertexRay);
1311 		GLdouble VertexRay_W = ScalarProd(SCPtr->W_CmplVec,VertexRay);
1312 
1313 		// Find the distance along the ray;
1314 		// watch out for excessively long or negative distances;
1315 		// force them to the maximum Z allowed.
1316 		// This is done because the screen coordinates of the area to be rendered
1317 		// has been rounded off to integers, which may cause off-the-edge errors.
1318 		GLdouble RayDistance = 0;
1319 		bool RayDistanceWasModified = false;
1320 		if (VertexRay_W == 0)
1321 		{
1322 			RayDistanceWasModified = true;
1323 			RayDistance = Z_Far;
1324 		}
1325 		else
1326 		{
1327 			RayDistance = TexOrigin_W/VertexRay_W;
1328 			// Test for possible wraparound
1329 			if (RayDistance < - 16*WORLD_ONE)
1330 			{
1331 				RayDistanceWasModified = true;
1332 				RayDistance = Z_Far;
1333 			}
1334 			// Test for too far
1335 			else if (RayDistance > Z_Far)
1336 			{
1337 				RayDistanceWasModified = true;
1338 				RayDistance = Z_Far;
1339 			}
1340 			// Test for too close
1341 			else if (RayDistance < Z_Near)
1342 			{
1343 				RayDistance = Z_Near;
1344 				RayDistanceWasModified = true;
1345 			}
1346 		}
1347 
1348 		// Find the texture coordinates
1349 		GLdouble U = VertexRay_U*RayDistance - TexOrigin_U;
1350 		GLdouble V = VertexRay_V*RayDistance - TexOrigin_V;
1351 
1352 		if (RayDistanceWasModified)
1353 		{
1354 			// Rebuild the vertex here.
1355 			// This is necessary here, since if the ray distance was modified,
1356 			// the vertex will be forced to move on the screen.
1357 			GLdouble TempU[3], TempV[3], TempUV[3];
1358 			VecScalarMult(SCPtr->U_Vec,U,TempU);
1359 			VecScalarMult(SCPtr->V_Vec,V,TempV);
1360 			VecAdd(TempU,TempV,TempUV);
1361 			VecAdd(TexOrigin,TempUV,EVData.Vertex);
1362 			EVData.Vertex[3] = TexOrigin[3];
1363 
1364 		} else {
1365 			// Project along the ray
1366 			VecScalarMult(VertexRay,RayDistance,EVData.Vertex);
1367 			EVData.Vertex[3] = TexOrigin[3];
1368 		}
1369 
1370 		// Store the texture coordinates
1371 		EVData.TexCoord[0] = U;
1372 		EVData.TexCoord[1] = V;
1373 	}
1374 
1375 	// Does the polygon have a variable shading over its extent?
1376 	bool PolygonVariableShade = false;
1377 	GLfloat GlowColor = TMgr.MinGlowIntensity();
1378 
1379 	if (TMgr.IsShadeless)
1380 	{
1381 		// The shadeless color is E-Z
1382 		glColor3f(1,1,1);
1383 		GlowColor = 1;
1384 	}
1385 	else if (RenderPolygon.ambient_shade < 0)
1386 	{
1387 		GLfloat Light = (- RenderPolygon.ambient_shade)/GLfloat(FIXED_ONE);
1388 		SglColor3f(Light,Light,Light);
1389 		GlowColor = std::max(GlowColor, Light);
1390 	}
1391 	else
1392 	{
1393 		// All this stuff is to do the self-luminosity properly,
1394 		// like how software rendering does it.
1395 
1396 		// Divide the polygon along these lines;
1397 		// these mark out the self-luminosity boundaries.
1398 		// Be sure to use OpenGL depth conventions
1399 		GLdouble SplitDepths[3];
1400 		// This is where the lighting reaches ambient
1401 		SplitDepths[0] = - FindCrossoverDepth(0);
1402 		// This is where the decline slope changes
1403 		SplitDepths[1] = - FindCrossoverDepth(RenderPolygon.ambient_shade);
1404 		// This is where the lighting gets saturated
1405 		SplitDepths[2] = - FindCrossoverDepth(FIXED_ONE - (RenderPolygon.ambient_shade>>1));
1406 
1407 		// Check to see if all of the polygon is outside the variable-lighting domain;
1408 		// set the lighting value appropriately if that is the case
1409 
1410 		// Is the polygon all in the ambient domain?
1411 		GLfloat Light = RenderPolygon.ambient_shade/GLfloat(FIXED_ONE);
1412 		for (int k=0; k<NumVertices; k++)
1413 		{
1414 			ExtendedVertexData& EV = ExtendedVertexList[k];
1415 			if (EV.Vertex[2] > SplitDepths[0])
1416 			{
1417 				PolygonVariableShade = true;
1418 				break;
1419 			}
1420 		}
1421 
1422 		if (!PolygonVariableShade)
1423 		{
1424 			// Is the polygon all in the saturated domain?
1425 			Light = 1;
1426 			for (int k=0; k<NumVertices; k++)
1427 			{
1428 				ExtendedVertexData& EV = ExtendedVertexList[k];
1429 				if (EV.Vertex[2] < SplitDepths[2])
1430 				{
1431 					PolygonVariableShade = true;
1432 					break;
1433 				}
1434 			}
1435 		}
1436 
1437 		if (PolygonVariableShade)
1438 		{
1439 			// If the saturation point is outward from the crossover point,
1440 			// make the crossover point the saturation point and the
1441 			// original saturation point out-of-bounds
1442 			if (SplitDepths[2] < SplitDepths[1])
1443 			{
1444 				SplitDepths[1] = SplitDepths[2];
1445 				SplitDepths[2] = 0;
1446 			}
1447 
1448 			// Find split points:
1449 			// These go in order: ascending split depths (0, 1, 2),
1450 			// then descending split depths (2, 1, 0)
1451 			// Locations of the splits
1452 			int Splits[6];
1453 			for (int m=0; m<6; m++)
1454 				Splits[m] = NONE;
1455 			// Interpolated values
1456 			ExtendedVertexData SplitVertices[6];
1457 
1458 			// Find ascending splits
1459 			for (int m=0; m<3; m++)
1460 			{
1461 				GLdouble SplitDepth = SplitDepths[m];
1462 				for (int k=0; k<NumVertices; k++)
1463 				{
1464 					ExtendedVertexData& EV0 = ExtendedVertexList[k];
1465 					ExtendedVertexData& EV1 = ExtendedVertexList[IncrementAndWrap(k,NumVertices)];
1466 					if (EV0.Vertex[2] < SplitDepth && EV1.Vertex[2] > SplitDepth)
1467 					{
1468 						Splits[m] = k;
1469 						InterpolateByDepth(SplitDepth,EV0,EV1,SplitVertices[m]);
1470 						break;
1471 					}
1472 				}
1473 			}
1474 
1475 			// Find descending splits
1476 			for (int m=0; m<3; m++)
1477 			{
1478 				GLdouble SplitDepth = SplitDepths[m];
1479 				for (int k=0; k<NumVertices; k++)
1480 				{
1481 					ExtendedVertexData& EV0 = ExtendedVertexList[k];
1482 					ExtendedVertexData& EV1 = ExtendedVertexList[IncrementAndWrap(k,NumVertices)];
1483 					if (EV0.Vertex[2] > SplitDepth && EV1.Vertex[2] < SplitDepth)
1484 					{
1485 						Splits[5-m] = k;
1486 						InterpolateByDepth(SplitDepth,EV0,EV1,SplitVertices[5-m]);
1487 						break;
1488 					}
1489 				}
1490 			}
1491 
1492 			// Insert the new vertices into place; first, create a vertex-source list
1493 			int VertexSource[MAXIMUM_VERTICES_OF_SPLIT_POLYGON];
1494 			for (int k=0; k<NumVertices; k++)
1495 				VertexSource[k] = k;
1496 
1497 			// Now work backward, so that the inserted vertices will be in the proper order;
1498 			// the inserted ones are mapped 0..5 to -1..-6
1499 			bool PolygonSplit = false;
1500 			for (int m=5; m>=0; m--)
1501 			{
1502 				int Split = Splits[m];
1503 				// If no split, then...
1504 				if (Split == NONE) continue;
1505 				// A split occurred; notify the rest of the function
1506 				PolygonSplit = true;
1507 				// Find the split location
1508 				int SplitLoc = NONE;
1509 				for (int k=0; k<NumVertices; k++)
1510 				{
1511 					if (VertexSource[k] == Split)
1512 					{
1513 						SplitLoc = k;
1514 						break;
1515 					}
1516 				}
1517 				assert(SplitLoc != NONE);
1518 
1519 				// Move up all those past the split;
1520 				// be sure to go backwards so as to move them correctly.
1521 				for (int k=NumVertices-1; k>SplitLoc; k--)
1522 					VertexSource[k+1] = VertexSource[k];
1523 
1524 				// Put the split one into place
1525 				VertexSource[SplitLoc+1] = -m-1;
1526 
1527 				// Bump up the number of vertices, since one has been added
1528 				NumVertices++;
1529 			}
1530 
1531 			// Now, remap
1532 			if (PolygonSplit)
1533 			{
1534 				// Go backwards, since the vertices have been pushed upwards
1535 				for (int k=NumVertices-1; k>=0; k--)
1536 				{
1537 					int Src = VertexSource[k];
1538 					if (Src >= 0)
1539 						ExtendedVertexList[k] = ExtendedVertexList[Src];
1540 					else
1541 						ExtendedVertexList[k] = SplitVertices[-Src-1];
1542 				}
1543 			}
1544 
1545 			// Set up for vertex lighting
1546 			glEnableClientState(GL_COLOR_ARRAY);
1547 			glColorPointer(3,GL_FLOAT,sizeof(ExtendedVertexData),ExtendedVertexList[0].Color);
1548 
1549 			// Calculate the lighting
1550 			for (int k=0; k<NumVertices; k++)
1551 			{
1552 				FindShadingColor(-ExtendedVertexList[k].Vertex[2],RenderPolygon.ambient_shade,ExtendedVertexList[k].Color);
1553 				ExtendedVertexList[k].GlowColor[0] = ExtendedVertexList[k].GlowColor[1] = ExtendedVertexList[k].GlowColor[2] = std::max(GlowColor, ExtendedVertexList[k].Color[0]);
1554 			}
1555 		}
1556 		else
1557 		{
1558 			SglColor3f(Light,Light,Light);
1559 			GlowColor = std::max(GlowColor, Light);
1560 		}
1561 	}
1562 
1563 	// Set up blending mode: either sharp edges or opaque
1564 	// Added support for partial-opacity blending of wall textures
1565 	// Added support for suppressing semitransparency when the void is on one side;
1566 	// this suppression is optional for those who like weird smearing effects
1567 	bool IsBlended = TMgr.IsBlended();
1568 	if (!RenderPolygon.VoidPresent || TMgr.VoidVisible())
1569 	{
1570 		if (IsBlended)
1571 		{
1572 			glEnable(GL_BLEND);
1573 			glDisable(GL_ALPHA_TEST);
1574 			glDisable(GL_DEPTH_TEST);
1575 		} else {
1576 			glDisable(GL_BLEND);
1577 			glEnable(GL_ALPHA_TEST);
1578 		}
1579 	} else {
1580 		// Completely opaque if can't see through void
1581 		IsBlended = false;
1582 		glDisable(GL_BLEND);
1583 		glDisable(GL_ALPHA_TEST);
1584 	}
1585 
1586 	// Proper projection
1587 	SetProjectionType(Projection_OpenGL_Eye);
1588 
1589 	// Location of data:
1590 	glVertexPointer(4,GL_DOUBLE,sizeof(ExtendedVertexData),ExtendedVertexList[0].Vertex);
1591 	glTexCoordPointer(2,GL_DOUBLE,sizeof(ExtendedVertexData),ExtendedVertexList[0].TexCoord);
1592 
1593 	// Painting a texture...
1594 	glEnable(GL_TEXTURE_2D);
1595 
1596 	TMgr.SetupTextureMatrix();
1597 	TMgr.RenderNormal();
1598 	if (TMgr.TransferMode == _static_transfer)
1599 	{
1600 		SetupStaticMode(IsBlended ? TMgr.TransferData : 0);
1601 		if (!IsBlended)
1602 			glDisable(GL_BLEND);
1603 
1604 		if (UseFlatStatic)
1605 		{
1606 			glDrawArrays(GL_POLYGON,0,NumVertices);
1607 		} else {
1608 			// Do multitextured stippling to create the static effect
1609 			for (int k=0; k<StaticEffectPasses; k++)
1610 			{
1611 				StaticModeIndivSetup(k);
1612 				glDrawArrays(GL_POLYGON,0,NumVertices);
1613 			}
1614 		}
1615 		TeardownStaticMode();
1616 	}
1617 	else
1618 	{
1619 	SetBlend(TMgr.NormalBlend());
1620 
1621 	GLint VertIndices[3*(MAXIMUM_VERTICES_OF_SPLIT_POLYGON - 2)];
1622 	if (PolygonVariableShade)
1623 	{
1624 		// Do triangulation by hand, creating a sort of ladder;
1625 		// this is to avoid creating a triangle fan
1626 
1627 		// Find minimum and maximum depths:
1628 		GLint MinVertex = 0;
1629 		GLint MaxVertex = 0;
1630 		GLdouble MinDepth = ExtendedVertexList[MinVertex].Vertex[2];
1631 		GLdouble MaxDepth = ExtendedVertexList[MaxVertex].Vertex[2];
1632 
1633 		for (int k=0; k<NumVertices; k++)
1634 		{
1635 			// Create some convenient references
1636 			ExtendedVertexData& EVData = ExtendedVertexList[k];
1637 
1638 			GLdouble Depth = EVData.Vertex[2];
1639 			if (Depth < MinDepth)
1640 			{
1641 				MinDepth = Depth;
1642 				MinVertex = k;
1643 			}
1644 			if (Depth > MaxDepth)
1645 			{
1646 				MaxDepth = Depth;
1647 				MaxVertex = k;
1648 			}
1649 		}
1650 
1651 		// Now create the ladder
1652 		GLint *VIPtr = VertIndices;
1653 
1654 		// FInd the two neighboring vertices
1655 		GLint LeftVertex = DecrementAndWrap(MinVertex,NumVertices);
1656 		GLint RightVertex = IncrementAndWrap(MinVertex,NumVertices);
1657 
1658 		// Place in the triangle; be sure to keep the proper order
1659 		*(VIPtr++) = LeftVertex;
1660 		*(VIPtr++) = MinVertex;
1661 		*(VIPtr++) = RightVertex;
1662 
1663 		// We know how many triangles there will be: NumVertices-2,
1664 		// and we have already found one of them.
1665 		for (int k=0; k<(NumVertices-3); k++)
1666 		{
1667 			if (LeftVertex == MaxVertex)
1668 			{
1669 				// Idiot-proofing; if the left vertex had reached the maximum,
1670 				// the right vertex ought not to be there
1671 				assert(RightVertex != MaxVertex);
1672 
1673 				// Advance the right vertex
1674 				GLint NewRightVertex = IncrementAndWrap(RightVertex,NumVertices);
1675 				*(VIPtr++) = LeftVertex;
1676 				*(VIPtr++) = RightVertex;
1677 				*(VIPtr++) = NewRightVertex;
1678 				RightVertex = NewRightVertex;
1679 			}
1680 			else if (RightVertex == MaxVertex)
1681 			{
1682 				// Advance the left vertex
1683 				GLint NewLeftVertex = DecrementAndWrap(LeftVertex,NumVertices);
1684 				*(VIPtr++) = NewLeftVertex;
1685 				*(VIPtr++) = LeftVertex;
1686 				*(VIPtr++) = RightVertex;
1687 				LeftVertex = NewLeftVertex;
1688 			}
1689 			else
1690 			{
1691 				// Right minus left depth
1692 				GLdouble RLDiff = ExtendedVertexList[RightVertex].Vertex[2]
1693 					- ExtendedVertexList[LeftVertex].Vertex[2];
1694 				if (RLDiff < 0)
1695 				{
1696 					// Left vertex ahead; advance the right one
1697 					GLint NewRightVertex = IncrementAndWrap(RightVertex,NumVertices);
1698 					*(VIPtr++) = LeftVertex;
1699 					*(VIPtr++) = RightVertex;
1700 					*(VIPtr++) = NewRightVertex;
1701 					RightVertex = NewRightVertex;
1702 				}
1703 				else if (RLDiff > 0)
1704 				{
1705 					// Right vertex ahead; advance the left one
1706 					GLint NewLeftVertex = DecrementAndWrap(LeftVertex,NumVertices);
1707 					*(VIPtr++) = NewLeftVertex;
1708 					*(VIPtr++) = LeftVertex;
1709 					*(VIPtr++) = RightVertex;
1710 					LeftVertex = NewLeftVertex;
1711 				}
1712 				else
1713 				{
1714 					// Advance to the closest one
1715 					GLint NewLeftVertex = DecrementAndWrap(LeftVertex,NumVertices);
1716 					GLint NewRightVertex = IncrementAndWrap(RightVertex,NumVertices);
1717 					RLDiff = ExtendedVertexList[NewRightVertex].Vertex[2]
1718 						- ExtendedVertexList[NewLeftVertex].Vertex[2];
1719 					if (RLDiff > 0)
1720 					{
1721 						// Left one is closer
1722 						*(VIPtr++) = NewLeftVertex;
1723 						*(VIPtr++) = LeftVertex;
1724 						*(VIPtr++) = RightVertex;
1725 						LeftVertex = NewLeftVertex;
1726 					}
1727 					else
1728 					{
1729 						// Right one is closer
1730 						*(VIPtr++) = LeftVertex;
1731 						*(VIPtr++) = RightVertex;
1732 						*(VIPtr++) = NewRightVertex;
1733 						RightVertex = NewRightVertex;
1734 					}
1735 				}
1736 			}
1737 		}
1738 
1739 		// Now, go!
1740 		glDrawElements(GL_TRIANGLES,3*(NumVertices-2),GL_UNSIGNED_INT,VertIndices);
1741 
1742 		// Switch off
1743 		glDisableClientState(GL_COLOR_ARRAY);
1744 	}
1745 	else
1746 		// Go!
1747 		// Don't care about triangulation here, because the polygon never got split
1748 		glDrawArrays(GL_POLYGON,0,NumVertices);
1749 
1750 	// Do textured rendering
1751 	if (TMgr.IsGlowMapped())
1752 	{
1753 		// Do blending here to get the necessary semitransparency;
1754 		// push the cutoff down so 0.5*0.5 (half of half-transparency)
1755 		// The cutoff is irrelevant if the texture is set to one of the blended modes
1756 		// instead of the crisp mode.
1757 		// Added "IsBlended" test, so that alpha-channel selection would work properly
1758 		// on a glowmap texture that is atop a texture that is opaque to the void.
1759 		glEnable(GL_BLEND);
1760 		glDisable(GL_ALPHA_TEST);
1761 		glDisable(GL_DEPTH_TEST);
1762 
1763 		TMgr.RenderGlowing();
1764 		SetBlend(TMgr.GlowBlend());
1765 
1766 		if (PolygonVariableShade)
1767 		{
1768 			glEnableClientState(GL_COLOR_ARRAY);
1769 			glColorPointer(3,GL_FLOAT,sizeof(ExtendedVertexData),ExtendedVertexList[0].GlowColor);
1770 			glDrawElements(GL_TRIANGLES,3*(NumVertices-2),GL_UNSIGNED_INT,VertIndices);
1771 			glDisableClientState(GL_COLOR_ARRAY);
1772 		}
1773 		else
1774 		{
1775 			SglColor3f(GlowColor,GlowColor,GlowColor);
1776 			glDrawArrays(GL_POLYGON,0,NumVertices);
1777 		}
1778 	}
1779 	}
1780 
1781 	// Revert to default blend
1782 	SetBlend(OGL_BlendType_Crossfade);
1783 	glEnable(GL_DEPTH_TEST);
1784 	TMgr.RestoreTextureMatrix();
1785 
1786 	return true;
1787 }
1788 
1789 
1790 // Render the wall texture as a landscape;
1791 // it returns whether or not the texture is a legitimate landscape texture
1792 // and it does not care about the surface's orientation
RenderAsLandscape(polygon_definition & RenderPolygon)1793 static bool RenderAsLandscape(polygon_definition& RenderPolygon)
1794 {
1795 	// Check for fog
1796 	bool IsActive = FogActive();
1797 	bool AffectsLandscapes = IsActive ? CurrFog->AffectsLandscapes : false;
1798 	if (AffectsLandscapes)
1799 	{
1800 		// Render as fog at infinity
1801 		glDisable(GL_FOG);
1802 		glDisable(GL_TEXTURE_2D);
1803 
1804 		// Set up the color
1805 		glColor3fv(CurrFogColor);
1806 
1807 		// Set up blending mode: opaque
1808 		glDisable(GL_ALPHA_TEST);
1809 		glDisable(GL_BLEND);
1810 
1811 		// Proper projection
1812 		SetProjectionType(Projection_Screen);
1813 
1814 		// Load an array of vertices
1815 		struct AltExtendedVertexData {
1816 			short Vertex[3];
1817 		} AltEVList[MAXIMUM_VERTICES_PER_SCREEN_POLYGON];
1818 
1819 		short NumVertices = RenderPolygon.vertex_count;
1820 		for (int k=0; k<NumVertices; k++)
1821 		{
1822 			// Create convenient references
1823 			point2d& Vertex = RenderPolygon.vertices[k];
1824 			AltExtendedVertexData& AltEV = AltEVList[k];
1825 
1826 			AdjustPoint(Vertex);
1827 			AltEV.Vertex[0] = Vertex.x;
1828 			AltEV.Vertex[1] = Vertex.y;
1829 			AltEV.Vertex[2] = -1;			// At positive oo
1830 		}
1831 		// Fog is flat-colored
1832 		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
1833 		glVertexPointer(3,GL_SHORT,sizeof(AltExtendedVertexData),AltEVList[0].Vertex);
1834 
1835 		// Go!
1836 		glDrawArrays(GL_POLYGON,0,NumVertices);
1837 
1838 		// Restore
1839 		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
1840 		glEnable(GL_FOG);
1841 
1842 		return true;
1843 	}
1844 
1845 	// Otherwise, the landscape would get fogged as a function of its world-geometry location
1846 	if (IsActive) glDisable(GL_FOG);
1847 
1848 	// Get the landscape-texturing options
1849 	LandscapeOptions *LandOpts = View_GetLandscapeOptions(RenderPolygon.ShapeDesc);
1850 
1851 	// Adjusted using the texture azimuth (yaw)
1852 	double AdjustedYaw = Yaw + FullCircleReciprocal*(LandOpts->Azimuth);
1853 
1854 	// Horizontal is straightforward
1855 	double HorizScale = double(1 << LandOpts->HorizExp);
1856 	// Vertical requires adjustment for aspect ratio;
1857 	// the texcoords must be stretched in the vertical direction
1858 	// if the texture is shrunken in the vertical direction
1859 	short AdjustedVertExp = LandOpts->VertExp + LandOpts->OGL_AspRatExp;
1860 	double VertScale = (AdjustedVertExp >= 0) ?
1861 		double(1 << AdjustedVertExp) :
1862 			1/double(1 << (-AdjustedVertExp));
1863 
1864 	// Do further adjustments,
1865 	// so as to do only two multiplies per texture-coordinate set
1866 	AdjustedYaw *= HorizScale;
1867 	HorizScale *= LandscapeRescale*Radian2Circle;
1868 	VertScale *= LandscapeRescale*Radian2Circle;
1869 
1870 	// Set up the texture manager with the input manager
1871 	TextureManager TMgr;
1872 	TMgr.ShapeDesc = RenderPolygon.ShapeDesc;
1873 	TMgr.ShadingTables = RenderPolygon.shading_tables;
1874 	TMgr.Texture = RenderPolygon.texture;
1875 	TMgr.TransferMode = RenderPolygon.transfer_mode;
1876 	TMgr.TransferData = RenderPolygon.transfer_data;
1877 	TMgr.IsShadeless = (RenderPolygon.flags&_SHADELESS_BIT) != 0;
1878 	TMgr.LandscapeVertRepeat = LandOpts->VertRepeat;
1879 	TMgr.Landscape_AspRatExp = LandOpts->OGL_AspRatExp;
1880 	TMgr.TextureType = OGL_Txtr_Landscape;
1881 
1882 	// Use that texture
1883 	if (!TMgr.Setup())
1884 	{
1885 		if (IsActive) glEnable(GL_FOG);
1886 		return false;
1887 	}
1888 
1889 	// Storage of intermediate results for mass render
1890 	ExtendedVertexData ExtendedVertexList[MAXIMUM_VERTICES_PER_SCREEN_POLYGON];
1891 
1892 	short NumVertices = RenderPolygon.vertex_count;
1893 	for (int k=0; k<NumVertices; k++)
1894 	{
1895 		// Create some convenient references
1896 		point2d& Vertex = RenderPolygon.vertices[k];
1897 		ExtendedVertexData& EVData = ExtendedVertexList[k];
1898 
1899 		// Load the vertex position;
1900 		// place it at positive infinity for the benefit of z-buffering
1901 		EVData.Vertex[0] = Vertex.x;
1902 		EVData.Vertex[1] = Vertex.y;
1903 		EVData.Vertex[2] = -1;
1904 
1905 		// Emit a ray from the vertex in OpenGL eye coords;
1906 		// it had been specified in screen coordinates
1907 		GLdouble VertexRay[3];
1908 		Screen2Ray(Vertex,VertexRay);
1909 
1910 		// Find the texture coordinates
1911 		GLdouble U = AdjustedYaw + HorizScale*VertexRay[0];
1912 		GLdouble V = 0.5 + VertScale*VertexRay[1];
1913 
1914 		// Store the texture coordinates
1915 		EVData.TexCoord[0] = U;
1916 		EVData.TexCoord[1] = V;
1917 	}
1918 
1919 	// Set up lighting:
1920 	glColor3f(1,1,1);
1921 
1922 	// Cribbed from RenderAsRealWall()
1923 	// Set up blending mode: either sharp edges or opaque
1924 	// Added support for partial-opacity blending of wall textures
1925 	// Added support for suppressing semitransparency when the void is on one side;
1926 	// this suppression is optional for those who like weird smearing effects
1927 	bool IsBlended = TMgr.IsBlended();
1928 	if (!RenderPolygon.VoidPresent || TMgr.VoidVisible())
1929 	{
1930 		if (IsBlended)
1931 		{
1932 			glEnable(GL_BLEND);
1933 			glDisable(GL_ALPHA_TEST);
1934 		} else {
1935 			glDisable(GL_BLEND);
1936 			glEnable(GL_ALPHA_TEST);
1937 		}
1938 	} else {
1939 		// Completely opaque if can't see through void
1940 		glDisable(GL_BLEND);
1941 		glDisable(GL_ALPHA_TEST);
1942 	}
1943 
1944 	// Proper projection
1945 	SetProjectionType(Projection_Screen);
1946 
1947 	// Location of data:
1948 	glVertexPointer(3,GL_DOUBLE,sizeof(ExtendedVertexData),ExtendedVertexList[0].Vertex);
1949 	glTexCoordPointer(2,GL_DOUBLE,sizeof(ExtendedVertexData),ExtendedVertexList[0].TexCoord);
1950 
1951 	// Painting a texture...
1952 	glEnable(GL_TEXTURE_2D);
1953 	TMgr.SetupTextureMatrix();
1954 	TMgr.RenderNormal();
1955 
1956 	// Go!
1957 	glDrawArrays(GL_POLYGON,0,NumVertices);
1958 
1959 	// Cribbed from RenderAsRealWall()
1960 	// Do textured rendering
1961 	if (TMgr.IsGlowMapped())
1962 	{
1963 		// Do blending here to get the necessary semitransparency;
1964 		// push the cutoff down so 0.5*0.5 (half of half-transparency)
1965 		// The cutoff is irrelevant if the texture is set to one of the blended modes
1966 		// instead of the crisp mode.
1967 		// Added "IsBlended" test, so that alpha-channel selection would work properly
1968 		// on a glowmap texture that is atop a texture that is opaque to the void.
1969 		glColor3f(1,1,1);
1970 		glEnable(GL_BLEND);
1971 		glDisable(GL_ALPHA_TEST);
1972 
1973 		TMgr.RenderGlowing();
1974 		SetBlend(TMgr.GlowBlend());
1975 		glDrawArrays(GL_POLYGON,0,NumVertices);
1976 	}
1977 
1978 	// Revert to default blend
1979 	SetBlend(OGL_BlendType_Crossfade);
1980 	TMgr.RestoreTextureMatrix();
1981 
1982 	if (IsActive) glEnable(GL_FOG);
1983 	return true;
1984 }
1985 
1986 // The wall renderer takes a flag that indicates whether or not it is vertical
OGL_RenderWall(polygon_definition & RenderPolygon,bool IsVertical)1987 bool OGL_RenderWall(polygon_definition& RenderPolygon, bool IsVertical)
1988 {
1989 	if (!OGL_IsActive()) return false;
1990 
1991 	// Make write-only, so as to avoid show-through by big objects behind,
1992 	// and also by walls behind landscapes
1993 	glDepthFunc(GL_ALWAYS);
1994 	switch(RenderPolygon.transfer_mode)
1995 	{
1996 	case _textured_transfer:
1997 	case _static_transfer:
1998 		RenderAsRealWall(RenderPolygon, IsVertical);
1999 		break;
2000 
2001 	case _big_landscaped_transfer:
2002 		RenderAsLandscape(RenderPolygon);
2003 		break;
2004 	}
2005 	// Standard z-buffering acceptance
2006 	glDepthFunc(GL_LEQUAL);
2007 	return true;
2008 }
2009 
2010 // Returns true if OpenGL is active; if not, then false.
OGL_RenderSprite(rectangle_definition & RenderRectangle)2011 bool OGL_RenderSprite(rectangle_definition& RenderRectangle)
2012 {
2013 	if (!OGL_IsActive()) return false;
2014 
2015 	// Set up the texture manager with the input manager
2016 	TextureManager TMgr;
2017 	TMgr.ShapeDesc = RenderRectangle.ShapeDesc;
2018 	TMgr.LowLevelShape = RenderRectangle.LowLevelShape;
2019 	TMgr.ShadingTables = RenderRectangle.shading_tables;
2020 	TMgr.Texture = RenderRectangle.texture;
2021 	TMgr.TransferMode = RenderRectangle.transfer_mode;
2022 	TMgr.TransferData = RenderRectangle.transfer_data;
2023 	TMgr.IsShadeless = (RenderRectangle.flags&_SHADELESS_BIT) != 0;
2024 
2025 	// Is this an inhabitant or a weapons-in-hand texture?
2026 	// Test by using the distance away from the viewpoint
2027 	bool IsInhabitant;
2028 	bool IsWeaponsInHand;
2029 	double RayDistance = double(RenderRectangle.depth);
2030 	if (RayDistance > 0)
2031 	{
2032 		IsInhabitant = true;
2033 		IsWeaponsInHand = false;
2034 		TMgr.TextureType = OGL_Txtr_Inhabitant;
2035 	}
2036 	else if (RayDistance == 0)
2037 	{
2038 		IsInhabitant = false;
2039 		IsWeaponsInHand = true;
2040 		TMgr.TextureType = OGL_Txtr_WeaponsInHand;
2041 	}
2042 	else return true;
2043 
2044 	// Render as a model if one is found
2045 	if (IsInhabitant)
2046 	{
2047 		OGL_ModelData *ModelPtr = RenderRectangle.ModelPtr;
2048 		if (ModelPtr)
2049 		{
2050 			RenderModelSetup(RenderRectangle);
2051 			return true;
2052 		}
2053 	}
2054 
2055 	// Find texture coordinates
2056 	ExtendedVertexData ExtendedVertexList[4];
2057 
2058 	point2d TopLeft, BottomRight;
2059 	// Clipped corners:
2060 	TopLeft.x = MAX(RenderRectangle.x0,RenderRectangle.clip_left);
2061 	TopLeft.y = MAX(RenderRectangle.y0,RenderRectangle.clip_top);
2062 	BottomRight.x = MIN(RenderRectangle.x1,RenderRectangle.clip_right);
2063 	BottomRight.y = MIN(RenderRectangle.y1,RenderRectangle.clip_bottom);
2064 
2065 	if (IsInhabitant)
2066 	{
2067 		// OpenGL eye coordinates
2068 		GLdouble VertexRay[3];
2069 		Screen2Ray(TopLeft,VertexRay);
2070 		VecScalarMult(VertexRay,RayDistance,ExtendedVertexList[0].Vertex);
2071 		Screen2Ray(BottomRight,VertexRay);
2072 		VecScalarMult(VertexRay,RayDistance,ExtendedVertexList[2].Vertex);
2073 	}
2074 	else if (IsWeaponsInHand)
2075 	{
2076 		// Simple adjustment
2077 		AdjustPoint(TopLeft);
2078 		AdjustPoint(BottomRight);
2079 
2080 		// Screen coordinates; weapons-in-hand are in the foreground
2081 		ExtendedVertexList[0].Vertex[0] = TopLeft.x;
2082 		ExtendedVertexList[0].Vertex[1] = TopLeft.y;
2083 		ExtendedVertexList[0].Vertex[2] = 1;
2084 		ExtendedVertexList[2].Vertex[0] = BottomRight.x;
2085 		ExtendedVertexList[2].Vertex[1] = BottomRight.y;
2086 		ExtendedVertexList[2].Vertex[2] = 1;
2087 	}
2088 	else return true;
2089 
2090 	// Completely clipped away?
2091 	if (BottomRight.x <= TopLeft.x) return true;
2092 	if (BottomRight.y <= TopLeft.y) return true;
2093 
2094 	// Use that texture
2095 	if (!TMgr.Setup()) return true;
2096 
2097 	// Calculate the texture coordinates;
2098 	// the scanline direction is downward, (texture coordinate 0)
2099 	// while the line-to-line direction is rightward (texture coordinate 1)
2100 	GLdouble U_Scale = TMgr.U_Scale/(RenderRectangle.y1 - RenderRectangle.y0);
2101 	GLdouble V_Scale = TMgr.V_Scale/(RenderRectangle.x1 - RenderRectangle.x0);
2102 	GLdouble U_Offset = TMgr.U_Offset;
2103 	GLdouble V_Offset = TMgr.V_Offset;
2104 
2105 	if (RenderRectangle.flip_vertical)
2106 	{
2107 		ExtendedVertexList[0].TexCoord[0] = U_Offset + U_Scale*(RenderRectangle.y1 - TopLeft.y);
2108 		ExtendedVertexList[2].TexCoord[0] = U_Offset + U_Scale*(RenderRectangle.y1 - BottomRight.y);
2109 	} else {
2110 		ExtendedVertexList[0].TexCoord[0] = U_Offset + U_Scale*(TopLeft.y - RenderRectangle.y0);
2111 		ExtendedVertexList[2].TexCoord[0] = U_Offset + U_Scale*(BottomRight.y - RenderRectangle.y0);
2112 	}
2113 	if (RenderRectangle.flip_horizontal)
2114 	{
2115 		ExtendedVertexList[0].TexCoord[1] = V_Offset + V_Scale*(RenderRectangle.x1 - TopLeft.x);
2116 		ExtendedVertexList[2].TexCoord[1] = V_Offset + V_Scale*(RenderRectangle.x1 - BottomRight.x);
2117 	} else {
2118 		ExtendedVertexList[0].TexCoord[1] = V_Offset + V_Scale*(TopLeft.x - RenderRectangle.x0);
2119 		ExtendedVertexList[2].TexCoord[1] = V_Offset + V_Scale*(BottomRight.x - RenderRectangle.x0);
2120 	}
2121 
2122 	// Fill in remaining points
2123 	// Be sure that the order gives a sidedness the same as
2124 	// that of the world-geometry polygons
2125 	ExtendedVertexList[1].Vertex[0] = ExtendedVertexList[2].Vertex[0];
2126 	ExtendedVertexList[1].Vertex[1] = ExtendedVertexList[0].Vertex[1];
2127 	ExtendedVertexList[1].Vertex[2] = ExtendedVertexList[0].Vertex[2];
2128 	ExtendedVertexList[1].TexCoord[0] = ExtendedVertexList[0].TexCoord[0];
2129 	ExtendedVertexList[1].TexCoord[1] = ExtendedVertexList[2].TexCoord[1];
2130 	ExtendedVertexList[3].Vertex[0] = ExtendedVertexList[0].Vertex[0];
2131 	ExtendedVertexList[3].Vertex[1] = ExtendedVertexList[2].Vertex[1];
2132 	ExtendedVertexList[3].Vertex[2] = ExtendedVertexList[2].Vertex[2];
2133 	ExtendedVertexList[3].TexCoord[0] = ExtendedVertexList[2].TexCoord[0];
2134 	ExtendedVertexList[3].TexCoord[1] = ExtendedVertexList[0].TexCoord[1];
2135 
2136 	// Proper projection
2137 	if (IsInhabitant)
2138 		SetProjectionType(Projection_OpenGL_Eye);
2139 	else if (IsWeaponsInHand)
2140 		SetProjectionType(Projection_Screen);
2141 
2142 	bool IsBlended = TMgr.IsBlended();
2143 	bool ExternallyLit = false;
2144 	GLfloat Color[4];
2145 	DoLightingAndBlending(RenderRectangle, IsBlended,
2146 		Color, ExternallyLit);
2147 
2148 	if (IsBlended)
2149 		// alpha test will be disabled, so don't hose z buffer
2150 		glDisable(GL_DEPTH_TEST);
2151 
2152 	// Already corrected
2153 	glColor4fv(Color);
2154 
2155 	// Location of data:
2156 	glVertexPointer(3,GL_DOUBLE,sizeof(ExtendedVertexData),ExtendedVertexList[0].Vertex);
2157 	glTexCoordPointer(2,GL_DOUBLE,sizeof(ExtendedVertexData),ExtendedVertexList[0].TexCoord);
2158 	glEnable(GL_TEXTURE_2D);
2159 
2160 	// Go!
2161 	TMgr.SetupTextureMatrix();
2162 	TMgr.RenderNormal();	// Always do this, of course
2163 	if (RenderRectangle.transfer_mode == _static_transfer)
2164 	{
2165 		SetupStaticMode(RenderRectangle.transfer_data);
2166 		if (UseFlatStatic)
2167 		{
2168 			glDisable(GL_DEPTH_TEST);
2169 			glDrawArrays(GL_POLYGON,0,4);
2170 		} else {
2171 			// Do multitextured stippling to create the static effect
2172 			for (int k=0; k<StaticEffectPasses; k++)
2173 			{
2174 				StaticModeIndivSetup(k);
2175 				glDrawArrays(GL_POLYGON,0,4);
2176 			}
2177 		}
2178 		TeardownStaticMode();
2179 	}
2180 	else
2181 	{
2182 		// Ought not to set this for static mode
2183 		SetBlend(TMgr.NormalBlend());
2184 
2185 		// Do textured rendering
2186 		glDrawArrays(GL_POLYGON,0,4);
2187 
2188 		if (TMgr.IsGlowMapped())
2189 		{
2190 			// Do blending here to get the necessary semitransparency;
2191 			// push the cutoff down so 0.5*0.5 (half of half-transparency)
2192 		  // DON'T sRGB this.
2193 			GLfloat GlowColor = TMgr.MinGlowIntensity();
2194 			glColor4f(std::max(GlowColor,Color[0]),std::max(GlowColor,Color[1]),std::max(GlowColor,Color[2]),Color[3]);
2195 			glEnable(GL_BLEND);
2196 			glDisable(GL_ALPHA_TEST);
2197 			glDisable(GL_DEPTH_TEST);
2198 
2199 			TMgr.RenderGlowing();
2200 			SetBlend(TMgr.GlowBlend());
2201 			glDrawArrays(GL_POLYGON,0,4);
2202 		}
2203 	}
2204 
2205 	// Revert to default blend
2206 	SetBlend(OGL_BlendType_Crossfade);
2207 	TMgr.RestoreTextureMatrix();
2208 
2209 	glEnable(GL_DEPTH_TEST);
2210 
2211 	return true;
2212 }
2213 
RenderModelSetup(rectangle_definition & RenderRectangle)2214 bool RenderModelSetup(rectangle_definition& RenderRectangle)
2215 {
2216 	OGL_ModelData *ModelPtr = RenderRectangle.ModelPtr;
2217 	assert(ModelPtr);
2218 
2219 	// Initial clip check: where relative to the liquid?
2220 	float Scale = RenderRectangle.Scale;
2221 	GLfloat ModelFloor = Scale*ModelPtr->Model.BoundingBox[0][2];
2222 	GLfloat ModelCeiling = Scale*ModelPtr->Model.BoundingBox[1][2];
2223 	short LiquidRelHeight = RenderRectangle.LiquidRelHeight;
2224 
2225 	if (RenderRectangle.BelowLiquid)
2226 	{
2227 		// Liquid below the bottom?
2228 		if (LiquidRelHeight <= ModelFloor)
2229 			return false;
2230 	}
2231 	else
2232 	{
2233 		// Liquid above the top?
2234 		if (LiquidRelHeight >= ModelCeiling)
2235 			return false;
2236 	}
2237 	if (RenderRectangle.clip_left >= RenderRectangle.x1) return false;
2238 	if (RenderRectangle.clip_right <= RenderRectangle.x0) return false;
2239 	if (RenderRectangle.clip_top >= RenderRectangle.y1) return false;
2240 	if (RenderRectangle.clip_bottom <= RenderRectangle.y0) return false;
2241 
2242 	// Find an animated model's vertex positions and normals:
2243 	short ModelSequence = RenderRectangle.ModelSequence;
2244 	if (ModelSequence >= 0)
2245 	{
2246 		int NumFrames = ModelPtr->Model.NumSeqFrames(ModelSequence);
2247 		if (NumFrames > 0)
2248 		{
2249 			short ModelFrame = PIN(RenderRectangle.ModelFrame,0,NumFrames-1);
2250 			short NextModelFrame = PIN(RenderRectangle.NextModelFrame,0,NumFrames-1);
2251 			float MixFrac = RenderRectangle.MixFrac;
2252 			ModelPtr->Model.FindPositions_Sequence(true,
2253 				ModelSequence,ModelFrame,MixFrac,NextModelFrame);
2254 		}
2255 		else
2256 			ModelPtr->Model.FindPositions_Neutral(true);	// Fallback: neutral
2257 	}
2258 	else
2259 		ModelPtr->Model.FindPositions_Neutral(true);	// Fallback: neutral (will do nothing for static models)
2260 
2261 	// For finding the clip planes: 0, 1, 2, 3, and 4
2262 	bool ClipLeft = false, ClipRight = false, ClipTop = false, ClipBottom = false, ClipLiquid = false;
2263 	GLdouble ClipPlane[4] = {0,0,0,0};
2264 
2265 	if (RenderRectangle.clip_left >= RenderRectangle.x0)
2266 	{
2267 		ClipLeft = true;
2268 		glEnable(GL_CLIP_PLANE0);
2269 		ClipPlane[0] = 1;
2270 		ClipPlane[1] = 0;
2271 		ClipPlane[2] = XScaleRecip*(RenderRectangle.clip_left - XOffset);
2272 		glClipPlane(GL_CLIP_PLANE0,ClipPlane);
2273 	}
2274 
2275 	if (RenderRectangle.clip_right <= RenderRectangle.x1)
2276 	{
2277 		ClipRight = true;
2278 		glEnable(GL_CLIP_PLANE1);
2279 		ClipPlane[0] = - 1;
2280 		ClipPlane[1] = 0;
2281 		ClipPlane[2] = - XScaleRecip*(RenderRectangle.clip_right - XOffset);
2282 		glClipPlane(GL_CLIP_PLANE1,ClipPlane);
2283 	}
2284 
2285 	if (RenderRectangle.clip_top >= RenderRectangle.y0)
2286 	{
2287 		ClipTop = true;
2288 		glEnable(GL_CLIP_PLANE2);
2289 		ClipPlane[0] = 0;
2290 		ClipPlane[1] = - 1;
2291 		ClipPlane[2] = - YScaleRecip*(RenderRectangle.clip_top - YOffset);
2292 		glClipPlane(GL_CLIP_PLANE2,ClipPlane);
2293 	}
2294 
2295 	if (RenderRectangle.clip_bottom <= RenderRectangle.y1)
2296 	{
2297 		ClipBottom = true;
2298 		glEnable(GL_CLIP_PLANE3);
2299 		ClipPlane[0] = 0;
2300 		ClipPlane[1] = 1;
2301 		ClipPlane[2] = YScaleRecip*(RenderRectangle.clip_bottom - YOffset);
2302 		glClipPlane(GL_CLIP_PLANE3,ClipPlane);
2303 	}
2304 
2305 	// Get from the model coordinates to the screen coordinates.
2306 	SetProjectionType(Projection_OpenGL_Eye);
2307 	glMatrixMode(GL_MODELVIEW);
2308 	glPushMatrix();
2309 	glLoadMatrixd(World_2_OGLEye);
2310 	world_point3d& Position = RenderRectangle.Position;
2311 	glTranslatef(Position.x,Position.y,Position.z);
2312 
2313 	// At model's position; now apply the liquid clipping
2314 	if (RenderRectangle.BelowLiquid)
2315 	{
2316 		// Liquid above the bottom? If so, then clip downward
2317 		if (LiquidRelHeight >= ModelFloor)
2318 		{
2319 			ClipLiquid = true;
2320 			glEnable(GL_CLIP_PLANE4);
2321 			ClipPlane[0] = ClipPlane[1] = 0;
2322 			ClipPlane[2] = - 1;
2323 			ClipPlane[3] = LiquidRelHeight;
2324 			glClipPlane(GL_CLIP_PLANE4,ClipPlane);
2325 		}
2326 	}
2327 	else
2328 	{
2329 		// Liquid below the top? If so, then clip upward
2330 		if (LiquidRelHeight <= ModelCeiling)
2331 		{
2332 			ClipLiquid = true;
2333 			glEnable(GL_CLIP_PLANE4);
2334 			ClipPlane[0] = ClipPlane[1] = 0;
2335 			ClipPlane[2] = 1;
2336 			ClipPlane[3] = - LiquidRelHeight;
2337 			glClipPlane(GL_CLIP_PLANE4,ClipPlane);
2338 		}
2339 	}
2340 
2341 	// Its orientation and size
2342 	glRotated((360.0/FULL_CIRCLE)*RenderRectangle.Azimuth,0,0,1);
2343 	GLfloat HorizScale = Scale*RenderRectangle.HorizScale;
2344 	glScalef(HorizScale,HorizScale,Scale);
2345 
2346 	// Be sure to include texture-mode effects as appropriate.
2347 	short CollColor = GET_DESCRIPTOR_COLLECTION(RenderRectangle.ShapeDesc);
2348 	short Collection = GET_COLLECTION(CollColor);
2349 	short CLUT = ModifyCLUT(RenderRectangle.transfer_mode,GET_COLLECTION_CLUT(CollColor));
2350 	bool ModelRendered = RenderModel(RenderRectangle,Collection,CLUT);
2351 
2352 	glPopMatrix();
2353 
2354 	// No need for the clip planes anymore
2355 	if (ClipLeft) glDisable(GL_CLIP_PLANE0);
2356 	if (ClipRight) glDisable(GL_CLIP_PLANE1);
2357 	if (ClipTop) glDisable(GL_CLIP_PLANE2);
2358 	if (ClipBottom) glDisable(GL_CLIP_PLANE3);
2359 	if (ClipLiquid) glDisable(GL_CLIP_PLANE4);
2360 
2361 	return ModelRendered;
2362 }
2363 
2364 
RenderModel(rectangle_definition & RenderRectangle,short Collection,short CLUT)2365 bool RenderModel(rectangle_definition& RenderRectangle, short Collection, short CLUT)
2366 {
2367 	OGL_ModelData *ModelPtr = RenderRectangle.ModelPtr;
2368 	assert(ModelPtr);
2369 
2370 	// Get the skin; test for whether one was actually found
2371 	OGL_SkinData *SkinPtr = ModelPtr->GetSkin(CLUT);
2372 	if (!SkinPtr) return false;
2373 
2374 	// Parallel to TextureManager::IsBlended() in OGL_Textures.h
2375 	bool IsBlended = SkinPtr->OpacityType != OGL_OpacType_Crisp;
2376 	bool ExternallyLit = false;
2377 	bool IsGlowmappable = DoLightingAndBlending(RenderRectangle, IsBlended,
2378 		ShaderData.Color, ExternallyLit);
2379 
2380 	ShaderData.ModelPtr = ModelPtr;
2381 	ShaderData.SkinPtr = SkinPtr;
2382 	ShaderData.Collection = Collection;
2383 	ShaderData.CLUT = CLUT;
2384 
2385 	// Don't care about the magnitude of this vector
2386 	short Azimuth = normalize_angle(RenderRectangle.Azimuth);
2387 	GLfloat Cosine = cosine_table[Azimuth];
2388 	GLfloat Sine = sine_table[Azimuth];
2389 	ModelRenderObject.ViewDirection[0] =   ViewDir[0]*Cosine + ViewDir[1]*Sine;
2390 	ModelRenderObject.ViewDirection[1] = - ViewDir[0]*Sine + ViewDir[1]*Cosine;
2391 	// The z-component is already set -- to 0
2392 
2393 	// Set up the render sidedness
2394 	if (ModelPtr->Sidedness < 0)
2395 	{
2396 		// Counterclockwise sides rendered
2397 		glEnable(GL_CULL_FACE);
2398 		glFrontFace(GL_CCW);
2399 	}
2400 	else if (ModelPtr->Sidedness == 0)
2401 	{
2402 		// Both sides rendered
2403 		glDisable(GL_CULL_FACE);
2404 	}
2405 	// Default: clockwise sides rendered
2406 
2407 	if (RenderRectangle.transfer_mode == _static_transfer)
2408 	{
2409 		SetupStaticMode(RenderRectangle.transfer_data);
2410 		if (UseFlatStatic)
2411 		{
2412 			// Do explicit depth sort because these textures are semitransparent
2413 			StandardShaders[0].Flags = ModelRenderer::Textured;
2414 			ModelRenderObject.Render(ModelPtr->Model, StandardShaders,
2415 				1, 0, true);
2416 		} else {
2417 			// Do multitextured stippling to create the static effect
2418 			ModelRenderObject.Render(ModelPtr->Model, StaticModeShaders,
2419 				StaticEffectPasses, SeparableStaticEffectPasses, true);
2420 		}
2421 		TeardownStaticMode();
2422 	}
2423 	else
2424 	{
2425 		bool IsGlowing = IsGlowmappable && SkinPtr->GlowImg.IsPresent();
2426 		int NumShaders = IsGlowing ? 2 : 1;
2427 		int NumSeparableShaders = IsBlended ? 0 : 1;
2428 
2429 		SET_FLAG(StandardShaders[0].Flags,ModelRenderer::ExtLight,ExternallyLit);
2430 		SET_FLAG(StandardShaders[0].Flags,ModelRenderer::EL_SemiTpt,false);
2431 
2432 		if (ExternallyLit)
2433 		{
2434 			// Find the light values to use
2435 			GLfloat AvgLight = (RenderRectangle.ceiling_light + RenderRectangle.ambient_shade)/GLfloat(2*FIXED_ONE);
2436 			GLfloat LightDiff = (RenderRectangle.ceiling_light - RenderRectangle.ambient_shade)/GLfloat(2*FIXED_ONE);
2437 			GLfloat *Dir = RenderRectangle.LightDirection;
2438 			// In the direction of observation; use this to find miner's-light effect
2439 			GLfloat LightInDir = AvgLight - Dir[2]*LightDiff;
2440 			GLfloat Color[3];
2441 			FindShadingColor(RenderRectangle.LightDepth,int(FIXED_ONE*LightInDir + 0.5),Color);
2442 
2443 			LightingData.Type = ModelPtr->LightType;
2444 			LightingData.ProjDistance = RenderRectangle.ProjDistance;
2445 			LightingData.Dir = Dir;
2446 			LightingData.AvgLight = AvgLight;
2447 			LightingData.LightDiff = LightDiff;
2448 
2449 			GLfloat Opacity = ShaderData.Color[3];
2450 			LightingData.Opacity = Opacity;
2451 			if (Opacity < 1)
2452 				SET_FLAG(StandardShaders[0].Flags,ModelRenderer::EL_SemiTpt,true);
2453 
2454 			// Whether to fade miner's light toward model sides
2455 			bool NoFade = false;
2456 
2457 			switch(LightingData.Type)
2458 			{
2459 			case OGL_MLight_Fast_NoFade:
2460 				NoFade = true;
2461 			case OGL_MLight_Fast:
2462 				for (int c=0; c<3; c++)
2463 				{
2464 					// Note: the miner's-light effect adds to the existing light,
2465 					// thus we subtract the bkgd before adding to the rest of the light
2466 					if (NoFade)
2467 					{
2468 						LightingData.Colors[c][0] = 0;
2469 						LightingData.Colors[c][1] = 0;
2470 						LightingData.Colors[c][2] = LightDiff;
2471 						LightingData.Colors[c][3] = AvgLight + (Color[c] - LightInDir);
2472 					}
2473 					else
2474 					{
2475 						GLfloat HC = (Color[c] - LightInDir)/2;
2476 						LightingData.Colors[c][0] = - HC*Dir[0];
2477 						LightingData.Colors[c][1] = - HC*Dir[1];
2478 						LightingData.Colors[c][2] = LightDiff - HC*Dir[2];
2479 						LightingData.Colors[c][3] = AvgLight + HC;
2480 					}
2481 				}
2482 			}
2483 		}
2484 
2485 		ModelRenderObject.Render(ModelPtr->Model, StandardShaders, NumShaders,
2486 			NumSeparableShaders, true);
2487 
2488 		// Revert to default blend
2489 		SetBlend(OGL_BlendType_Crossfade);
2490 
2491 		// Restore default
2492 		if (ExternallyLit) glDisableClientState(GL_COLOR_ARRAY);
2493 	}
2494 
2495 	// Restore the default render sidedness
2496 	if (ModelPtr->Sidedness <= 0)
2497 	{
2498 		// Clockwise sides rendered
2499 		glEnable(GL_CULL_FACE);
2500 		glFrontFace(GL_CW);
2501 	}
2502 
2503 	return true;
2504 }
2505 
DoLightingAndBlending(rectangle_definition & RenderRectangle,bool & IsBlended,GLfloat * Color,bool & ExternallyLit)2506 bool DoLightingAndBlending(rectangle_definition& RenderRectangle, bool& IsBlended,
2507 	GLfloat *Color, bool& ExternallyLit)
2508 {
2509 	bool IsGlowmappable = true;
2510 	ExternallyLit = true;
2511 
2512 	// Apply lighting
2513 	bool IsInvisible = false;
2514 	if (RenderRectangle.transfer_mode == _static_transfer)
2515 	{
2516 		// Crisp, no glowmap
2517 		IsBlended = false;
2518 		IsGlowmappable = false;
2519 		glEnable(GL_ALPHA_TEST);
2520 		glDisable(GL_BLEND);
2521 		return IsGlowmappable;
2522 	}
2523 	else if (RenderRectangle.transfer_mode == _tinted_transfer)
2524 	{
2525 		// Used for invisibility; the tinting refers to the already-rendered color's fate
2526 		// The opacity is controlled by the transfer data; its value is guessed from
2527 		// the rendering source code (render.c, scottish_textures.c, low_level_textures.c)
2528 		Color[0] = Color[1] = Color[2] = 0;
2529 		Color[3] = 1 - RenderRectangle.transfer_data/32.0F;
2530 		if(Using_sRGB) Color[3] = std::sqrt(Color[3]);
2531 		IsInvisible = true;
2532 		IsGlowmappable = false;
2533 	}
2534 	else if (RenderRectangle.flags&_SHADELESS_BIT)
2535 	{
2536 		// Only set when infravision is active
2537 		Color[0] = Color[1] = Color[2] = 1;
2538 		Color[3] = RenderRectangle.Opacity;
2539 		IsGlowmappable = false;
2540 	}
2541 	else if (RenderRectangle.ambient_shade < 0)
2542 	{
2543 		GLfloat Light = (- RenderRectangle.ambient_shade)/GLfloat(FIXED_ONE);
2544 		Color[0] = Color[1] = Color[2] = Light;
2545 		Color[3] = RenderRectangle.Opacity;
2546 	}
2547 	else
2548 	{
2549 		// External lighting includes the player's "miner's light"
2550 		ExternallyLit = true;
2551 		FindShadingColor(RenderRectangle.depth,RenderRectangle.ambient_shade,Color);
2552 		Color[3] = RenderRectangle.Opacity;
2553 	}
2554 
2555 	// Make the sprites crisp-edged, except if they are in invisibility mode
2556 	// or are otherwise semitransparent
2557 	if (IsInvisible || IsBlended || (RenderRectangle.Opacity < 1))
2558 	{
2559 		glDisable(GL_ALPHA_TEST);
2560 		glEnable(GL_BLEND);
2561 		IsBlended = true;
2562 	} else {
2563 		glEnable(GL_ALPHA_TEST);
2564 		glDisable(GL_BLEND);
2565 	}
2566 
2567 	return IsGlowmappable;
2568 }
2569 
2570 
SetupStaticMode(int16 transfer_data)2571 void SetupStaticMode(int16 transfer_data)
2572 {
2573 	if (UseFlatStatic)
2574 	{
2575 		// Per-sprite coloring; be sure to add the transparency in appropriate fashion
2576 		for (int c=0; c<3; c++)
2577 			FlatStaticColor[c] = StaticRandom.KISS() + StaticRandom.LFIB4();
2578 		FlatStaticColor[3] = 65535 - transfer_data;
2579 
2580 		// Do flat-color version of static effect
2581 		glDisable(GL_ALPHA_TEST);
2582 		glEnable(GL_BLEND);
2583 		SglColor4usv(FlatStaticColor);
2584 	} else {
2585 #ifdef USE_STIPPLE_STATIC_EFFECT
2586 		// Do multitextured stippling to create the static effect
2587 
2588 		// The colors:
2589 		// Index from 1 to 3; index 0 is reserve for the initial blackout
2590 		for (int c=1; c<4; c++)
2591 			for (int n=0; n<StatPatLen; n++)
2592 				StaticPatterns[c][n] = StaticRandom.KISS() + StaticRandom.LFIB4();
2593 
2594 		// The alpha channel:
2595 		for (int n=0; n<StatPatLen; n++)
2596 		{
2597 			uint32 Val = 0;
2598 			// Avoid doing extra random-number evaluations
2599 			// for the case of complete opacity
2600 			if (transfer_data > 0)
2601 			{
2602 				for (int k=0; k<32; k++)
2603 				{
2604 					// Have to do this for every bit to get the proper probability distribution
2605 					Val <<= 1;
2606 					uint16 RandSamp = uint16(StaticRandom.KISS());
2607 					Val |= (RandSamp >= transfer_data) ? 1 : 0;
2608 				}
2609 			}
2610 			else
2611 				Val = 0xffffffff;
2612 
2613 			// Premultiply the stipple-color values by the alpha
2614 			StaticPatterns[0][n] = Val;
2615 			for (int c=1; c<4; c++)
2616 				StaticPatterns[c][n] &= Val;
2617 		}
2618 
2619 		// Get ready to use those static patterns
2620 		glEnable(GL_POLYGON_STIPPLE);
2621 #else
2622 		// Use the stencil buffer to create the static effect
2623 		glEnable(GL_STENCIL_TEST);
2624 
2625 		StencilTxtrOpacity = 1 - transfer_data/65535.0;
2626 #endif
2627 	}
2628 }
2629 
TeardownStaticMode()2630 void TeardownStaticMode()
2631 {
2632 	if (UseFlatStatic)
2633 	{
2634 		// Nothing
2635 	}
2636 	else
2637 	{
2638 #ifdef USE_STIPPLE_STATIC_EFFECT
2639 		// Restore the default blending
2640 		glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
2641 		// glDisable(GL_COLOR_LOGIC_OP);
2642 		glDisable(GL_POLYGON_STIPPLE);
2643 #else
2644 		// Done with the stencil buffer
2645 		glDisable(GL_STENCIL_TEST);
2646 #endif
2647 	}
2648 }
2649 
2650 
NormalShader(void * Data)2651 void NormalShader(void *Data)
2652 {
2653 	// Normal setup: be sure to use the normal color
2654 	SglColor4fv(ShaderData.Color);
2655 
2656 	if (ShaderData.ModelPtr->Use(ShaderData.CLUT,OGL_SkinManager::Normal))
2657 	{
2658 		LoadModelSkin(ShaderData.SkinPtr->NormalImg,ShaderData.Collection, ShaderData.CLUT);
2659 		SetBlend(ShaderData.SkinPtr->NormalBlend);
2660 	}
2661 }
2662 
GlowingShader(void * Data)2663 void GlowingShader(void *Data)
2664 {
2665 	// Glowmapped setup
2666 	GLfloat GlowColor = ShaderData.SkinPtr->MinGlowIntensity;
2667 	SglColor4f(std::max(ShaderData.Color[0],GlowColor),std::max(ShaderData.Color[1],GlowColor),std::max(ShaderData.Color[2],GlowColor),ShaderData.Color[3]*(Using_sRGB ? ShaderData.Color[3] : 1.0));
2668 	glEnable(GL_BLEND);
2669 	glDisable(GL_ALPHA_TEST);
2670 
2671 	if (ShaderData.ModelPtr->Use(ShaderData.CLUT,OGL_SkinManager::Glowing))
2672 	{
2673 		LoadModelSkin(ShaderData.SkinPtr->GlowImg, ShaderData.Collection, ShaderData.CLUT);
2674 		SetBlend(ShaderData.SkinPtr->GlowBlend);
2675 	}
2676 }
2677 
2678 
StaticModeIndivSetup(int SeqNo)2679 void StaticModeIndivSetup(int SeqNo)
2680 {
2681 #ifdef USE_STIPPLE_STATIC_EFFECT
2682 	// Black [backing], red, green, blue
2683 	const GLfloat StaticBaseColors[4][3] = {{0,0,0},{1,0,0},{0,1,0},{0,0,1}};
2684 
2685 	switch(SeqNo)
2686 	{
2687 	case 0:	// In case of another go-around, as in z-buffer-less rendering
2688 		// Paint on the backing color by making it unblended
2689 		glDisable(GL_BLEND);
2690 		// glDisable(GL_COLOR_LOGIC_OP);
2691 		break;
2692 
2693 	case 1:	// No need to do this for cases 2 and 3, since they will follow
2694 		// Add the color-channel contributions with appropriate blending
2695 		glEnable(GL_BLEND);
2696 		glBlendFunc(GL_SRC_ALPHA,GL_ONE);
2697 		// glEnable(GL_COLOR_LOGIC_OP);
2698 		// glLogicOp(GL_OR);
2699 	}
2700 
2701 	// no need to correct
2702 	glColor3fv(StaticBaseColors[SeqNo]);
2703 	glPolygonStipple((byte *)StaticPatterns[SeqNo]);
2704 #else
2705 	// Stencil buffering
2706 	switch(SeqNo)
2707 	{
2708 	case 0:
2709 		// The stencil buffer will become 0 across the whole rendered object
2710 		glStencilFunc(GL_ALWAYS,1,1);
2711 		glStencilOp(GL_KEEP, GL_ZERO, GL_ZERO);
2712 		glStencilMask(1);
2713 		glDisable(GL_BLEND);
2714 		glDisable(GL_ALPHA_TEST);
2715 		glColorMask(GL_FALSE,GL_FALSE,GL_FALSE,GL_FALSE);
2716 		break;
2717 
2718 	case 1:
2719 		// The stencil buffer will become 1 everywhere a pixel is to be rendered
2720 		glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
2721 		glEnable(GL_ALPHA_TEST);
2722 		break;
2723 
2724 	case 2:
2725 		// Use the stencil buffer -- don't write into it, of course
2726 		glStencilFunc(GL_EQUAL,1,1);
2727 		glStencilMask(0);
2728 		glEnable(GL_BLEND);
2729 		glDisable(GL_ALPHA_TEST);
2730 		if(Using_sRGB) glDisable(GL_FRAMEBUFFER_sRGB);
2731 		glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
2732 		glColor4f(1,1,1,Using_sRGB ? StencilTxtrOpacity*StencilTxtrOpacity : StencilTxtrOpacity);	// Static is fully bright and partially transparent
2733 		break;
2734 	}
2735 #endif
2736 }
2737 
StaticModeShader(void * Data)2738 void StaticModeShader(void *Data)
2739 {
2740 	int *Which = (int *)Data;
2741 	StaticModeIndivSetup(*Which);
2742 
2743 #ifndef USE_STIPPLE_STATIC_EFFECT
2744 	if (*Which < 2)
2745 	{
2746 #endif
2747 	// The silhouette texture is a "normal" one
2748 	if (ShaderData.ModelPtr->Use(ShaderData.CLUT,OGL_SkinManager::Normal))
2749 		LoadModelSkin(ShaderData.SkinPtr->NormalImg, ShaderData.Collection, ShaderData.CLUT);
2750 #ifndef USE_STIPPLE_STATIC_EFFECT
2751 	}
2752 	else
2753 	{
2754 		// For the static effect
2755 		glBindTexture(GL_TEXTURE_2D,0);
2756 
2757 		const int TxSize = 64;
2758 		const int TxPxls = TxSize*TxSize;
2759 		static uint32 Buffer[TxPxls];
2760 		for (int k=0; k<TxPxls; k++)
2761 		{
2762 			uint32 Pxl = 0;
2763 			for (int m=0; m<3; m++)
2764 			{
2765 				Pxl += (StaticRandom.KISS() + StaticRandom.LFIB4()) & 0x000000ff;
2766 				Pxl <<= 8;
2767 			}
2768 			Pxl += 0xff;
2769 			Buffer[k] = Pxl;
2770 		}
2771 
2772 		glTexImage2D(GL_TEXTURE_2D, 0, Using_sRGB ? GL_SRGB_ALPHA : GL_RGBA8, TxSize, TxSize,
2773 			0, GL_RGBA, GL_UNSIGNED_BYTE, Buffer);
2774 
2775 		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
2776 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
2777 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
2778 
2779 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
2780 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
2781 	}
2782 	if(*Which == 3 && Using_sRGB) glEnable(GL_FRAMEBUFFER_sRGB);
2783 #endif
2784 }
2785 
2786 
2787 
LightingCallback(void * Data,size_t NumVerts,GLfloat * Normals,GLfloat * Positions,GLfloat * Colors)2788 void LightingCallback(void *Data, size_t NumVerts, GLfloat *Normals, GLfloat *Positions, GLfloat *Colors)
2789 {
2790 	LightingDataStruct *LPtr = (LightingDataStruct *)Data;
2791 
2792 	// In case of a semitransparent object
2793 	GLfloat Opacity = LPtr->Opacity;
2794 	bool Semitransparent = (Opacity < 1);
2795 	int NumCPlanes = Semitransparent ? 4 : 3;
2796 
2797 	// Whether to fade miner's light toward model sides
2798 	bool NoFade = false;
2799 
2800 	switch(LPtr->Type)
2801 	{
2802 	case OGL_MLight_Fast_NoFade:
2803 		NoFade = true;
2804 	case OGL_MLight_Fast:
2805 	{
2806 		GLfloat *el0 = LPtr->Colors[0], *el1 = LPtr->Colors[1], *el2 = LPtr->Colors[2];
2807 		for (size_t k=0; k<NumVerts; k++)
2808 		{
2809 			GLfloat N0 = *(Normals++);
2810 			GLfloat N1 = *(Normals++);
2811 			GLfloat N2 = *(Normals++);
2812 			*(Colors++) = el0[0]*N0 + el0[1]*N1 + el0[2]*N2 + el0[3];
2813 			*(Colors++) = el1[0]*N0 + el1[1]*N1 + el1[2]*N2 + el1[3];
2814 			*(Colors++) = el2[0]*N0 + el2[1]*N1 + el2[2]*N2 + el2[3];
2815 			if (Semitransparent)
2816 				*(Colors++) = Opacity;
2817 		}
2818 	}
2819 	break;
2820 
2821 	case OGL_MLight_Indiv_NoFade:
2822 	NoFade = true;
2823 	case OGL_MLight_Indiv:
2824 	{
2825 		for (size_t k=0; k<NumVerts; k++, Normals += 3, Positions +=3, Colors +=NumCPlanes)
2826 		{
2827 			GLfloat *Dir = LPtr->Dir;
2828 			GLfloat Depth = LPtr->ProjDistance + (Dir[0]*Positions[0] + Dir[1]*Positions[1]);
2829 			GLfloat Light = LPtr->AvgLight + LPtr->LightDiff*Normals[2];
2830 			if (NoFade)
2831 				FindShadingColor(Depth,int(FIXED_ONE*Light + 0.5),Colors);
2832 			else
2833 			{
2834 				GLfloat FrontColor[3], BackColor[3];
2835 				BackColor[0] = BackColor[1] = BackColor[2] = Light;
2836 				FindShadingColor(Depth,int(FIXED_ONE*Light + 0.5),FrontColor);
2837 				GLfloat NProd = -(Normals[0]*Dir[0] + Normals[1]*Dir[1] + Normals[2]*Dir[2]);
2838 				GLfloat BackMult = (1-NProd)/2;
2839 				GLfloat FrontMult = (1+NProd)/2;
2840 				for (int c=0; c<3; c++)
2841 					Colors[c] = BackMult*BackColor[c] + FrontMult*FrontColor[c];
2842 				if (Semitransparent)
2843 					Colors[3] = Opacity;
2844 			}
2845 		}
2846 	}
2847 	break;
2848 	}
2849 }
2850 
2851 
SetupShaders()2852 void SetupShaders()
2853 {
2854 	StandardShaders[0].Flags = ModelRenderer::Textured;
2855 	StandardShaders[0].TextureCallback = NormalShader;
2856 	StandardShaders[0].LightingCallback = LightingCallback;
2857 	StandardShaders[0].LightingCallbackData = &LightingData;
2858 
2859 	StandardShaders[1].Flags = ModelRenderer::Textured;
2860 	StandardShaders[1].TextureCallback = GlowingShader;
2861 
2862 	StaticModeShaders[0].Flags = ModelRenderer::Textured;
2863 	StaticModeShaders[0].TextureCallback = StaticModeShader;
2864 	StaticModeShaders[0].TextureCallbackData = SequenceNumbers + 0;
2865 
2866 	StaticModeShaders[1].Flags = ModelRenderer::Textured;
2867 	StaticModeShaders[1].TextureCallback = StaticModeShader;
2868 	StaticModeShaders[1].TextureCallbackData = SequenceNumbers + 1;
2869 
2870 	StaticModeShaders[2].Flags = ModelRenderer::Textured;
2871 	StaticModeShaders[2].TextureCallback = StaticModeShader;
2872 	StaticModeShaders[2].TextureCallbackData = SequenceNumbers + 2;
2873 
2874 	StaticModeShaders[3].Flags = ModelRenderer::Textured;
2875 	StaticModeShaders[3].TextureCallback = StaticModeShader;
2876 	StaticModeShaders[3].TextureCallbackData = SequenceNumbers + 3;
2877 }
2878 
2879 
2880 // Rendering crosshairs
OGL_RenderCrosshairs()2881 bool OGL_RenderCrosshairs()
2882 {
2883 	if (!OGL_IsActive()) return false;
2884 	if (use_lua_hud_crosshairs) return false;
2885 
2886 	// Crosshair features
2887 	CrosshairData& Crosshairs = GetCrosshairData();
2888 
2889 	// Proper projection
2890 	SetProjectionType(Projection_Screen);
2891 
2892 	// No textures painted here, but will blend
2893 	glDisable(GL_TEXTURE_2D);
2894 	glDisable(GL_ALPHA_TEST);
2895 	glEnable(GL_BLEND);
2896 
2897 	// What color; make 50% transparent (Alexander Strange's idea)
2898 	// Changed it to use the crosshairs data
2899 	if (!Crosshairs.PreCalced)
2900 	{
2901 	    Crosshairs.PreCalced = true;
2902 	    Crosshairs.GLColorsPreCalc[0] = Crosshairs.Color.red/65535.0F;
2903 	    Crosshairs.GLColorsPreCalc[1] = Crosshairs.Color.green/65535.0F;
2904 	    Crosshairs.GLColorsPreCalc[2] = Crosshairs.Color.blue/65535.0F;
2905 	    Crosshairs.GLColorsPreCalc[3] = Crosshairs.Opacity;
2906 	}
2907 	SglColor4fv(Crosshairs.GLColorsPreCalc);
2908 
2909 	// Create a new modelview matrix for the occasion
2910 	glMatrixMode(GL_MODELVIEW);
2911 	glPushMatrix();
2912 	glTranslated(ViewWidth / 2, ViewHeight / 2, 1);
2913 
2914 	// To keep pixels aligned, we have to draw on pixel boundaries.
2915 	// The SW renderer always offsets down and to the right when faced
2916 	// with an odd size. We're going to rotate our coordinate system,
2917 	// but we still have to obey the global offset rules.
2918 	//
2919 	// We precalculate the offsets for crosshair thickness below,
2920 	// for each of the four quadrants.
2921 	int halfWidthMin = -Crosshairs.Thickness / 2;
2922 	int halfWidthMax = halfWidthMin - (Crosshairs.Thickness % 2);
2923 	int offsets[4][2] = {   // [quadrant][local x/y]
2924 		{ halfWidthMin, halfWidthMin },
2925 		{ halfWidthMax, halfWidthMin },
2926 		{ halfWidthMax, halfWidthMax },
2927 		{ halfWidthMin, halfWidthMax } };
2928 
2929 	for (int quad = 0; quad < 4; quad++)
2930 	{
2931 		int WidthMin = offsets[quad][0];
2932 		int WidthMax = WidthMin + Crosshairs.Thickness;
2933 		int HeightMin = offsets[quad][1];
2934 		int HeightMax = HeightMin + Crosshairs.Thickness;
2935 
2936 		switch(Crosshairs.Shape)
2937 		{
2938 		case CHShape_RealCrosshairs:
2939 			{
2940 				// Four simple rectangles
2941 
2942 				int LenMin = Crosshairs.FromCenter;
2943 				int LenMax = LenMin + Crosshairs.Length;
2944 
2945 				// at the initial rotation, this is the rectangle at 3:00
2946 				OGL_RenderRect(LenMin, HeightMin, LenMax - LenMin, HeightMax - HeightMin);
2947 			}
2948 			break;
2949 		case CHShape_Circle:
2950 			{
2951 				// This will really be an octagon, for OpenGL-rendering convenience
2952 				//
2953 				// Each of the four sections is drawn with three quads --
2954 				// the middle diagonal section and two straight ends.
2955 				// Depending on the crosshair parameters, some segments
2956 				// have zero length: this happens when LenMid == LenMin.
2957 
2958 				int LenMax = Crosshairs.Length;
2959 				int LenMid = LenMax / 2;
2960 				int LenMin = std::min(LenMid, static_cast<int>(Crosshairs.FromCenter));
2961 
2962 				// at the initial rotation, this is the bottom right
2963 				GLint vertices[16] = {
2964 					LenMax + WidthMin, LenMin + HeightMin,
2965 					LenMax + WidthMax, LenMin + HeightMin,
2966 					LenMax + WidthMin, LenMid + HeightMin,
2967 					LenMax + WidthMax, LenMid + HeightMax,
2968 					LenMid + WidthMin, LenMax + HeightMin,
2969 					LenMid + WidthMax, LenMax + HeightMax,
2970 					LenMin + WidthMin, LenMax + HeightMin,
2971 					LenMin + WidthMin, LenMax + HeightMax
2972 				};
2973 				glVertexPointer(2, GL_INT, 0, vertices);
2974 				glDrawArrays(GL_TRIANGLE_STRIP, 0, 8);
2975 			}
2976 			break;
2977 		}
2978 		glRotated(-90.0, 0, 0, 1); // turn clockwise
2979 	}
2980 
2981 	// Done with that modelview matrix
2982 	glPopMatrix();
2983 
2984 	return true;
2985 }
2986 
2987 // Rendering text
OGL_RenderText(short BaseX,short BaseY,const char * Text,unsigned char r,unsigned char g,unsigned char b)2988 bool OGL_RenderText(short BaseX, short BaseY, const char *Text, unsigned char r, unsigned char g, unsigned char b)
2989 {
2990 	if (!OGL_IsActive()) return false;
2991 
2992 	// Create display list for the current text string;
2993 	// use the "standard" text-font display list (display lists can be nested)
2994 	GLuint TextDisplayList;
2995 	TextDisplayList = glGenLists(1);
2996 	glNewList(TextDisplayList,GL_COMPILE);
2997 	GetOnScreenFont().OGL_Render(Text);
2998 	glEndList();
2999 
3000 	// Place the text in the foreground of the display
3001 	SetProjectionType(Projection_Screen);
3002 	GLfloat Depth = 0;
3003 
3004 	// Using a modelview matrix, of course
3005 	glMatrixMode(GL_MODELVIEW);
3006 	glPushMatrix();
3007 
3008 	// Background
3009 	glColor3f(0,0,0);
3010 
3011 	// Changed to drop shadow only for performance reasons
3012 	/*
3013 	glLoadIdentity();
3014 	glTranslatef(BaseX-1,BaseY-1,Depth);
3015 	glCallList(TextDisplayList);
3016 
3017 	glLoadIdentity();
3018 	glTranslatef(BaseX,BaseY-1,Depth);
3019 	glCallList(TextDisplayList);
3020 
3021 	glLoadIdentity();
3022 	glTranslatef(BaseX+1,BaseY-1,Depth);
3023 	glCallList(TextDisplayList);
3024 
3025 	glLoadIdentity();
3026 	glTranslatef(BaseX-1,BaseY,Depth);
3027 	glCallList(TextDisplayList);
3028 
3029 	glLoadIdentity();
3030 	glTranslatef(BaseX+1,BaseY,Depth);
3031 	glCallList(TextDisplayList);
3032 
3033 	glLoadIdentity();
3034 	glTranslatef(BaseX-1,BaseY+1,Depth);
3035 	glCallList(TextDisplayList);
3036 
3037 	glLoadIdentity();
3038 	glTranslatef(BaseX,BaseY+1,Depth);
3039 	glCallList(TextDisplayList);
3040 	*/
3041 
3042 	glLoadIdentity();
3043 	glTranslatef(BaseX+1.0F,BaseY+1.0F,Depth);
3044 	glCallList(TextDisplayList);
3045 
3046 	// Foreground
3047 	SglColor3f(r/255.0f,g/255.0f,b/255.0f);
3048 
3049 	glLoadIdentity();
3050 	glTranslatef(BaseX,BaseY,Depth);
3051 	glCallList(TextDisplayList);
3052 
3053 	// Clean up
3054 	glDeleteLists(TextDisplayList,1);
3055 	glPopMatrix();
3056 
3057 	return true;
3058 }
3059 
OGL_RenderRect(float x,float y,float w,float h)3060 void OGL_RenderRect(float x, float y, float w, float h)
3061 {
3062 	glDisable(GL_TEXTURE_2D);
3063 	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
3064 
3065 	GLfloat vertices[8] = { x, y, x + w, y, x + w, y + h, x, y + h };
3066 	glVertexPointer(2, GL_FLOAT, 0, vertices);
3067 	glDrawArrays(GL_POLYGON, 0, 4);
3068 
3069 	glEnable(GL_TEXTURE_2D);
3070 	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
3071 }
3072 
OGL_RenderRect(const SDL_Rect & rect)3073 void OGL_RenderRect(const SDL_Rect& rect)
3074 {
3075 	OGL_RenderRect(rect.x, rect.y, rect.w, rect.h);
3076 }
3077 
OGL_RenderTexturedRect(float x,float y,float w,float h,float tleft,float ttop,float tright,float tbottom)3078 void OGL_RenderTexturedRect(float x, float y, float w, float h, float tleft, float ttop, float tright, float tbottom)
3079 {
3080 	GLfloat vertices[8] = { x, y, x + w, y, x + w, y + h, x, y + h };
3081 	GLfloat texcoords[8] = { tleft, ttop, tright, ttop, tright, tbottom, tleft, tbottom };
3082     glVertexPointer(2, GL_FLOAT, 0, vertices);
3083 	glTexCoordPointer(2, GL_FLOAT, 0, texcoords);
3084 	glDrawArrays(GL_POLYGON, 0, 4);
3085 }
3086 
OGL_RenderTexturedRect(const SDL_Rect & rect,float tleft,float ttop,float tright,float tbottom)3087 void OGL_RenderTexturedRect(const SDL_Rect& rect, float tleft, float ttop, float tright, float tbottom)
3088 {
3089 	OGL_RenderTexturedRect(rect.x, rect.y, rect.w, rect.h, tleft, ttop, tright, tbottom);
3090 }
3091 
OGL_RenderFrame(float x,float y,float w,float h,float t)3092 void OGL_RenderFrame(float x, float y, float w, float h, float t)
3093 {
3094 	glDisable(GL_TEXTURE_2D);
3095 	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
3096 
3097 	GLfloat vertices[20] = {
3098 		x,         y,
3099 		x + t,     y + t,
3100 		x,         y + h,
3101 		x + t,     y + h - t,
3102 		x + w,	   y + h,
3103 		x + w - t, y + h - t,
3104 		x + w,     y,
3105 		x + w - t, y + t,
3106 		x,		   y,
3107 		x + t,	   y + t
3108 	};
3109 	glVertexPointer(2, GL_FLOAT, 0, vertices);
3110 	glDrawArrays(GL_TRIANGLE_STRIP, 0, 10);
3111 
3112 	glEnable(GL_TEXTURE_2D);
3113 	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
3114 }
3115 
OGL_RenderLines(const std::vector<world_point2d> & points,float thickness)3116 void OGL_RenderLines(const std::vector<world_point2d>& points, float thickness)
3117 {
3118 	if (points.empty())
3119 		return;
3120 
3121 	glDisable(GL_TEXTURE_2D);
3122 	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
3123 
3124 	std::vector<GLfloat> coords;
3125 	for (size_t i = 1; i < points.size(); i += 2)
3126 	{
3127 		world_point2d prev = points[i - 1];
3128 		world_point2d cur = points[i];
3129 
3130 		float rise = cur.y - prev.y;
3131 		float run = cur.x - prev.x;
3132 		float length = sqrtf(rise*rise + run*run);
3133 
3134 		// Skip degenerate lines
3135 		if (length == 0)
3136 			continue;
3137 
3138 		float scale = thickness / length;
3139 		float xd = run * scale * 0.5f;
3140 		float yd = rise * scale * 0.5f;
3141 
3142 		coords.push_back(prev.x - yd);
3143 		coords.push_back(prev.y + xd);
3144 		coords.push_back(prev.x + yd);
3145 		coords.push_back(prev.y - xd);
3146 		coords.push_back(cur.x - yd);
3147 		coords.push_back(cur.y + xd);
3148 
3149 		coords.push_back(prev.x + yd);
3150 		coords.push_back(prev.y - xd);
3151 		coords.push_back(cur.x + yd);
3152 		coords.push_back(cur.y - xd);
3153 		coords.push_back(cur.x - yd);
3154 		coords.push_back(cur.y + xd);
3155 	}
3156 
3157 	if (!coords.empty())
3158 	{
3159 		glVertexPointer(2, GL_FLOAT, 0, &coords.front());
3160 		glDrawArrays(GL_TRIANGLES, 0, coords.size() / 2);
3161 	}
3162 
3163 	glEnable(GL_TEXTURE_2D);
3164 	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
3165 }
3166 
3167 // Render the console cursor
OGL_RenderTextCursor(const SDL_Rect & rect,unsigned char r,unsigned char g,unsigned char b)3168 bool OGL_RenderTextCursor(const SDL_Rect& rect, unsigned char r, unsigned char g, unsigned char b)
3169 {
3170 	if (!OGL_IsActive()) return false;
3171 
3172 	// Place the cursor in the foreground of the display
3173 	SetProjectionType(Projection_Screen);
3174 
3175 	SglColor3f(r/255.0f, g/255.0f, b/255.0f);
3176 	OGL_RenderRect(rect);
3177 
3178 	return true;
3179 }
3180 
3181 
3182 // Sets the infravision tinting color for a shapes collection, and whether to use such tinting;
3183 // the color values are from 0 to 1.
OGL_SetInfravisionTint(short Collection,bool IsTinted,float Red,float Green,float Blue)3184 bool OGL_SetInfravisionTint(short Collection, bool IsTinted, float Red, float Green, float Blue)
3185 {
3186 	// Can be called when OpenGL is inactive
3187 	if (!OGL_IsPresent()) return false;
3188 
3189 	// A way of defining some OGL_Textures stuff in OGL_Render.h
3190 	return SetInfravisionTint(Collection, IsTinted, Red, Green, Blue);
3191 }
3192 
3193 
3194 // Set the blend, being sure to remember the blend type set to
SetBlend(short _BlendType)3195 static void SetBlend(short _BlendType)
3196 {
3197 	// Don't need to do anything if no change
3198 	if (_BlendType == BlendType) return;
3199 
3200 	// Remember what's being set to
3201 	BlendType = _BlendType;
3202 
3203 	switch(BlendType)
3204 	{
3205 	// Blend-function args are incoming pixel, then background/previous pixel
3206 	case OGL_BlendType_Crossfade:
3207 		glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
3208 		break;
3209 
3210 	case OGL_BlendType_Add:
3211 		glBlendFunc(GL_SRC_ALPHA,GL_ONE);
3212 		break;
3213 	case OGL_BlendType_Crossfade_Premult:
3214 		glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
3215 		break;
3216 	case OGL_BlendType_Add_Premult:
3217 		glBlendFunc(GL_ONE, GL_ONE);
3218 		break;
3219 	}
3220 }
3221 
3222 #else
3223 
3224 // No OpenGL present
OGL_IsActive()3225 bool OGL_IsActive()
3226 {
3227 	return false;
3228 }
3229 
3230 #endif // def HAVE_OPENGL
3231