1 /**
2 * @file
3 * @brief Functions to generate and render spheres
4 */
5
6 /*
7 Copyright (C) 1997-2001 Id Software, Inc.
8
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
18 See the GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23
24 */
25
26 #include "r_local.h"
27 #include "r_sphere.h"
28 #include "r_error.h"
29 #include "r_geoscape.h"
30
31 static cvar_t* r_sphereDetails;
32
33 sphere_t r_globeEarth;
34 sphere_t r_globeMoon;
35 sphere_t r_globeEarthAtmosphere;
36
rhoSpiral(const int index,const float deltaRho,const float thetaAngle)37 static inline float rhoSpiral (const int index, const float deltaRho,const float thetaAngle)
38 {
39 const float rhoAngle = (index == 0) ? 0.0f : (float) ((index - 1) * deltaRho + thetaAngle * deltaRho / (2.0f * M_PI));
40 return rhoAngle > M_PI ? M_PI : rhoAngle;
41 }
42
43 /**
44 * @brief Initialize the globe chain arrays
45 * @param[out] sphere The sphere you want to create
46 * @param[in] tris The amount of tris the globe should have - the higher the number
47 * the higher the details. tris*tris triangles are obtained.
48 * @param[in] radius The radius of the sphere
49 * @sa R_Draw3DGlobe
50 */
R_SphereGenerate(sphere_t * sphere,const int tris,const float radius)51 void R_SphereGenerate (sphere_t* sphere, const int tris, const float radius)
52 {
53 const float drho = M_PI / tris; /**< angle from the pole (z-axis) */
54 /* must multiply pi by 2, rather than do integer division of tris by two,
55 * otherwise it goes wrong when tris is an odd number */
56 const float dtheta = 2.0f * M_PI / tris; /**< angle around the equator, from y-axis */
57
58 int i, j;
59
60 int vertspos = 0;
61 int texespos = 0;
62
63 sphere->glslProgram = nullptr;
64
65 sphere->verts = Mem_PoolAllocTypeN(float, (tris + 1) * (tris + 2) * 6, vid_genericPool);
66 sphere->texes = Mem_PoolAllocTypeN(float, (tris + 1) * (tris + 2) * 4, vid_genericPool);
67 sphere->normals = Mem_PoolAllocTypeN(float, (tris + 1) * (tris + 2) * 6, vid_genericPool);
68
69 /* must be i <= tris, as one loop is wasted, because of the spiral */
70 for (i = 0; i <= tris; i++) { /* loop through rho, from pole to pole */
71 /* must be j <= tris, so it meets up again */
72 for (j = 0; j <= tris ; j++) { /* loop through theta, around equator */
73 const float theta = j * dtheta;
74 const float stheta = (float) (-sin(theta));
75 const float ctheta = (float) (cos(theta));
76
77 /* second term in rho adds a spiral */
78 const float rho = rhoSpiral(i, drho, theta);
79 const float rhodrho = rhoSpiral(i + 1, drho, theta); /* rho plus drho, minding boundary conditions */
80 const float srho = (float) (sin(rho));
81 const float crho = (float) (cos(rho));
82 const float srhodrho = (float) (sin(rhodrho));
83 const float crhodrho = (float) (cos(rhodrho));
84
85 const float st = 1 - rho / M_PI;
86 const float stdt = 1 - rhodrho / M_PI;
87
88 const float s = theta / (4.0f * M_PI);
89
90 Vector2Set((&sphere->texes[texespos]), s, stdt);
91 texespos += 2;
92
93 VectorSet((&sphere->verts[vertspos]),
94 stheta * srhodrho * radius,
95 ctheta * srhodrho * radius,
96 crhodrho * radius);
97 VectorNormalize2((&sphere->verts[vertspos]), (&sphere->normals[vertspos]));
98 vertspos += 3;
99
100 Vector2Set((&sphere->texes[texespos]), s, st);
101 texespos += 2;
102
103 VectorSet((&sphere->verts[vertspos]),
104 stheta * srho * radius,
105 ctheta * srho * radius,
106 crho * radius);
107 VectorNormalize2((&sphere->verts[vertspos]), (&sphere->normals[vertspos]));
108 vertspos += 3;
109 }
110 }
111 sphere->num_tris = (tris + 1) * (tris + 2) * 2;
112 }
113
114 /**
115 * @brief Creates the spheres we need for rendering the 3d globe
116 * @note The moon sphere has less detail because it's smaller in the scene
117 * @note The sizes are arbitrary, becasue we use orthographic projection. The real
118 * sizes are: lunarRadius = 0.273 * earthRadius, solarRadius = 110.0 * earthRadius
119 * @sa R_Init
120 */
R_SphereInit(void)121 void R_SphereInit (void)
122 {
123 r_sphereDetails = Cvar_Get("r_sphereDetails", "1.0", CVAR_ARCHIVE, "Factor to increase or decrease the sphere tris");
124 if (r_sphereDetails->integer <= 0)
125 Cvar_SetValue("r_sphereDetails", 1.0);
126
127 R_SphereGenerate(&r_globeEarth, 60 * r_sphereDetails->value, EARTH_RADIUS);
128 R_SphereGenerate(&r_globeEarthAtmosphere, 60 * r_sphereDetails->value, EARTH_RADIUS * 1.03);
129 /* the earth has more details than the moon */
130 R_SphereGenerate(&r_globeMoon, 20 * r_sphereDetails->value, MOON_RADIUS);
131 }
132
R_SphereActivateTextureUnit(gltexunit_t * texunit,void * texCoordBuffer)133 static inline void R_SphereActivateTextureUnit (gltexunit_t* texunit, void* texCoordBuffer)
134 {
135 R_SelectTexture(texunit);
136 R_BindArray(GL_TEXTURE_COORD_ARRAY, GL_FLOAT, texCoordBuffer);
137 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
138 }
139
R_SphereDeactivateTextureUnit(gltexunit_t * texunit)140 static inline void R_SphereDeactivateTextureUnit (gltexunit_t* texunit)
141 {
142 R_SelectTexture(texunit);
143 R_BindDefaultArray(GL_TEXTURE_COORD_ARRAY);
144 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
145 }
146
R_SphereRenderTris(const sphere_t * sphere)147 static void R_SphereRenderTris (const sphere_t* sphere)
148 {
149 glEnable(GL_CULL_FACE);
150 glEnable(GL_NORMALIZE);
151
152 glDrawArrays(GL_TRIANGLE_STRIP, 0, sphere->num_tris);
153
154 refdef.batchCount++;
155
156 glDisable(GL_NORMALIZE);
157 glDisable(GL_CULL_FACE);
158 }
159
160 /**
161 * @param sphere The sphere to check
162 * @return @c true if all needed data is loaded to use the geoscape glsl shaders, @c false otherwise
163 */
R_SphereCheckGLSL(const sphere_t * sphere)164 static inline bool R_SphereCheckGLSL (const sphere_t* sphere)
165 {
166 return sphere->glslProgram && qglUseProgram && r_programs->integer;
167 }
168
169 /**
170 * @brief render sphere using standard OpenGL lighting
171 */
R_SphereShade(const sphere_t * sphere)172 static void R_SphereShade (const sphere_t* sphere)
173 {
174 if (sphere->overlay)
175 R_BindTexture(sphere->overlay->texnum);
176 else
177 R_BindTexture(sphere->texture->texnum);
178
179 if (sphere->overlayAlphaMask) {
180 R_EnableTexture(&texunit_lightmap, true);
181 R_SelectTexture(&texunit_lightmap);
182 R_BindArray(GL_TEXTURE_COORD_ARRAY, GL_FLOAT, sphere->texes);
183 R_BindLightmapTexture(sphere->overlayAlphaMask->texnum);
184 }
185
186 R_BindArray(GL_VERTEX_ARRAY, GL_FLOAT, sphere->verts);
187 R_BindArray(GL_TEXTURE_COORD_ARRAY, GL_FLOAT, sphere->texes);
188 R_BindArray(GL_NORMAL_ARRAY, GL_FLOAT, sphere->normals);
189
190 glEnableClientState(GL_NORMAL_ARRAY);
191
192 R_SphereRenderTris(sphere);
193
194 glDisableClientState(GL_NORMAL_ARRAY);
195
196 if (sphere->overlayAlphaMask)
197 R_EnableTexture(&texunit_lightmap, false);
198 }
199
200 /**
201 * @brief render sphere using GLSL (bump mapping, specularity, and season-blending)
202 */
R_SphereShadeGLSL(const sphere_t * sphere)203 static void R_SphereShadeGLSL (const sphere_t* sphere)
204 {
205 if (Vector4NotEmpty(sphere->nightLightPos))
206 glLightfv(GL_LIGHT1, GL_POSITION, sphere->nightLightPos);
207
208 /* configure openGL to use our shader program */
209 R_EnableLighting(sphere->glslProgram, true);
210
211 R_BindTexture(sphere->texture->texnum);
212 if (sphere->blendTexture)
213 R_BindTextureForTexUnit(sphere->blendTexture->texnum, &texunit_1);
214 if (sphere->normalMap)
215 R_BindTextureForTexUnit(sphere->normalMap->texnum, &texunit_2);
216
217 if (sphere->blendScale >= 0)
218 R_ProgramParameter1f("BLENDSCALE", sphere->blendScale);
219 if (sphere->glowScale >= 0)
220 R_ProgramParameter1f("GLOWSCALE", sphere->glowScale);
221
222 /* set up pointers */
223 R_SphereActivateTextureUnit(&texunit_1, sphere->texes);
224 R_SphereActivateTextureUnit(&texunit_2, sphere->texes);
225
226 R_SelectTexture(&texunit_diffuse);
227 R_BindArray(GL_VERTEX_ARRAY, GL_FLOAT, sphere->verts);
228 R_BindArray(GL_TEXTURE_COORD_ARRAY, GL_FLOAT, sphere->texes);
229 R_BindArray(GL_NORMAL_ARRAY, GL_FLOAT, sphere->normals);
230
231 R_SphereRenderTris(sphere);
232
233 R_SphereDeactivateTextureUnit(&texunit_1);
234 R_SphereDeactivateTextureUnit(&texunit_2);
235
236 /* deactivate the shader program */
237 R_EnableLighting(nullptr, false);
238 R_SelectTexture(&texunit_diffuse);
239 }
240
241 /**
242 * @brief Draw the sphere
243 * @param[in] sphere The sphere that should be rendered
244 * @param[in] pos The position (translation) of the matrix
245 * @param[in] rotate The rotation of the matrix
246 * @param[in] scale The scale of the matrix
247 * @param[in] lightPos Set this to nullptr if you don't want to change the light position
248 */
R_SphereRender(const sphere_t * sphere,const vec3_t pos,const vec3_t rotate,const float scale,const vec4_t lightPos)249 void R_SphereRender (const sphere_t* sphere, const vec3_t pos, const vec3_t rotate, const float scale, const vec4_t lightPos)
250 {
251 /* go to a new matrix */
252 glPushMatrix();
253
254 glMatrixMode(GL_MODELVIEW);
255 glTranslatef(pos[0], pos[1], pos[2]);
256
257 /* flatten the sphere */
258 glScalef(scale * viddef.rx, scale * viddef.ry, scale);
259 R_CheckError();
260
261 /* rotate the globe as given in ccs.angles */
262 glRotatef(rotate[YAW], 1, 0, 0);
263 glRotatef(rotate[ROLL], 0, 1, 0);
264 glRotatef(rotate[PITCH], 0, 0, 1);
265
266 if (lightPos && VectorNotEmpty(lightPos))
267 glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
268
269 R_CheckError();
270
271 if (!sphere->overlay && R_SphereCheckGLSL(sphere))
272 R_SphereShadeGLSL(sphere); /* render globe with bump mapping, specularity, etc. */
273 else
274 R_SphereShade(sphere); /* otherwise, use basic OpenGL rendering */
275
276 /* cleanup common to both GLSL and normal rendering */
277 R_CheckError();
278
279 /* restore the previous matrix */
280 glPopMatrix();
281
282 refdef.aliasCount += sphere->num_tris * sphere->num_tris;
283
284 R_BindDefaultArray(GL_VERTEX_ARRAY);
285 R_BindDefaultArray(GL_TEXTURE_COORD_ARRAY);
286 R_BindDefaultArray(GL_NORMAL_ARRAY);
287 }
288