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