6 void DrawSphere( vec3_t center, float radius, int sides, int nGLState ){
8 float dt = (float) ( 2 * Q_PI / (float) sides );
9 float dp = (float) ( Q_PI / (float) sides );
17 g_QglTable.m_pfn_qglBegin( GL_TRIANGLES );
18 for ( i = 0; i <= sides - 1; i++ ) {
19 for ( j = 0; j <= sides - 2; j++ ) {
21 p = (float) ( ( j * dp ) - ( Q_PI / 2 ) );
23 VectorPolar( v, radius, t, p );
24 VectorAdd( v, center, v );
25 g_QglTable.m_pfn_qglVertex3fv( v );
27 VectorPolar( v, radius, t, p + dp );
28 VectorAdd( v, center, v );
29 g_QglTable.m_pfn_qglVertex3fv( v );
31 VectorPolar( v, radius, t + dt, p + dp );
32 VectorAdd( v, center, v );
33 g_QglTable.m_pfn_qglVertex3fv( v );
35 VectorPolar( v, radius, t, p );
36 VectorAdd( v, center, v );
37 g_QglTable.m_pfn_qglVertex3fv( v );
39 VectorPolar( v, radius, t + dt, p + dp );
40 VectorAdd( v, center, v );
41 g_QglTable.m_pfn_qglVertex3fv( v );
43 VectorPolar( v, radius, t + dt, p );
44 VectorAdd( v, center, v );
45 g_QglTable.m_pfn_qglVertex3fv( v );
49 p = (float) ( ( sides - 1 ) * dp - ( Q_PI / 2 ) );
50 for ( i = 0; i <= sides - 1; i++ ) {
53 VectorPolar( v, radius, t, p );
54 VectorAdd( v, center, v );
55 g_QglTable.m_pfn_qglVertex3fv( v );
57 VectorPolar( v, radius, t + dt, p + dp );
58 VectorAdd( v, center, v );
59 g_QglTable.m_pfn_qglVertex3fv( v );
61 VectorPolar( v, radius, t + dt, p );
62 VectorAdd( v, center, v );
63 g_QglTable.m_pfn_qglVertex3fv( v );
65 g_QglTable.m_pfn_qglEnd();
68 #define LIGHT_ATTEN_LINEAR 1
69 #define LIGHT_ATTEN_ANGLE 2
70 #define LIGHT_ATTEN_DISTANCE 4
72 #define LIGHT_Q3A_DEFAULT ( LIGHT_ATTEN_ANGLE | LIGHT_ATTEN_DISTANCE )
73 #define LIGHT_WOLF_DEFAULT ( LIGHT_ATTEN_LINEAR | LIGHT_ATTEN_DISTANCE )
75 float CalculateEnvelopeForLight( entity_t * e, float fFalloffTolerance ){
76 float fEnvelope = 0.f;
77 int iSpawnFlags = atoi( ValueForKey( e, "spawnflags" ) );
80 float fIntensity, fPhotons;
82 const char *gameFile = g_FuncTable.m_pfnGetGameFile();
84 // These variables are tweakable on the q3map2 console, setting to q3map2
85 // default here as there is no way to find out what the user actually uses
86 // right now. Maybe move them to worldspawn?
87 float fPointScale = 7500.f;
88 float fLinearScale = 1.f / 8000.f;
89 //float fFalloffTolerance = 1.f; // Need it as parameter
91 // Arnout: HACK for per-game radii - really need to move this to a per-game module?
92 if ( !strcmp( gameFile, "wolf.game" ) || !strcmp( gameFile, "et.game" ) ) {
98 iLightFlags = LIGHT_WOLF_DEFAULT;
100 // inverse distance squared attenuation?
101 if ( iSpawnFlags & 1 ) {
102 iLightFlags &= ~LIGHT_ATTEN_LINEAR;
103 iLightFlags |= LIGHT_ATTEN_ANGLE;
106 if ( iSpawnFlags & 2 ) {
107 iLightFlags |= LIGHT_ATTEN_ANGLE;
116 iLightFlags = LIGHT_Q3A_DEFAULT;
118 // linear attenuation?
119 if ( iSpawnFlags & 1 ) {
120 iLightFlags |= LIGHT_ATTEN_LINEAR;
121 iLightFlags &= ~LIGHT_ATTEN_ANGLE;
123 // no angle attenuate?
124 if ( iSpawnFlags & 2 ) {
125 iLightFlags &= ~LIGHT_ATTEN_ANGLE;
129 // set fade key (from wolf)
130 if ( iLightFlags & LIGHT_ATTEN_LINEAR ) {
131 fFade = FloatForKey( e, "fade" );
132 if ( fFade <= 0.f ) {
136 // set light intensity
137 fIntensity = FloatForKey( e, "_light" );
138 if ( fIntensity == 0.f ) {
139 fIntensity = FloatForKey( e, "light" );
141 if ( fIntensity == 0.f ) {
145 // set light scale (sof2)
146 fScale = FloatForKey( e, "scale" );
147 if ( fScale <= 0.f ) {
150 fIntensity *= fScale;
153 fPhotons = fIntensity * fPointScale;
155 // calculate envelope
157 // solve distance for non-distance lights
158 if ( !( iLightFlags & LIGHT_ATTEN_DISTANCE ) ) {
159 //!\todo (spog) can't access global objects in a module - globals are EVIL - solution: API for querying global settings.
160 fEnvelope = 131072 /*g_MaxWorldCoord * 2.f*/;
162 // solve distance for linear lights
163 else if ( iLightFlags & LIGHT_ATTEN_LINEAR ) {
164 fEnvelope = ( ( fPhotons * fLinearScale ) - fFalloffTolerance ) / fFade;
166 // solve for inverse square falloff
168 fEnvelope = sqrt( fPhotons / fFalloffTolerance ) /* + fRadius */ ; // Arnout radius is always 0, only for area lights
174 float CalculateLightRadius( entity_t * e, bool outer ){
175 float fEnvelope = 0.f;
176 int iSpawnFlags = atoi( ValueForKey( e, "spawnflags" ) );
179 const char *gameFile = g_FuncTable.m_pfnGetGameFile();
181 fIntensity = FloatForKey( e, "light" );
182 if ( fIntensity == 0.f ) {
186 // Arnout: HACK for per-game radii - really need to move this to a per-game module
187 if ( !strcmp( gameFile, "sof2.game" ) || !strcmp( gameFile, "jk2.game" ) || !strcmp( gameFile, "ja.game" ) ) {
193 if ( iSpawnFlags & 2 ) {
200 // set light scale (sof2)
201 fScale = FloatForKey( e, "scale" );
202 if ( fScale <= 0.f ) {
205 fIntensity *= fScale;
207 fEnvelope = fIntensity;
210 float fPointScale = 7500.f;
213 fEnvelope = sqrt( fIntensity * fPointScale / 48.f );
216 fEnvelope = sqrt( fIntensity * fPointScale / 255.f );
223 void Light_OnIntensityChanged( entity_t* e ){
224 e->fLightEnvelope1[0] = CalculateEnvelopeForLight( e, 1.f );
225 e->fLightEnvelope1[1] = CalculateEnvelopeForLight( e, 48.f );
226 e->fLightEnvelope1[2] = CalculateEnvelopeForLight( e, 255.f );
228 e->fLightEnvelope2[0] = CalculateLightRadius( e, TRUE );
229 e->fLightEnvelope2[1] = CalculateLightRadius( e, FALSE );
232 void Light_OnKeyValueChanged( entity_t *e, const char *key, const char* value ){
233 if ( strcmp( key,"_color" ) == 0 ) {
234 if ( sscanf( ValueForKey( e, "_color" ),"%f %f %f",
235 &e->color[0], &e->color[1], &e->color[2] ) != 3 ) {
236 VectorSet( e->color, 1, 1, 1 );
239 else if ( strcmp( key,"spawnflags" ) == 0 ||
240 strcmp( key,"fade" ) == 0 ||
241 strcmp( key,"_light" ) == 0 ||
242 strcmp( key,"light" ) == 0 ||
243 strcmp( key,"scale" ) == 0 ) {
244 Light_OnIntensityChanged( e );
248 bool Entity_IsLight( entity_t *e ){
249 return e->eclass != NULL && e->eclass->nShowFlags & ECLASS_LIGHT; //strncmp(ValueforKey(e, "classname"), "light") == 0
252 static void DrawLightSphere( entity_t * e, int nGLState, int pref ){
253 const char *target = ValueForKey( e, "target" );
254 bool bIsSpotLight = !!target[0];
255 //!\todo Write an API for modules to register preference settings, and make this preference module-specific.
256 // int nPasses = pref == 1 ? 3 : 2;
258 g_QglTable.m_pfn_qglPushAttrib( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT );
259 g_QglTable.m_pfn_qglDepthMask( GL_FALSE );
260 g_QglTable.m_pfn_qglEnable( GL_BLEND );
261 g_QglTable.m_pfn_qglBlendFunc( GL_ONE, GL_ONE );
263 // Arnout: TODO: spotlight rendering
264 if ( !( bIsSpotLight ) ) {
268 g_QglTable.m_pfn_qglColor3f( e->color[0] * .05f,
270 e->color[2] * .05f );
271 DrawSphere( e->origin, e->fLightEnvelope1[0], 16, nGLState );
272 DrawSphere( e->origin, e->fLightEnvelope1[1], 16, nGLState );
273 DrawSphere( e->origin, e->fLightEnvelope1[2], 16, nGLState );
276 g_QglTable.m_pfn_qglColor3f( e->color[0] * .15f * .95f,
277 e->color[1] * .15f * .95f,
278 e->color[2] * .15f * .95f );
279 DrawSphere( e->origin, e->fLightEnvelope2[0], 16, nGLState );
280 DrawSphere( e->origin, e->fLightEnvelope2[1], 16, nGLState );
286 g_QglTable.m_pfn_qglPopAttrib();
289 float F = 0.70710678f;
290 // North, East, South, West
291 vec3_t normals[8] = { { 0, F, F }, { F, 0, F }, { 0,-F, F }, {-F, 0, F },
292 { 0, F,-F }, { F, 0,-F }, { 0,-F,-F }, {-F, 0,-F } };
294 unsigned short indices[24] = { 0, 2, 3, 0, 3, 4, 0, 4, 5, 0, 5, 2,
295 1, 2, 5, 1, 5, 4, 1, 4, 3, 1, 3, 2 };
297 void DrawLight( entity_t* e, int nGLState, int pref, int nViewType ){
299 // top, bottom, tleft, tright, bright, bleft
301 vec3_t vMid, vMin, vMax;
302 VectorAdd( e->origin, e->eclass->mins, vMin );
303 VectorAdd( e->origin, e->eclass->maxs, vMax );
304 vMid[0] = ( vMin[0] + vMax[0] ) * 0.5;
305 vMid[1] = ( vMin[1] + vMax[1] ) * 0.5;
306 vMid[2] = ( vMin[2] + vMax[2] ) * 0.5;
308 VectorSet( points[0], vMid[0], vMid[1], vMax[2] );
309 VectorSet( points[1], vMid[0], vMid[1], vMin[2] );
310 VectorSet( points[2], vMin[0], vMax[1], vMid[2] );
311 VectorSet( points[3], vMax[0], vMax[1], vMid[2] );
312 VectorSet( points[4], vMax[0], vMin[1], vMid[2] );
313 VectorSet( points[5], vMin[0], vMin[1], vMid[2] );
315 if ( nGLState & DRAW_GL_LIGHTING ) { // && g_PrefsDlg.m_bGLLighting)
316 g_QglTable.m_pfn_qglBegin( GL_TRIANGLES ); // NOTE: comment to use gl_triangle_fan instead
317 //g_QglTable.m_pfn_qglBegin(GL_TRIANGLE_FAN);
318 g_QglTable.m_pfn_qglVertex3fv( points[0] );
319 g_QglTable.m_pfn_qglVertex3fv( points[2] );
320 g_QglTable.m_pfn_qglNormal3fv( normals[0] );
321 g_QglTable.m_pfn_qglVertex3fv( points[3] );
323 g_QglTable.m_pfn_qglVertex3fv( points[0] ); //
324 g_QglTable.m_pfn_qglVertex3fv( points[3] ); //
325 g_QglTable.m_pfn_qglNormal3fv( normals[1] );
326 g_QglTable.m_pfn_qglVertex3fv( points[4] );
328 g_QglTable.m_pfn_qglVertex3fv( points[0] ); //
329 g_QglTable.m_pfn_qglVertex3fv( points[4] ); //
330 g_QglTable.m_pfn_qglNormal3fv( normals[2] );
331 g_QglTable.m_pfn_qglVertex3fv( points[5] );
333 g_QglTable.m_pfn_qglVertex3fv( points[0] ); //
334 g_QglTable.m_pfn_qglVertex3fv( points[5] ); //
335 g_QglTable.m_pfn_qglNormal3fv( normals[3] );
336 g_QglTable.m_pfn_qglVertex3fv( points[2] );
338 //g_QglTable.m_pfn_qglEnd();
339 //g_QglTable.m_pfn_qglBegin(GL_TRIANGLE_FAN);
341 g_QglTable.m_pfn_qglVertex3fv( points[1] );
342 g_QglTable.m_pfn_qglVertex3fv( points[2] );
343 g_QglTable.m_pfn_qglNormal3fv( normals[7] );
344 g_QglTable.m_pfn_qglVertex3fv( points[5] );
346 g_QglTable.m_pfn_qglVertex3fv( points[1] ); //
347 g_QglTable.m_pfn_qglVertex3fv( points[5] ); //
348 g_QglTable.m_pfn_qglNormal3fv( normals[6] );
349 g_QglTable.m_pfn_qglVertex3fv( points[4] );
351 g_QglTable.m_pfn_qglVertex3fv( points[1] ); //
352 g_QglTable.m_pfn_qglVertex3fv( points[4] ); //
353 g_QglTable.m_pfn_qglNormal3fv( normals[5] );
354 g_QglTable.m_pfn_qglVertex3fv( points[3] );
356 g_QglTable.m_pfn_qglVertex3fv( points[1] ); //
357 g_QglTable.m_pfn_qglVertex3fv( points[3] ); //
358 g_QglTable.m_pfn_qglNormal3fv( normals[4] );
359 g_QglTable.m_pfn_qglVertex3fv( points[2] );
361 g_QglTable.m_pfn_qglEnd();
363 else if ( nGLState & DRAW_GL_FILL ) {
365 VectorScale( e->color, 0.95, colors[0] );
366 VectorScale( colors[0], 0.95, colors[1] );
367 VectorScale( colors[1], 0.95, colors[2] );
368 VectorScale( colors[2], 0.95, colors[3] );
369 g_QglTable.m_pfn_qglBegin( GL_TRIANGLES ); // NOTE: comment to use gl_triangle_fan instead
370 //g_QglTable.m_pfn_qglBegin(GL_TRIANGLE_FAN);
371 g_QglTable.m_pfn_qglColor3fv( colors[0] );
372 g_QglTable.m_pfn_qglVertex3fv( points[0] );
373 g_QglTable.m_pfn_qglVertex3fv( points[2] );
374 g_QglTable.m_pfn_qglVertex3fv( points[3] );
376 g_QglTable.m_pfn_qglColor3fv( colors[1] );
377 g_QglTable.m_pfn_qglVertex3fv( points[0] ); //
378 g_QglTable.m_pfn_qglVertex3fv( points[3] ); //
379 g_QglTable.m_pfn_qglVertex3fv( points[4] );
381 g_QglTable.m_pfn_qglColor3fv( colors[2] );
382 g_QglTable.m_pfn_qglVertex3fv( points[0] ); //
383 g_QglTable.m_pfn_qglVertex3fv( points[4] ); //
384 g_QglTable.m_pfn_qglVertex3fv( points[5] );
386 g_QglTable.m_pfn_qglColor3fv( colors[3] );
387 g_QglTable.m_pfn_qglVertex3fv( points[0] ); //
388 g_QglTable.m_pfn_qglVertex3fv( points[5] ); //
389 g_QglTable.m_pfn_qglVertex3fv( points[2] );
391 //g_QglTable.m_pfn_qglEnd();
392 //g_QglTable.m_pfn_qglBegin(GL_TRIANGLE_FAN);
394 g_QglTable.m_pfn_qglColor3fv( colors[0] );
395 g_QglTable.m_pfn_qglVertex3fv( points[1] );
396 g_QglTable.m_pfn_qglVertex3fv( points[2] );
397 g_QglTable.m_pfn_qglVertex3fv( points[5] );
399 g_QglTable.m_pfn_qglColor3fv( colors[1] );
400 g_QglTable.m_pfn_qglVertex3fv( points[1] ); //
401 g_QglTable.m_pfn_qglVertex3fv( points[5] ); //
402 g_QglTable.m_pfn_qglVertex3fv( points[4] );
404 g_QglTable.m_pfn_qglColor3fv( colors[2] );
405 g_QglTable.m_pfn_qglVertex3fv( points[1] ); //
406 g_QglTable.m_pfn_qglVertex3fv( points[4] ); //
407 g_QglTable.m_pfn_qglVertex3fv( points[3] );
409 g_QglTable.m_pfn_qglColor3fv( colors[3] );
410 g_QglTable.m_pfn_qglVertex3fv( points[1] ); //
411 g_QglTable.m_pfn_qglVertex3fv( points[3] ); //
412 g_QglTable.m_pfn_qglVertex3fv( points[2] );
414 g_QglTable.m_pfn_qglEnd();
418 g_QglTable.m_pfn_qglVertexPointer( 3, GL_FLOAT, 0, points );
419 g_QglTable.m_pfn_qglDrawElements( GL_TRIANGLES, 24, GL_UNSIGNED_SHORT, indices );
423 // NOTE: prolly not relevant until some time..
424 // check for DOOM lights
425 if ( strlen( ValueForKey( e, "light_right" ) ) > 0 ) {
426 vec3_t vRight, vUp, vTarget, vTemp;
427 GetVectorForKey( e, "light_right", vRight );
428 GetVectorForKey( e, "light_up", vUp );
429 GetVectorForKey( e, "light_target", vTarget );
431 g_QglTable.m_pfn_qglColor3f( 0, 1, 0 );
432 g_QglTable.m_pfn_qglBegin( GL_LINE_LOOP );
433 VectorAdd( vTarget, e->origin, vTemp );
434 VectorAdd( vTemp, vRight, vTemp );
435 VectorAdd( vTemp, vUp, vTemp );
436 g_QglTable.m_pfn_qglVertex3fv( e->origin );
437 g_QglTable.m_pfn_qglVertex3fv( vTemp );
438 VectorAdd( vTarget, e->origin, vTemp );
439 VectorAdd( vTemp, vUp, vTemp );
440 VectorSubtract( vTemp, vRight, vTemp );
441 g_QglTable.m_pfn_qglVertex3fv( e->origin );
442 g_QglTable.m_pfn_qglVertex3fv( vTemp );
443 VectorAdd( vTarget, e->origin, vTemp );
444 VectorAdd( vTemp, vRight, vTemp );
445 VectorSubtract( vTemp, vUp, vTemp );
446 g_QglTable.m_pfn_qglVertex3fv( e->origin );
447 g_QglTable.m_pfn_qglVertex3fv( vTemp );
448 VectorAdd( vTarget, e->origin, vTemp );
449 VectorSubtract( vTemp, vUp, vTemp );
450 VectorSubtract( vTemp, vRight, vTemp );
451 g_QglTable.m_pfn_qglVertex3fv( e->origin );
452 g_QglTable.m_pfn_qglVertex3fv( vTemp );
453 g_QglTable.m_pfn_qglEnd();
457 if ( nGLState & DRAW_GL_FILL ) {
458 DrawLightSphere( e, nGLState, pref );
462 // Arnout: FIXME: clean this up a bit
463 // now draw lighting radius stuff...
465 bool bDrawSpotlightArc = false;
466 int nPasses = pref == 1 ? 3 : 2;
468 const char *target = ValueForKey( e, "target" );
469 bool bIsSpotLight = !!target[0];
474 // find the origin of the target...
475 entity_t *e = FindEntity("targetname", target);
478 bDrawSpotlightArc = true;
482 g_QglTable.m_pfn_qglPushAttrib( GL_LINE_BIT );
483 g_QglTable.m_pfn_qglLineStipple( 8, 0xAAAA );
484 g_QglTable.m_pfn_qglEnable( GL_LINE_STIPPLE );
486 float* envelope = ( pref == 1 ) ? e->fLightEnvelope1 : e->fLightEnvelope2;
487 for ( int iPass = 0; iPass < nPasses; iPass++ )
489 float fRadius = envelope[iPass];
491 g_QglTable.m_pfn_qglBegin( GL_LINE_LOOP );
493 if ( bIsSpotLight ) {
494 if ( bDrawSpotlightArc ) {
495 // I give up on this, it's beyond me
504 for ( i = 0; i <= 24; i++ )
506 ds = sin( ( i * 2 * Q_PI ) / 24 );
507 dc = cos( ( i * 2 * Q_PI ) / 24 );
512 g_QglTable.m_pfn_qglVertex3f( e->origin[0] + fRadius * dc,
513 e->origin[1] + fRadius * ds,
517 g_QglTable.m_pfn_qglVertex3f( e->origin[0] + fRadius * dc,
519 e->origin[2] + fRadius * ds );
522 g_QglTable.m_pfn_qglVertex3f( e->origin[0],
523 e->origin[1] + fRadius * dc,
524 e->origin[2] + fRadius * ds );
530 g_QglTable.m_pfn_qglEnd();
532 g_QglTable.m_pfn_qglPopAttrib();