1 /*
2 ===========================================================================
3
4 Return to Castle Wolfenstein multiplayer GPL Source Code
5 Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
6
7 This file is part of the Return to Castle Wolfenstein multiplayer GPL Source Code (RTCW MP Source Code).
8
9 RTCW MP Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 RTCW MP Source Code 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. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with RTCW MP Source Code. If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the RTCW MP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW MP Source Code. If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29 // tr_sky.c
30 #include "tr_local.h"
31
32 #define SKY_SUBDIVISIONS 8
33 #define HALF_SKY_SUBDIVISIONS ( SKY_SUBDIVISIONS / 2 )
34
35 static float s_cloudTexCoords[6][SKY_SUBDIVISIONS + 1][SKY_SUBDIVISIONS + 1][2];
36 static float s_cloudTexP[6][SKY_SUBDIVISIONS + 1][SKY_SUBDIVISIONS + 1];
37
38 /*
39 ===================================================================================
40
41 POLYGON TO BOX SIDE PROJECTION
42
43 ===================================================================================
44 */
45
46 static vec3_t sky_clip[6] =
47 {
48 {1,1,0},
49 {1,-1,0},
50 {0,-1,1},
51 {0,1,1},
52 {1,0,1},
53 {-1,0,1}
54 };
55
56 static float sky_mins[2][6], sky_maxs[2][6];
57 static float sky_min, sky_max;
58
59 /*
60 ================
61 AddSkyPolygon
62 ================
63 */
AddSkyPolygon(int nump,vec3_t vecs)64 static void AddSkyPolygon( int nump, vec3_t vecs ) {
65 int i,j;
66 vec3_t v, av;
67 float s, t, dv;
68 int axis;
69 float *vp;
70 // s = [0]/[2], t = [1]/[2]
71 static int vec_to_st[6][3] =
72 {
73 {-2,3,1},
74 {2,3,-1},
75
76 {1,3,2},
77 {-1,3,-2},
78
79 {-2,-1,3},
80 {-2,1,-3}
81
82 // {-1,2,3},
83 // {1,2,-3}
84 };
85
86 // decide which face it maps to
87 VectorCopy( vec3_origin, v );
88 for ( i = 0, vp = vecs ; i < nump ; i++, vp += 3 )
89 {
90 VectorAdd( vp, v, v );
91 }
92 av[0] = fabs( v[0] );
93 av[1] = fabs( v[1] );
94 av[2] = fabs( v[2] );
95 if ( av[0] > av[1] && av[0] > av[2] ) {
96 if ( v[0] < 0 ) {
97 axis = 1;
98 } else {
99 axis = 0;
100 }
101 } else if ( av[1] > av[2] && av[1] > av[0] ) {
102 if ( v[1] < 0 ) {
103 axis = 3;
104 } else {
105 axis = 2;
106 }
107 } else
108 {
109 if ( v[2] < 0 ) {
110 axis = 5;
111 } else {
112 axis = 4;
113 }
114 }
115
116 // project new texture coords
117 for ( i = 0 ; i < nump ; i++, vecs += 3 )
118 {
119 j = vec_to_st[axis][2];
120 if ( j > 0 ) {
121 dv = vecs[j - 1];
122 } else {
123 dv = -vecs[-j - 1];
124 }
125 if ( dv < 0.001 ) {
126 continue; // don't divide by zero
127 }
128 j = vec_to_st[axis][0];
129 if ( j < 0 ) {
130 s = -vecs[-j - 1] / dv;
131 } else {
132 s = vecs[j - 1] / dv;
133 }
134 j = vec_to_st[axis][1];
135 if ( j < 0 ) {
136 t = -vecs[-j - 1] / dv;
137 } else {
138 t = vecs[j - 1] / dv;
139 }
140
141 if ( s < sky_mins[0][axis] ) {
142 sky_mins[0][axis] = s;
143 }
144 if ( t < sky_mins[1][axis] ) {
145 sky_mins[1][axis] = t;
146 }
147 if ( s > sky_maxs[0][axis] ) {
148 sky_maxs[0][axis] = s;
149 }
150 if ( t > sky_maxs[1][axis] ) {
151 sky_maxs[1][axis] = t;
152 }
153 }
154 }
155
156 #define ON_EPSILON 0.1f // point on plane side epsilon
157 #define MAX_CLIP_VERTS 64
158 /*
159 ================
160 ClipSkyPolygon
161 ================
162 */
ClipSkyPolygon(int nump,vec3_t vecs,int stage)163 static void ClipSkyPolygon( int nump, vec3_t vecs, int stage ) {
164 float *norm;
165 float *v;
166 qboolean front, back;
167 float d, e;
168 float dists[MAX_CLIP_VERTS];
169 int sides[MAX_CLIP_VERTS];
170 vec3_t newv[2][MAX_CLIP_VERTS];
171 int newc[2];
172 int i, j;
173
174 if ( nump > MAX_CLIP_VERTS - 2 ) {
175 ri.Error( ERR_DROP, "ClipSkyPolygon: MAX_CLIP_VERTS" );
176 }
177 if ( stage == 6 ) { // fully clipped, so draw it
178 AddSkyPolygon( nump, vecs );
179 return;
180 }
181
182 front = back = qfalse;
183 norm = sky_clip[stage];
184 for ( i = 0, v = vecs ; i < nump ; i++, v += 3 )
185 {
186 d = DotProduct( v, norm );
187 if ( d > ON_EPSILON ) {
188 front = qtrue;
189 sides[i] = SIDE_FRONT;
190 } else if ( d < -ON_EPSILON ) {
191 back = qtrue;
192 sides[i] = SIDE_BACK;
193 } else {
194 sides[i] = SIDE_ON;
195 }
196 dists[i] = d;
197 }
198
199 if ( !front || !back ) { // not clipped
200 ClipSkyPolygon( nump, vecs, stage + 1 );
201 return;
202 }
203
204 // clip it
205 sides[i] = sides[0];
206 dists[i] = dists[0];
207 VectorCopy( vecs, ( vecs + ( i * 3 ) ) );
208 newc[0] = newc[1] = 0;
209
210 for ( i = 0, v = vecs ; i < nump ; i++, v += 3 )
211 {
212 switch ( sides[i] )
213 {
214 case SIDE_FRONT:
215 VectorCopy( v, newv[0][newc[0]] );
216 newc[0]++;
217 break;
218 case SIDE_BACK:
219 VectorCopy( v, newv[1][newc[1]] );
220 newc[1]++;
221 break;
222 case SIDE_ON:
223 VectorCopy( v, newv[0][newc[0]] );
224 newc[0]++;
225 VectorCopy( v, newv[1][newc[1]] );
226 newc[1]++;
227 break;
228 }
229
230 if ( sides[i] == SIDE_ON || sides[i + 1] == SIDE_ON || sides[i + 1] == sides[i] ) {
231 continue;
232 }
233
234 d = dists[i] / ( dists[i] - dists[i + 1] );
235 for ( j = 0 ; j < 3 ; j++ )
236 {
237 e = v[j] + d * ( v[j + 3] - v[j] );
238 newv[0][newc[0]][j] = e;
239 newv[1][newc[1]][j] = e;
240 }
241 newc[0]++;
242 newc[1]++;
243 }
244
245 // continue
246 ClipSkyPolygon( newc[0], newv[0][0], stage + 1 );
247 ClipSkyPolygon( newc[1], newv[1][0], stage + 1 );
248 }
249
250 /*
251 ==============
252 ClearSkyBox
253 ==============
254 */
ClearSkyBox(void)255 static void ClearSkyBox( void ) {
256 int i;
257
258 for ( i = 0 ; i < 6 ; i++ ) {
259 sky_mins[0][i] = sky_mins[1][i] = 9999;
260 sky_maxs[0][i] = sky_maxs[1][i] = -9999;
261 }
262 }
263
264 /*
265 ================
266 RB_ClipSkyPolygons
267 ================
268 */
RB_ClipSkyPolygons(shaderCommands_t * input)269 void RB_ClipSkyPolygons( shaderCommands_t *input ) {
270 vec3_t p[5]; // need one extra point for clipping
271 int i, j;
272
273 ClearSkyBox();
274
275 for ( i = 0; i < input->numIndexes; i += 3 )
276 {
277 for ( j = 0 ; j < 3 ; j++ )
278 {
279 VectorSubtract( input->xyz[input->indexes[i + j]],
280 backEnd.viewParms.or.origin,
281 p[j] );
282 }
283 ClipSkyPolygon( 3, p[0], 0 );
284 }
285 }
286
287 /*
288 ===================================================================================
289
290 CLOUD VERTEX GENERATION
291
292 ===================================================================================
293 */
294
295 /*
296 ** MakeSkyVec
297 **
298 ** Parms: s, t range from -1 to 1
299 */
MakeSkyVec(float s,float t,int axis,float outSt[2],vec3_t outXYZ)300 static void MakeSkyVec( float s, float t, int axis, float outSt[2], vec3_t outXYZ ) {
301 // 1 = s, 2 = t, 3 = 2048
302 static int st_to_vec[6][3] =
303 {
304 {3,-1,2},
305 {-3,1,2},
306
307 {1,3,2},
308 {-1,-3,2},
309
310 {-2,-1,3}, // 0 degrees yaw, look straight up
311 {2,-1,-3} // look straight down
312 };
313
314 vec3_t b;
315 int j, k;
316 float boxSize;
317
318 // JPW NERVE swiped from Sherman SP fix
319 // if(glfogNum > FOG_NONE && glfogsettings[FOG_CURRENT].mode == GL_EXP) {
320 if ( glfogsettings[FOG_SKY].registered ) { // (SA) trying this...
321 /// boxSize = backEnd.viewParms.zFar / 1.75; // div sqrt(3)
322 // boxSize = glfogsettings[FOG_CURRENT].end / 1.75;
323 boxSize = glfogsettings[FOG_SKY].end; // (SA) trying this...
324 // jpw
325 } else {
326 boxSize = backEnd.viewParms.zFar / 1.75; // div sqrt(3)
327
328 }
329 // JPW NERVE swiped from Sherman
330 // make sure the sky is not near clipped
331 if ( boxSize < r_znear->value * 2.0 ) {
332 boxSize = r_znear->value * 2.0;
333 }
334 // jpw
335 b[0] = s * boxSize;
336 b[1] = t * boxSize;
337 b[2] = boxSize;
338
339 for ( j = 0 ; j < 3 ; j++ )
340 {
341 k = st_to_vec[axis][j];
342 if ( k < 0 ) {
343 outXYZ[j] = -b[-k - 1];
344 } else
345 {
346 outXYZ[j] = b[k - 1];
347 }
348 }
349
350 // avoid bilerp seam
351 s = ( s + 1 ) * 0.5;
352 t = ( t + 1 ) * 0.5;
353 if ( s < sky_min ) {
354 s = sky_min;
355 } else if ( s > sky_max ) {
356 s = sky_max;
357 }
358
359 if ( t < sky_min ) {
360 t = sky_min;
361 } else if ( t > sky_max ) {
362 t = sky_max;
363 }
364
365 t = 1.0 - t;
366
367
368 if ( outSt ) {
369 outSt[0] = s;
370 outSt[1] = t;
371 }
372 }
373
374 static int sky_texorder[6] = {0,2,1,3,4,5};
375 static vec3_t s_skyPoints[SKY_SUBDIVISIONS + 1][SKY_SUBDIVISIONS + 1];
376 static float s_skyTexCoords[SKY_SUBDIVISIONS + 1][SKY_SUBDIVISIONS + 1][2];
377
DrawSkySide(struct image_s * image,const int mins[2],const int maxs[2])378 static void DrawSkySide( struct image_s *image, const int mins[2], const int maxs[2] )
379 {
380 int s, t;
381 int firstVertex = tess.numVertexes;
382 //int firstIndex = tess.numIndexes;
383 vec4_t color;
384
385 //tess.numVertexes = 0;
386 //tess.numIndexes = 0;
387 tess.firstIndex = tess.numIndexes;
388
389 GL_BindToTMU( image, TB_COLORMAP );
390 GL_Cull( CT_TWO_SIDED );
391
392 for ( t = mins[1]+HALF_SKY_SUBDIVISIONS; t <= maxs[1]+HALF_SKY_SUBDIVISIONS; t++ )
393 {
394 for ( s = mins[0]+HALF_SKY_SUBDIVISIONS; s <= maxs[0]+HALF_SKY_SUBDIVISIONS; s++ )
395 {
396 tess.xyz[tess.numVertexes][0] = s_skyPoints[t][s][0];
397 tess.xyz[tess.numVertexes][1] = s_skyPoints[t][s][1];
398 tess.xyz[tess.numVertexes][2] = s_skyPoints[t][s][2];
399 tess.xyz[tess.numVertexes][3] = 1.0;
400
401 tess.texCoords[tess.numVertexes][0] = s_skyTexCoords[t][s][0];
402 tess.texCoords[tess.numVertexes][1] = s_skyTexCoords[t][s][1];
403
404 tess.numVertexes++;
405
406 if(tess.numVertexes >= SHADER_MAX_VERTEXES)
407 {
408 ri.Error(ERR_DROP, "SHADER_MAX_VERTEXES hit in DrawSkySideVBO()");
409 }
410 }
411 }
412
413 for ( t = 0; t < maxs[1] - mins[1]; t++ )
414 {
415 for ( s = 0; s < maxs[0] - mins[0]; s++ )
416 {
417 if (tess.numIndexes + 6 >= SHADER_MAX_INDEXES)
418 {
419 ri.Error(ERR_DROP, "SHADER_MAX_INDEXES hit in DrawSkySideVBO()");
420 }
421
422 tess.indexes[tess.numIndexes++] = s + t * (maxs[0] - mins[0] + 1) + firstVertex;
423 tess.indexes[tess.numIndexes++] = s + (t + 1) * (maxs[0] - mins[0] + 1) + firstVertex;
424 tess.indexes[tess.numIndexes++] = (s + 1) + t * (maxs[0] - mins[0] + 1) + firstVertex;
425
426 tess.indexes[tess.numIndexes++] = (s + 1) + t * (maxs[0] - mins[0] + 1) + firstVertex;
427 tess.indexes[tess.numIndexes++] = s + (t + 1) * (maxs[0] - mins[0] + 1) + firstVertex;
428 tess.indexes[tess.numIndexes++] = (s + 1) + (t + 1) * (maxs[0] - mins[0] + 1) + firstVertex;
429 }
430 }
431
432 // FIXME: A lot of this can probably be removed for speed, and refactored into a more convenient function
433 RB_UpdateTessVao(ATTR_POSITION | ATTR_TEXCOORD);
434 /*
435 {
436 shaderProgram_t *sp = &tr.textureColorShader;
437
438 GLSL_BindProgram(sp);
439
440 GLSL_SetUniformMat4(sp, UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection);
441
442 color[0] =
443 color[1] =
444 color[2] = tr.identityLight;
445 color[3] = 1.0f;
446 GLSL_SetUniformVec4(sp, UNIFORM_COLOR, color);
447 }
448 */
449 {
450 shaderProgram_t *sp = &tr.lightallShader[0];
451 vec4_t vector;
452
453 GLSL_BindProgram(sp);
454
455 GLSL_SetUniformMat4(sp, UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection);
456
457 color[0] =
458 color[1] =
459 color[2] =
460 color[3] = 1.0f;
461 GLSL_SetUniformVec4(sp, UNIFORM_BASECOLOR, color);
462
463 color[0] =
464 color[1] =
465 color[2] =
466 color[3] = 0.0f;
467 GLSL_SetUniformVec4(sp, UNIFORM_VERTCOLOR, color);
468
469 VectorSet4(vector, 1.0, 0.0, 0.0, 1.0);
470 GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX, vector);
471
472 VectorSet4(vector, 0.0, 0.0, 0.0, 0.0);
473 GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXOFFTURB, vector);
474
475 GLSL_SetUniformInt(sp, UNIFORM_ALPHATEST, 0);
476 }
477
478 R_DrawElements(tess.numIndexes - tess.firstIndex, tess.firstIndex);
479
480 //qglDrawElements(GL_TRIANGLES, tess.numIndexes - tess.firstIndex, GL_INDEX_TYPE, BUFFER_OFFSET(tess.firstIndex * sizeof(glIndex_t)));
481
482 //R_BindNullVBO();
483 //R_BindNullIBO();
484
485 tess.numIndexes = tess.firstIndex;
486 tess.numVertexes = firstVertex;
487 tess.firstIndex = 0;
488 }
489
DrawSkySideInner(struct image_s * image,const int mins[2],const int maxs[2])490 static void DrawSkySideInner( struct image_s *image, const int mins[2], const int maxs[2] )
491 {
492 int s, t;
493 int firstVertex = tess.numVertexes;
494 //int firstIndex = tess.numIndexes;
495 vec4_t color;
496
497 //tess.numVertexes = 0;
498 //tess.numIndexes = 0;
499 tess.firstIndex = tess.numIndexes;
500
501 GL_BindToTMU( image, TB_COLORMAP );
502 GL_Cull( CT_TWO_SIDED );
503
504 for ( t = mins[1]+HALF_SKY_SUBDIVISIONS; t <= maxs[1]+HALF_SKY_SUBDIVISIONS; t++ )
505 {
506 for ( s = mins[0]+HALF_SKY_SUBDIVISIONS; s <= maxs[0]+HALF_SKY_SUBDIVISIONS; s++ )
507 {
508 tess.xyz[tess.numVertexes][0] = s_skyPoints[t][s][0];
509 tess.xyz[tess.numVertexes][1] = s_skyPoints[t][s][1];
510 tess.xyz[tess.numVertexes][2] = s_skyPoints[t][s][2];
511 tess.xyz[tess.numVertexes][3] = 1.0;
512
513 tess.texCoords[tess.numVertexes][0] = s_skyTexCoords[t][s][0];
514 tess.texCoords[tess.numVertexes][1] = s_skyTexCoords[t][s][1];
515
516 tess.numVertexes++;
517
518 if(tess.numVertexes >= SHADER_MAX_VERTEXES)
519 {
520 ri.Error(ERR_DROP, "SHADER_MAX_VERTEXES hit in DrawSkySideVBO()\n");
521 }
522 }
523 }
524
525 for ( t = 0; t < maxs[1] - mins[1]; t++ )
526 {
527 for ( s = 0; s < maxs[0] - mins[0]; s++ )
528 {
529 if (tess.numIndexes + 6 >= SHADER_MAX_INDEXES)
530 {
531 ri.Error(ERR_DROP, "SHADER_MAX_INDEXES hit in DrawSkySideVBO()\n");
532 }
533
534 tess.indexes[tess.numIndexes++] = s + t * (maxs[0] - mins[0] + 1) + firstVertex;
535 tess.indexes[tess.numIndexes++] = s + (t + 1) * (maxs[0] - mins[0] + 1) + firstVertex;
536 tess.indexes[tess.numIndexes++] = (s + 1) + t * (maxs[0] - mins[0] + 1) + firstVertex;
537
538 tess.indexes[tess.numIndexes++] = (s + 1) + t * (maxs[0] - mins[0] + 1) + firstVertex;
539 tess.indexes[tess.numIndexes++] = s + (t + 1) * (maxs[0] - mins[0] + 1) + firstVertex;
540 tess.indexes[tess.numIndexes++] = (s + 1) + (t + 1) * (maxs[0] - mins[0] + 1) + firstVertex;
541 }
542 }
543
544 // FIXME: A lot of this can probably be removed for speed, and refactored into a more convenient function
545 RB_UpdateTessVao(ATTR_POSITION | ATTR_TEXCOORD);
546 /*
547 {
548 shaderProgram_t *sp = &tr.textureColorShader;
549
550 GLSL_BindProgram(sp);
551
552 GLSL_SetUniformMat4(sp, UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection);
553
554 color[0] =
555 color[1] =
556 color[2] = tr.identityLight;
557 color[3] = 1.0f;
558 GLSL_SetUniformVec4(sp, UNIFORM_COLOR, color);
559 }
560 */
561 {
562 shaderProgram_t *sp = &tr.lightallShader[0];
563 vec4_t vector;
564
565 GLSL_BindProgram(sp);
566
567 GLSL_SetUniformMat4(sp, UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection);
568
569 color[0] =
570 color[1] =
571 color[2] =
572 color[3] = 1.0f;
573 GLSL_SetUniformVec4(sp, UNIFORM_BASECOLOR, color);
574
575 color[0] =
576 color[1] =
577 color[2] =
578 color[3] = 0.0f;
579 GLSL_SetUniformVec4(sp, UNIFORM_VERTCOLOR, color);
580
581 VectorSet4(vector, 1.0, 0.0, 0.0, 1.0);
582 GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX, vector);
583
584 VectorSet4(vector, 0.0, 0.0, 0.0, 0.0);
585 GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXOFFTURB, vector);
586
587 GLSL_SetUniformInt(sp, UNIFORM_ALPHATEST, 0);
588 }
589
590 R_DrawElements(tess.numIndexes - tess.firstIndex, tess.firstIndex);
591
592 //qglDrawElements(GL_TRIANGLES, tess.numIndexes - tess.firstIndex, GL_INDEX_TYPE, BUFFER_OFFSET(tess.firstIndex * sizeof(glIndex_t)));
593
594 //R_BindNullVBO();
595 //R_BindNullIBO();
596
597 tess.numIndexes = tess.firstIndex;
598 tess.numVertexes = firstVertex;
599 tess.firstIndex = 0;
600 }
601
DrawSkyBox(shader_t * shader)602 static void DrawSkyBox( shader_t *shader ) {
603 int i;
604
605 sky_min = 0;
606 sky_max = 1;
607
608 memset( s_skyTexCoords, 0, sizeof( s_skyTexCoords ) );
609
610 for ( i = 0 ; i < 6 ; i++ )
611 {
612 int sky_mins_subd[2], sky_maxs_subd[2];
613 int s, t;
614
615 sky_mins[0][i] = floor( sky_mins[0][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS;
616 sky_mins[1][i] = floor( sky_mins[1][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS;
617 sky_maxs[0][i] = ceil( sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS;
618 sky_maxs[1][i] = ceil( sky_maxs[1][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS;
619
620 if ( ( sky_mins[0][i] >= sky_maxs[0][i] ) ||
621 ( sky_mins[1][i] >= sky_maxs[1][i] ) ) {
622 continue;
623 }
624
625 sky_mins_subd[0] = sky_mins[0][i] * HALF_SKY_SUBDIVISIONS;
626 sky_mins_subd[1] = sky_mins[1][i] * HALF_SKY_SUBDIVISIONS;
627 sky_maxs_subd[0] = sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS;
628 sky_maxs_subd[1] = sky_maxs[1][i] * HALF_SKY_SUBDIVISIONS;
629
630 if ( sky_mins_subd[0] < -HALF_SKY_SUBDIVISIONS ) {
631 sky_mins_subd[0] = -HALF_SKY_SUBDIVISIONS;
632 } else if ( sky_mins_subd[0] > HALF_SKY_SUBDIVISIONS ) {
633 sky_mins_subd[0] = HALF_SKY_SUBDIVISIONS;
634 }
635 if ( sky_mins_subd[1] < -HALF_SKY_SUBDIVISIONS ) {
636 sky_mins_subd[1] = -HALF_SKY_SUBDIVISIONS;
637 } else if ( sky_mins_subd[1] > HALF_SKY_SUBDIVISIONS ) {
638 sky_mins_subd[1] = HALF_SKY_SUBDIVISIONS;
639 }
640
641 if ( sky_maxs_subd[0] < -HALF_SKY_SUBDIVISIONS ) {
642 sky_maxs_subd[0] = -HALF_SKY_SUBDIVISIONS;
643 } else if ( sky_maxs_subd[0] > HALF_SKY_SUBDIVISIONS ) {
644 sky_maxs_subd[0] = HALF_SKY_SUBDIVISIONS;
645 }
646 if ( sky_maxs_subd[1] < -HALF_SKY_SUBDIVISIONS ) {
647 sky_maxs_subd[1] = -HALF_SKY_SUBDIVISIONS;
648 } else if ( sky_maxs_subd[1] > HALF_SKY_SUBDIVISIONS ) {
649 sky_maxs_subd[1] = HALF_SKY_SUBDIVISIONS;
650 }
651
652 //
653 // iterate through the subdivisions
654 //
655 for ( t = sky_mins_subd[1] + HALF_SKY_SUBDIVISIONS; t <= sky_maxs_subd[1] + HALF_SKY_SUBDIVISIONS; t++ )
656 {
657 for ( s = sky_mins_subd[0] + HALF_SKY_SUBDIVISIONS; s <= sky_maxs_subd[0] + HALF_SKY_SUBDIVISIONS; s++ )
658 {
659 MakeSkyVec( ( s - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS,
660 ( t - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS,
661 i,
662 s_skyTexCoords[t][s],
663 s_skyPoints[t][s] );
664 }
665 }
666
667 DrawSkySide( shader->sky.outerbox[sky_texorder[i]],
668 sky_mins_subd,
669 sky_maxs_subd );
670 }
671
672 }
673
674
DrawSkyBoxInner(shader_t * shader)675 static void DrawSkyBoxInner( shader_t *shader ) {
676 int i;
677
678 memset( s_skyTexCoords, 0, sizeof( s_skyTexCoords ) );
679
680 for ( i = 0 ; i < 6 ; i++ )
681 {
682 int sky_mins_subd[2], sky_maxs_subd[2];
683 int s, t;
684
685 sky_mins[0][i] = floor( sky_mins[0][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS;
686 sky_mins[1][i] = floor( sky_mins[1][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS;
687 sky_maxs[0][i] = ceil( sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS;
688 sky_maxs[1][i] = ceil( sky_maxs[1][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS;
689
690 if ( ( sky_mins[0][i] >= sky_maxs[0][i] ) ||
691 ( sky_mins[1][i] >= sky_maxs[1][i] ) ) {
692 continue;
693 }
694
695 sky_mins_subd[0] = sky_mins[0][i] * HALF_SKY_SUBDIVISIONS;
696 sky_mins_subd[1] = sky_mins[1][i] * HALF_SKY_SUBDIVISIONS;
697 sky_maxs_subd[0] = sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS;
698 sky_maxs_subd[1] = sky_maxs[1][i] * HALF_SKY_SUBDIVISIONS;
699
700 if ( sky_mins_subd[0] < -HALF_SKY_SUBDIVISIONS ) {
701 sky_mins_subd[0] = -HALF_SKY_SUBDIVISIONS;
702 } else if ( sky_mins_subd[0] > HALF_SKY_SUBDIVISIONS ) {
703 sky_mins_subd[0] = HALF_SKY_SUBDIVISIONS;
704 }
705 if ( sky_mins_subd[1] < -HALF_SKY_SUBDIVISIONS ) {
706 sky_mins_subd[1] = -HALF_SKY_SUBDIVISIONS;
707 } else if ( sky_mins_subd[1] > HALF_SKY_SUBDIVISIONS ) {
708 sky_mins_subd[1] = HALF_SKY_SUBDIVISIONS;
709 }
710
711 if ( sky_maxs_subd[0] < -HALF_SKY_SUBDIVISIONS ) {
712 sky_maxs_subd[0] = -HALF_SKY_SUBDIVISIONS;
713 } else if ( sky_maxs_subd[0] > HALF_SKY_SUBDIVISIONS ) {
714 sky_maxs_subd[0] = HALF_SKY_SUBDIVISIONS;
715 }
716 if ( sky_maxs_subd[1] < -HALF_SKY_SUBDIVISIONS ) {
717 sky_maxs_subd[1] = -HALF_SKY_SUBDIVISIONS;
718 } else if ( sky_maxs_subd[1] > HALF_SKY_SUBDIVISIONS ) {
719 sky_maxs_subd[1] = HALF_SKY_SUBDIVISIONS;
720 }
721
722 //
723 // iterate through the subdivisions
724 //
725 for ( t = sky_mins_subd[1] + HALF_SKY_SUBDIVISIONS; t <= sky_maxs_subd[1] + HALF_SKY_SUBDIVISIONS; t++ )
726 {
727 for ( s = sky_mins_subd[0] + HALF_SKY_SUBDIVISIONS; s <= sky_maxs_subd[0] + HALF_SKY_SUBDIVISIONS; s++ )
728 {
729 MakeSkyVec( ( s - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS,
730 ( t - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS,
731 i,
732 s_skyTexCoords[t][s],
733 s_skyPoints[t][s] );
734 }
735 }
736
737 DrawSkySideInner( shader->sky.innerbox[sky_texorder[i]],
738 sky_mins_subd,
739 sky_maxs_subd );
740 }
741
742 }
743
FillCloudySkySide(const int mins[2],const int maxs[2],qboolean addIndexes)744 static void FillCloudySkySide( const int mins[2], const int maxs[2], qboolean addIndexes ) {
745 int s, t;
746 int vertexStart = tess.numVertexes;
747 int tHeight, sWidth;
748
749 tHeight = maxs[1] - mins[1] + 1;
750 sWidth = maxs[0] - mins[0] + 1;
751
752 for ( t = mins[1] + HALF_SKY_SUBDIVISIONS; t <= maxs[1] + HALF_SKY_SUBDIVISIONS; t++ )
753 {
754 for ( s = mins[0] + HALF_SKY_SUBDIVISIONS; s <= maxs[0] + HALF_SKY_SUBDIVISIONS; s++ )
755 {
756 VectorAdd( s_skyPoints[t][s], backEnd.viewParms.or.origin, tess.xyz[tess.numVertexes] );
757 tess.texCoords[tess.numVertexes][0] = s_skyTexCoords[t][s][0];
758 tess.texCoords[tess.numVertexes][1] = s_skyTexCoords[t][s][1];
759
760 tess.numVertexes++;
761
762 if ( tess.numVertexes >= SHADER_MAX_VERTEXES ) {
763 ri.Error( ERR_DROP, "SHADER_MAX_VERTEXES hit in FillCloudySkySide()" );
764 }
765 }
766 }
767
768 // only add indexes for one pass, otherwise it would draw multiple times for each pass
769 if ( addIndexes ) {
770 for ( t = 0; t < tHeight - 1; t++ )
771 {
772 for ( s = 0; s < sWidth - 1; s++ )
773 {
774 tess.indexes[tess.numIndexes] = vertexStart + s + t * ( sWidth );
775 tess.numIndexes++;
776 tess.indexes[tess.numIndexes] = vertexStart + s + ( t + 1 ) * ( sWidth );
777 tess.numIndexes++;
778 tess.indexes[tess.numIndexes] = vertexStart + s + 1 + t * ( sWidth );
779 tess.numIndexes++;
780
781 tess.indexes[tess.numIndexes] = vertexStart + s + ( t + 1 ) * ( sWidth );
782 tess.numIndexes++;
783 tess.indexes[tess.numIndexes] = vertexStart + s + 1 + ( t + 1 ) * ( sWidth );
784 tess.numIndexes++;
785 tess.indexes[tess.numIndexes] = vertexStart + s + 1 + t * ( sWidth );
786 tess.numIndexes++;
787 }
788 }
789 }
790 }
791
FillCloudBox(const shader_t * shader,int stage)792 static void FillCloudBox( const shader_t *shader, int stage ) {
793 int i;
794
795 for ( i = 0; i < 6; i++ )
796 {
797 int sky_mins_subd[2], sky_maxs_subd[2];
798 int s, t;
799 float MIN_T;
800
801 if ( 1 ) { // FIXME? shader->sky.fullClouds )
802 MIN_T = -HALF_SKY_SUBDIVISIONS;
803
804 // still don't want to draw the bottom, even if fullClouds
805 if ( i == 5 ) {
806 continue;
807 }
808 } else
809 {
810 switch ( i )
811 {
812 case 0:
813 case 1:
814 case 2:
815 case 3:
816 MIN_T = -1;
817 break;
818 case 5:
819 // don't draw clouds beneath you
820 continue;
821 case 4: // top
822 default:
823 MIN_T = -HALF_SKY_SUBDIVISIONS;
824 break;
825 }
826 }
827
828 sky_mins[0][i] = floor( sky_mins[0][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS;
829 sky_mins[1][i] = floor( sky_mins[1][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS;
830 sky_maxs[0][i] = ceil( sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS;
831 sky_maxs[1][i] = ceil( sky_maxs[1][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS;
832
833 if ( ( sky_mins[0][i] >= sky_maxs[0][i] ) ||
834 ( sky_mins[1][i] >= sky_maxs[1][i] ) ) {
835 continue;
836 }
837
838 sky_mins_subd[0] = ri.ftol( sky_mins[0][i] * HALF_SKY_SUBDIVISIONS );
839 sky_mins_subd[1] = ri.ftol( sky_mins[1][i] * HALF_SKY_SUBDIVISIONS );
840 sky_maxs_subd[0] = ri.ftol( sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS );
841 sky_maxs_subd[1] = ri.ftol( sky_maxs[1][i] * HALF_SKY_SUBDIVISIONS );
842
843 if ( sky_mins_subd[0] < -HALF_SKY_SUBDIVISIONS ) {
844 sky_mins_subd[0] = -HALF_SKY_SUBDIVISIONS;
845 } else if ( sky_mins_subd[0] > HALF_SKY_SUBDIVISIONS ) {
846 sky_mins_subd[0] = HALF_SKY_SUBDIVISIONS;
847 }
848 if ( sky_mins_subd[1] < MIN_T ) {
849 sky_mins_subd[1] = MIN_T;
850 } else if ( sky_mins_subd[1] > HALF_SKY_SUBDIVISIONS ) {
851 sky_mins_subd[1] = HALF_SKY_SUBDIVISIONS;
852 }
853
854 if ( sky_maxs_subd[0] < -HALF_SKY_SUBDIVISIONS ) {
855 sky_maxs_subd[0] = -HALF_SKY_SUBDIVISIONS;
856 } else if ( sky_maxs_subd[0] > HALF_SKY_SUBDIVISIONS ) {
857 sky_maxs_subd[0] = HALF_SKY_SUBDIVISIONS;
858 }
859 if ( sky_maxs_subd[1] < MIN_T ) {
860 sky_maxs_subd[1] = MIN_T;
861 } else if ( sky_maxs_subd[1] > HALF_SKY_SUBDIVISIONS ) {
862 sky_maxs_subd[1] = HALF_SKY_SUBDIVISIONS;
863 }
864
865 //
866 // iterate through the subdivisions
867 //
868 for ( t = sky_mins_subd[1] + HALF_SKY_SUBDIVISIONS; t <= sky_maxs_subd[1] + HALF_SKY_SUBDIVISIONS; t++ )
869 {
870 for ( s = sky_mins_subd[0] + HALF_SKY_SUBDIVISIONS; s <= sky_maxs_subd[0] + HALF_SKY_SUBDIVISIONS; s++ )
871 {
872 MakeSkyVec( ( s - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS,
873 ( t - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS,
874 i,
875 NULL,
876 s_skyPoints[t][s] );
877
878 s_skyTexCoords[t][s][0] = s_cloudTexCoords[i][t][s][0];
879 s_skyTexCoords[t][s][1] = s_cloudTexCoords[i][t][s][1];
880 }
881 }
882
883 // only add indexes for first stage
884 FillCloudySkySide( sky_mins_subd, sky_maxs_subd, ( stage == 0 ) );
885 }
886 }
887
888 /*
889 ** R_BuildCloudData
890 */
R_BuildCloudData(shaderCommands_t * input)891 void R_BuildCloudData( shaderCommands_t *input ) {
892 int i;
893 shader_t *shader;
894
895 shader = input->shader;
896
897 assert( shader->isSky );
898
899 sky_min = 1.0 / 256.0f; // FIXME: not correct?
900 sky_max = 255.0 / 256.0f;
901
902 // set up for drawing
903 tess.numIndexes = 0;
904 tess.numVertexes = 0;
905 tess.firstIndex = 0;
906
907 if ( shader->sky.cloudHeight ) {
908 for ( i = 0; i < MAX_SHADER_STAGES; i++ )
909 {
910 if ( !tess.xstages[i] ) {
911 break;
912 }
913 FillCloudBox( shader, i );
914 }
915 }
916 }
917
918 /*
919 ** R_InitSkyTexCoords
920 ** Called when a sky shader is parsed
921 */
922 #define SQR( a ) ( ( a ) * ( a ) )
R_InitSkyTexCoords(float heightCloud)923 void R_InitSkyTexCoords( float heightCloud ) {
924 int i, s, t;
925 float radiusWorld = 4096;
926 float p;
927 float sRad, tRad;
928 vec3_t skyVec;
929 vec3_t v;
930
931 // init zfar so MakeSkyVec works even though
932 // a world hasn't been bounded
933 backEnd.viewParms.zFar = 1024;
934
935 for ( i = 0; i < 6; i++ )
936 {
937 for ( t = 0; t <= SKY_SUBDIVISIONS; t++ )
938 {
939 for ( s = 0; s <= SKY_SUBDIVISIONS; s++ )
940 {
941 // compute vector from view origin to sky side integral point
942 MakeSkyVec( ( s - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS,
943 ( t - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS,
944 i,
945 NULL,
946 skyVec );
947
948 // compute parametric value 'p' that intersects with cloud layer
949 p = ( 1.0f / ( 2 * DotProduct( skyVec, skyVec ) ) ) *
950 ( -2 * skyVec[2] * radiusWorld +
951 2 * sqrt( SQR( skyVec[2] ) * SQR( radiusWorld ) +
952 2 * SQR( skyVec[0] ) * radiusWorld * heightCloud +
953 SQR( skyVec[0] ) * SQR( heightCloud ) +
954 2 * SQR( skyVec[1] ) * radiusWorld * heightCloud +
955 SQR( skyVec[1] ) * SQR( heightCloud ) +
956 2 * SQR( skyVec[2] ) * radiusWorld * heightCloud +
957 SQR( skyVec[2] ) * SQR( heightCloud ) ) );
958
959 s_cloudTexP[i][t][s] = p;
960
961 // compute intersection point based on p
962 VectorScale( skyVec, p, v );
963 v[2] += radiusWorld;
964
965 // compute vector from world origin to intersection point 'v'
966 VectorNormalize( v );
967
968 sRad = Q_acos( v[0] );
969 tRad = Q_acos( v[1] );
970
971 s_cloudTexCoords[i][t][s][0] = sRad;
972 s_cloudTexCoords[i][t][s][1] = tRad;
973 }
974 }
975 }
976 }
977
978 //======================================================================================
979
980 /*
981 ==============
982 RB_DrawSun
983 (SA) FIXME: sun should render behind clouds, so passing dark areas cover it up
984 ==============
985 */
RB_DrawSun(float scale,shader_t * shader)986 void RB_DrawSun( float scale, shader_t *shader ) {
987 float size;
988 float dist;
989 vec3_t origin, vec1, vec2;
990 vec3_t temp;
991 // vec4_t color;
992
993 if ( !shader ) {
994 return;
995 }
996
997 if ( !backEnd.skyRenderedThisView ) {
998 return;
999 }
1000
1001 //qglLoadMatrixf( backEnd.viewParms.world.modelMatrix );
1002 //qglTranslatef (backEnd.viewParms.or.origin[0], backEnd.viewParms.or.origin[1], backEnd.viewParms.or.origin[2]);
1003 {
1004 // FIXME: this could be a lot cleaner
1005 mat4_t translation, modelview;
1006
1007 Mat4Translation( backEnd.viewParms.or.origin, translation );
1008 Mat4Multiply( backEnd.viewParms.world.modelMatrix, translation, modelview );
1009 GL_SetModelviewMatrix( modelview );
1010 }
1011
1012 dist = backEnd.viewParms.zFar / 1.75; // div sqrt(3)
1013
1014 // (SA) shrunk the size of the sun
1015 size = dist * scale;
1016
1017 VectorScale( tr.sunDirection, dist, origin );
1018 PerpendicularVector( vec1, tr.sunDirection );
1019 CrossProduct( tr.sunDirection, vec1, vec2 );
1020
1021 VectorScale( vec1, size, vec1 );
1022 VectorScale( vec2, size, vec2 );
1023
1024 // farthest depth range
1025 qglDepthRange( 1.0, 1.0 );
1026
1027 RB_BeginSurface( shader, 0, 0 );
1028
1029 // color[0] = color[1] = color[2] = color[3] = 1;
1030
1031 RB_AddQuadStamp(origin, vec1, vec2, colorWhite);
1032
1033 RB_EndSurface();
1034
1035
1036 if ( r_drawSun->integer > 1 ) { // draw flare effect
1037 // (SA) FYI: This is cheezy and was only a test so far.
1038 // If we decide to use the flare business I will /definatly/ improve all this
1039
1040 // get a point a little closer
1041 dist = dist * 0.7;
1042 VectorScale( tr.sunDirection, dist, origin );
1043
1044 // and make the flare a little smaller
1045 VectorScale( vec1, 0.5f, vec1 );
1046 VectorScale( vec2, 0.5f, vec2 );
1047
1048 // add the vectors to give an 'off angle' result
1049 VectorAdd( tr.sunDirection, backEnd.viewParms.or.axis[0], temp );
1050 VectorNormalize( temp );
1051
1052 // amplify the result
1053 origin[0] += temp[0] * 500.0;
1054 origin[1] += temp[1] * 500.0;
1055 origin[2] += temp[2] * 500.0;
1056
1057 // (SA) FIXME: todo: flare effect should render last (on top of everything else) and only when sun is in view (sun moving out of camera past degree n should start to cause flare dimming until view angle to sun is off by angle n + x.
1058
1059 // draw the flare
1060 RB_BeginSurface( tr.sunflareShader_old[0], 0, 0 );
1061 RB_AddQuadStamp( origin, vec1, vec2, colorWhite );
1062 RB_EndSurface();
1063 }
1064
1065 // back to normal depth range
1066 qglDepthRange( 0.0, 1.0 );
1067 }
1068
1069 /*
1070 ================
1071 RB_StageIteratorSky
1072
1073 All of the visible sky triangles are in tess
1074
1075 Other things could be stuck in here, like birds in the sky, etc
1076 ================
1077 */
RB_StageIteratorSky(void)1078 void RB_StageIteratorSky( void ) {
1079 if ( r_fastsky->integer ) {
1080 return;
1081 }
1082
1083 // when portal sky exists, only render skybox for the portal sky scene
1084 if ( skyboxportal && !( backEnd.refdef.rdflags & RDF_SKYBOXPORTAL ) ) {
1085 return;
1086 }
1087
1088 // does the current fog require fastsky?
1089 if ( backEnd.viewParms.glFog.registered ) {
1090 if ( !backEnd.viewParms.glFog.drawsky ) {
1091 return;
1092 }
1093 } else if ( glfogNum > FOG_NONE ) {
1094 if ( !glfogsettings[FOG_CURRENT].drawsky ) {
1095 return;
1096 }
1097 }
1098
1099
1100 backEnd.refdef.rdflags |= RDF_DRAWINGSKY;
1101
1102
1103 // go through all the polygons and project them onto
1104 // the sky box to see which blocks on each side need
1105 // to be drawn
1106 RB_ClipSkyPolygons( &tess );
1107
1108 // r_showsky will let all the sky blocks be drawn in
1109 // front of everything to allow developers to see how
1110 // much sky is getting sucked in
1111 if ( r_showsky->integer ) {
1112 qglDepthRange( 0.0, 0.0 );
1113 } else {
1114 qglDepthRange( 1.0, 1.0 );
1115 }
1116
1117 // draw the outer skybox
1118 if ( tess.shader->sky.outerbox[0] && tess.shader->sky.outerbox[0] != tr.defaultImage ) {
1119 mat4_t oldmodelview;
1120
1121 GL_State( 0 );
1122 GL_Cull( CT_FRONT_SIDED );
1123 //qglTranslatef( backEnd.viewParms.or.origin[0], backEnd.viewParms.or.origin[1], backEnd.viewParms.or.origin[2] );
1124
1125 {
1126 // FIXME: this could be a lot cleaner
1127 mat4_t trans, product;
1128
1129 Mat4Copy( glState.modelview, oldmodelview );
1130 Mat4Translation( backEnd.viewParms.or.origin, trans );
1131 Mat4Multiply( glState.modelview, trans, product );
1132 GL_SetModelviewMatrix( product );
1133
1134 }
1135
1136 DrawSkyBox( tess.shader );
1137
1138 GL_SetModelviewMatrix( oldmodelview );
1139 }
1140
1141 // generate the vertexes for all the clouds, which will be drawn
1142 // by the generic shader routine
1143 R_BuildCloudData( &tess );
1144
1145 RB_StageIteratorGeneric();
1146
1147 // draw the inner skybox
1148 // Rafael - drawing inner skybox
1149 if ( tess.shader->sky.innerbox[0] && tess.shader->sky.innerbox[0] != tr.defaultImage ) {
1150 mat4_t oldmodelview;
1151
1152 GL_State( 0 );
1153 GL_Cull( CT_FRONT_SIDED );
1154 //qglTranslatef( backEnd.viewParms.or.origin[0], backEnd.viewParms.or.origin[1], backEnd.viewParms.or.origin[2] );
1155
1156 {
1157 // FIXME: this could be a lot cleaner
1158 mat4_t trans, product;
1159
1160 Mat4Copy( glState.modelview, oldmodelview );
1161 Mat4Translation( backEnd.viewParms.or.origin, trans );
1162 Mat4Multiply( glState.modelview, trans, product );
1163 GL_SetModelviewMatrix( product );
1164
1165 }
1166
1167 DrawSkyBoxInner( tess.shader );
1168
1169 GL_SetModelviewMatrix( oldmodelview );
1170 }
1171 // Rafael - end
1172
1173 // back to normal depth range
1174 qglDepthRange( 0.0, 1.0 );
1175
1176 backEnd.refdef.rdflags &= ~RDF_DRAWINGSKY;
1177
1178 // note that sky was drawn so we will draw a sun later
1179 backEnd.skyRenderedThisView = qtrue;
1180 }
1181
1182