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