1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 
13 See the GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19 
20 //
21 // rf_sky.c
22 // Sky clipping and rendering
23 //
24 
25 #include "rf_local.h"
26 
27 typedef struct skyState_s {
28 	qBool			loaded;
29 
30 	char			baseName[MAX_QPATH];
31 	float			rotation;
32 	vec3_t			axis;
33 
34 	mesh_t			meshes[6];
35 	shader_t		*shaders[6];
36 
37 	vec2_t			coords[6][4];
38 	vec3_t			verts[6][4];
39 } skyState_t;
40 
41 static skyState_t		r_skyState;
42 
43 /*
44 =============================================================================
45 
46 	SKY
47 
48 =============================================================================
49 */
50 
51 static const float	r_skyClip[6][3] = {
52 	{1, 1, 0},		{1, -1, 0},		{0, -1, 1},		{0, 1, 1},		{1, 0, 1},		{-1, 0, 1}
53 };
54 static const int	r_skySTToVec[6][3] = {
55 	{3, -1, 2},		{-3, 1, 2},		{1, 3, 2},		{-1, -3, 2},	{-2, -1, 3},	{2, -1, -3}
56 };
57 static const int	r_skyVecToST[6][3] = {
58 	{-2, 3, 1},		{2, 3, -1},		{1, 3, 2},		{-1, 3, -2},	{-2, -1, 3},	{-2, 1, -3}
59 };
60 static const int	r_skyTexOrder[6] = {0, 2, 1, 3, 4, 5};
61 
62 /*
63 =================
64 R_ClipSkySurface
65 =================
66 */
ClipSkyPolygon(int nump,vec3_t vecs,int stage)67 static void ClipSkyPolygon (int nump, vec3_t vecs, int stage)
68 {
69 	const float	*norm;
70 	float	*v, d, e;
71 	float	dists[SKY_MAXCLIPVERTS];
72 	int		sides[SKY_MAXCLIPVERTS];
73 	int		newc[2], i, j;
74 	vec3_t	newv[2][SKY_MAXCLIPVERTS];
75 	qBool	front, back;
76 
77 	if (nump > SKY_MAXCLIPVERTS-2)
78 		Com_Error (ERR_DROP, "ClipSkyPolygon: SKY_MAXCLIPVERTS");
79 
80 	if (stage == 6) {
81 		// Fully clipped, so draw it
82 		int		i, j;
83 		vec3_t	v, av;
84 		float	s, t, dv;
85 		int		axis;
86 		float	*vp;
87 
88 		// Decide which face it maps to
89 		Vec3Clear (v);
90 		for (i=0, vp=vecs ; i<nump ; i++, vp+=3)
91 			Vec3Add (vp, v, v);
92 
93 		Vec3Set (av, (float)fabs (v[0]), (float)fabs (v[1]), (float)fabs (v[2]));
94 
95 		if (av[0] > av[1] && av[0] > av[2])
96 			axis = (v[0] < 0) ? 1 : 0;
97 		else if (av[1] > av[2] && av[1] > av[0])
98 			axis = (v[1] < 0) ? 3 : 2;
99 		else
100 			axis = (v[2] < 0) ? 5 : 4;
101 
102 		// Project new texture coords
103 		for (i=0 ; i<nump ; i++, vecs+=3) {
104 			j = r_skyVecToST[axis][2];
105 			dv = (j > 0) ? vecs[j - 1] : -vecs[-j - 1];
106 
107 			if (dv < 0.001)
108 				continue;	// Don't divide by zero
109 
110 			dv = 1.0f / dv;
111 
112 			j = r_skyVecToST[axis][0];
113 			s = (j < 0) ? -vecs[-j -1] * dv : vecs[j-1] * dv;
114 
115 			j = r_skyVecToST[axis][1];
116 			t = (j < 0) ? -vecs[-j -1] * dv : vecs[j-1] * dv;
117 
118 			if (s < r_currentList->skyMins[axis][0])
119 				r_currentList->skyMins[axis][0] = s;
120 			if (t < r_currentList->skyMins[axis][1])
121 				r_currentList->skyMins[axis][1] = t;
122 
123 			if (s > r_currentList->skyMaxs[axis][0])
124 				r_currentList->skyMaxs[axis][0] = s;
125 			if (t > r_currentList->skyMaxs[axis][1])
126 				r_currentList->skyMaxs[axis][1] = t;
127 		}
128 
129 		return;
130 	}
131 
132 	front = back = qFalse;
133 	norm = r_skyClip[stage];
134 	for (i=0, v=vecs ; i<nump ; i++, v+=3) {
135 		d = DotProduct (v, norm);
136 		if (d > LARGE_EPSILON) {
137 			front = qTrue;
138 			sides[i] = SIDE_FRONT;
139 		}
140 		else if (d < -LARGE_EPSILON) {
141 			back = qTrue;
142 			sides[i] = SIDE_BACK;
143 		}
144 		else
145 			sides[i] = SIDE_ON;
146 		dists[i] = d;
147 	}
148 
149 	if (!front || !back) {
150 		// Not clipped
151 		ClipSkyPolygon (nump, vecs, stage+1);
152 		return;
153 	}
154 
155 	// Clip it
156 	sides[i] = sides[0];
157 	dists[i] = dists[0];
158 	Vec3Copy (vecs, (vecs+(i*3)));
159 	newc[0] = newc[1] = 0;
160 
161 	for (i=0, v=vecs ; i<nump ; i++, v+=3) {
162 		switch (sides[i]) {
163 		case SIDE_FRONT:
164 			Vec3Copy (v, newv[0][newc[0]]);
165 			newc[0]++;
166 			break;
167 
168 		case SIDE_BACK:
169 			Vec3Copy (v, newv[1][newc[1]]);
170 			newc[1]++;
171 			break;
172 
173 		case SIDE_ON:
174 			Vec3Copy (v, newv[0][newc[0]]);
175 			newc[0]++;
176 			Vec3Copy (v, newv[1][newc[1]]);
177 			newc[1]++;
178 			break;
179 		}
180 
181 		if (sides[i] == SIDE_ON
182 		|| sides[i+1] == SIDE_ON
183 		|| sides[i+1] == sides[i])
184 			continue;
185 
186 		d = dists[i] / (dists[i] - dists[i+1]);
187 		for (j=0 ; j<3 ; j++) {
188 			e = v[j] + d * (v[j+3] - v[j]);
189 			newv[0][newc[0]][j] = e;
190 			newv[1][newc[1]][j] = e;
191 		}
192 		newc[0]++;
193 		newc[1]++;
194 	}
195 
196 	// Continue
197 	ClipSkyPolygon (newc[0], newv[0][0], stage+1);
198 	ClipSkyPolygon (newc[1], newv[1][0], stage+1);
199 }
R_ClipSkySurface(mBspSurface_t * surf)200 void R_ClipSkySurface (mBspSurface_t *surf)
201 {
202 	vec3_t	*vert;
203 	vec3_t	verts[4];
204 	int		*index, i;
205 
206 	// Don't draw twice
207 	surf->visFrame = ri.frameCount;
208 
209 	// Calculate vertex values for sky box
210 	vert = surf->mesh->vertexArray;
211 	index = surf->mesh->indexArray;
212 	for (i=0 ; i<surf->mesh->numIndexes ; i+=3, index+=3) {
213 		Vec3Subtract (vert[index[0]], ri.def.viewOrigin, verts[0]);
214 		Vec3Subtract (vert[index[1]], ri.def.viewOrigin, verts[1]);
215 		Vec3Subtract (vert[index[2]], ri.def.viewOrigin, verts[2]);
216 
217 		ClipSkyPolygon (3, verts[0], 0);
218 	}
219 }
220 
221 
222 /*
223 ==============
224 R_AddSkyToList
225 ==============
226 */
R_AddSkyToList(void)227 void R_AddSkyToList (void)
228 {
229 	if (!r_skyState.loaded)
230 		return;
231 
232 	// FIXME
233 	R_AddMeshToList (r_skyState.shaders[r_skyTexOrder[0]], 0, NULL, NULL, MBT_SKY, r_skyState.verts);
234 }
235 
236 
237 /*
238 ==============
239 R_ClearSky
240 ==============
241 */
R_ClearSky(void)242 void R_ClearSky (void)
243 {
244 	int		i;
245 
246 	if (!r_skyState.loaded)
247 		return;
248 
249 	for (i=0 ; i<6 ; i++)
250 		Clear2DBounds (r_currentList->skyMins[i], r_currentList->skyMaxs[i]);
251 }
252 
253 
254 /*
255 ==============
256 R_DrawSky
257 ==============
258 */
R_StoreSkyVerts(int side,int vertNum,float s,float t)259 static void R_StoreSkyVerts (int side, int vertNum, float s, float t)
260 {
261 	vec3_t		v, b;
262 	int			j, k;
263 
264 	// Coords
265 	r_skyState.coords[side][vertNum][0] = clamp ((s + 1) * 0.5f, 0, 1);
266 	r_skyState.coords[side][vertNum][1] = 1.0f - clamp ((t + 1) * 0.5f, 0, 1);
267 
268 	// Verts
269 	Vec3Set (b, s * SKY_BOXSIZE, t * SKY_BOXSIZE, SKY_BOXSIZE);
270 
271 	for (j=0 ; j<3 ; j++) {
272 		k = r_skySTToVec[side][j];
273 		if (k < 0)
274 			v[j] = -b[-k - 1];
275 		else
276 			v[j] = b[k - 1];
277 	}
278 
279 	r_skyState.verts[side][vertNum][0] = v[0] + ri.def.viewOrigin[0];
280 	r_skyState.verts[side][vertNum][1] = v[1] + ri.def.viewOrigin[1];
281 	r_skyState.verts[side][vertNum][2] = v[2] + ri.def.viewOrigin[2];
282 }
R_DrawSky(meshBuffer_t * mb)283 void R_DrawSky (meshBuffer_t *mb)
284 {
285 	int			i;
286 
287 	if (r_skyState.rotation) {
288 		// Check for sky visibility
289 		for (i=0 ; i<6 ; i++) {
290 			if (r_currentList->skyMins[i][0] < r_currentList->skyMaxs[i][0]
291 			&& r_currentList->skyMins[i][1] < r_currentList->skyMaxs[i][1])
292 				break;
293 		}
294 		if (i == 6)
295 			return;	// Nothing visible
296 
297 		// Rotation matrix
298 		qglPushMatrix ();
299 		qglRotatef (ri.def.time * r_skyState.rotation, r_skyState.axis[0], r_skyState.axis[1], r_skyState.axis[2]);
300 	}
301 
302 	for (i=0 ; i<6 ; i++) {
303 		if (r_skyState.rotation) {
304 			// Hack, forces full sky to draw when rotating
305 			r_currentList->skyMins[i][0] = -1;
306 			r_currentList->skyMins[i][1] = -1;
307 			r_currentList->skyMaxs[i][0] = 1;
308 			r_currentList->skyMaxs[i][1] = 1;
309 		}
310 		else {
311 			if (r_currentList->skyMins[i][0] >= r_currentList->skyMaxs[i][0]
312 			|| r_currentList->skyMins[i][1] >= r_currentList->skyMaxs[i][1])
313 				continue;
314 		}
315 
316 		// Push and render
317 		R_StoreSkyVerts (i, 0, r_currentList->skyMins[i][0], r_currentList->skyMins[i][1]);
318 		R_StoreSkyVerts (i, 1, r_currentList->skyMins[i][0], r_currentList->skyMaxs[i][1]);
319 		R_StoreSkyVerts (i, 2, r_currentList->skyMaxs[i][0], r_currentList->skyMaxs[i][1]);
320 		R_StoreSkyVerts (i, 3, r_currentList->skyMaxs[i][0], r_currentList->skyMins[i][1]);
321 
322 		mb->shader = r_skyState.shaders[r_skyTexOrder[i]];
323 		RB_PushMesh (&r_skyState.meshes[i], MF_NONBATCHED|MF_TRIFAN|mb->shader->features);
324 		RB_RenderMeshBuffer (mb, qFalse);
325 	}
326 
327 	if (r_skyState.rotation)
328 		qglPopMatrix ();
329 }
330 
331 
332 /*
333 =================
334 R_CheckLoadSky
335 
336 Returns qTrue if there are ANY sky surfaces in the map, called on map load
337 =================
338 */
R_CheckLoadSky(mBspNode_t * node)339 static qBool R_CheckLoadSky (mBspNode_t *node)
340 {
341 	mBspSurface_t	*surf, **mark;
342 	mBspLeaf_t		*leaf;
343 	int				i;
344 
345 	if (node->c.q2_contents == CONTENTS_SOLID)
346 		return qFalse;		// Solid
347 
348 	// Recurse down the children
349 	if (node->c.q2_contents == -1)
350 		return R_CheckLoadSky (node->children[0]) || R_CheckLoadSky (node->children[1]);
351 
352 	// If this is a leaf node, draw it
353 	leaf = (mBspLeaf_t *)node;
354 	if (!leaf->q2_numMarkSurfaces)
355 		return qFalse;
356 
357 	// Search
358 	for (i=0, mark=leaf->q2_firstMarkSurface ; i<leaf->q2_numMarkSurfaces ; i++, mark++) {
359 		surf = *mark;
360 		if (surf->q2_texInfo->flags & SURF_TEXINFO_SKY)
361 			return qTrue;
362 	}
363 
364 	return qFalse;
365 }
366 
367 
368 /*
369 ============
370 R_SetSky
371 ============
372 */
R_SetSky(char * name,float rotate,vec3_t axis)373 void R_SetSky (char *name, float rotate, vec3_t axis)
374 {
375 	char	pathName[MAX_QPATH];
376 	int		i;
377 
378 	if (ri.scn.worldModel->type == MODEL_Q3BSP) {
379 		r_skyState.loaded = qTrue;
380 	}
381 	else {
382 		r_skyState.loaded = R_CheckLoadSky (ri.scn.worldModel->bspModel.nodes);
383 		if (!r_skyState.loaded)
384 			return;
385 	}
386 
387 	Q_strncpyz (r_skyState.baseName, name, sizeof (r_skyState.baseName));
388 	r_skyState.rotation = rotate;
389 	Vec3Copy (axis, r_skyState.axis);
390 
391 	for (i=0 ; i<6 ; i++) {
392 		Q_snprintfz (pathName, sizeof (pathName), "env/%s%s.tga", r_skyState.baseName, r_skyNameSuffix[i]);
393 		r_skyState.shaders[i] = R_RegisterSky (pathName);
394 
395 		if (!r_skyState.shaders[i])
396 			r_skyState.shaders[i] = r_noShaderSky;
397 	}
398 }
399 
400 /*
401 =============================================================================
402 
403 	CONSOLE COMMANDS
404 
405 =============================================================================
406 */
407 
408 /*
409 =================
410 R_SetSky_f
411 
412 Set a specific sky and rotation speed
413 =================
414 */
R_SetSky_f(void)415 static void R_SetSky_f (void)
416 {
417 	float	rotate;
418 	vec3_t	axis;
419 
420 	if (!r_skyState.loaded) {
421 		Com_Printf (0, "No sky surfaces!\n");
422 		return;
423 	}
424 
425 	if (Cmd_Argc () < 2) {
426 		Com_Printf (0, "Usage: sky <basename> <rotate> [axis x y z]\n");
427 		Com_Printf (0, "Currently: sky <%s> <%.1f> [%.1f %.1f %.1f]\n", r_skyState.baseName, r_skyState.rotation, r_skyState.axis[0], r_skyState.axis[1], r_skyState.axis[2]);
428 		return;
429 	}
430 
431 	if (Cmd_Argc () > 2)
432 		rotate = (float)atof (Cmd_Argv (2));
433 	else
434 		rotate = 0;
435 
436 	if (Cmd_Argc () == 6)
437 		Vec3Set (axis, (float)atof (Cmd_Argv (3)), (float)atof (Cmd_Argv (4)), (float)atof (Cmd_Argv (5)));
438 	else
439 		Vec3Set (axis, 0, 0, 1);
440 
441 	R_SetSky (Cmd_Argv (1), rotate, axis);
442 }
443 
444 /*
445 =============================================================================
446 
447 	INIT / SHUTDOWN
448 
449 =============================================================================
450 */
451 
452 static void	*cmd_sky;
453 
454 /*
455 ==================
456 R_SkyInit
457 ==================
458 */
R_SkyInit(void)459 void R_SkyInit (void)
460 {
461 	int		i;
462 
463 	// Commands
464 	cmd_sky = Cmd_AddCommand ("sky",		R_SetSky_f,		"Changes the sky env basename");
465 
466 	// Init sky meshes
467 	for (i=0 ; i<6 ; i++) {
468 		r_skyState.meshes[i].numIndexes = 0;
469 		r_skyState.meshes[i].numVerts = 4;
470 
471 		r_skyState.meshes[i].colorArray = NULL;
472 		r_skyState.meshes[i].coordArray = r_skyState.coords[i];
473 		r_skyState.meshes[i].indexArray = NULL;
474 		r_skyState.meshes[i].lmCoordArray = NULL;
475 		r_skyState.meshes[i].normalsArray = NULL;
476 		r_skyState.meshes[i].sVectorsArray = NULL;
477 		r_skyState.meshes[i].tVectorsArray = NULL;
478 		r_skyState.meshes[i].trNeighborsArray = NULL;
479 		r_skyState.meshes[i].trNormalsArray = NULL;
480 		r_skyState.meshes[i].vertexArray = r_skyState.verts[i];
481 	}
482 }
483 
484 
485 /*
486 ==================
487 R_SkyShutdown
488 ==================
489 */
R_SkyShutdown(void)490 void R_SkyShutdown (void)
491 {
492 	// Remove commands
493 	Cmd_RemoveCommand ("sky", cmd_sky);
494 }
495