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