6 void DrawSphere(vec3_t center, float radius, int sides, int nGLState)
\r
9 float dt = (float) (2 * Q_PI / (float) sides);
\r
10 float dp = (float) (Q_PI / (float) sides);
\r
17 g_QglTable.m_pfn_qglBegin(GL_TRIANGLES);
\r
18 for (i = 0; i <= sides - 1; i++) {
\r
19 for (j = 0; j <= sides - 2; j++) {
\r
21 p = (float) ((j * dp) - (Q_PI / 2));
\r
23 VectorPolar(v, radius, t, p);
\r
24 VectorAdd(v, center, v);
\r
25 g_QglTable.m_pfn_qglVertex3fv(v);
\r
27 VectorPolar(v, radius, t, p + dp);
\r
28 VectorAdd(v, center, v);
\r
29 g_QglTable.m_pfn_qglVertex3fv(v);
\r
31 VectorPolar(v, radius, t + dt, p + dp);
\r
32 VectorAdd(v, center, v);
\r
33 g_QglTable.m_pfn_qglVertex3fv(v);
\r
35 VectorPolar(v, radius, t, p);
\r
36 VectorAdd(v, center, v);
\r
37 g_QglTable.m_pfn_qglVertex3fv(v);
\r
39 VectorPolar(v, radius, t + dt, p + dp);
\r
40 VectorAdd(v, center, v);
\r
41 g_QglTable.m_pfn_qglVertex3fv(v);
\r
43 VectorPolar(v, radius, t + dt, p);
\r
44 VectorAdd(v, center, v);
\r
45 g_QglTable.m_pfn_qglVertex3fv(v);
\r
49 p = (float) ((sides - 1) * dp - (Q_PI / 2));
\r
50 for (i = 0; i <= sides - 1; i++) {
\r
53 VectorPolar(v, radius, t, p);
\r
54 VectorAdd(v, center, v);
\r
55 g_QglTable.m_pfn_qglVertex3fv(v);
\r
57 VectorPolar(v, radius, t + dt, p + dp);
\r
58 VectorAdd(v, center, v);
\r
59 g_QglTable.m_pfn_qglVertex3fv(v);
\r
61 VectorPolar(v, radius, t + dt, p);
\r
62 VectorAdd(v, center, v);
\r
63 g_QglTable.m_pfn_qglVertex3fv(v);
\r
65 g_QglTable.m_pfn_qglEnd();
\r
68 #define LIGHT_ATTEN_LINEAR 1
\r
69 #define LIGHT_ATTEN_ANGLE 2
\r
70 #define LIGHT_ATTEN_DISTANCE 4
\r
72 #define LIGHT_Q3A_DEFAULT (LIGHT_ATTEN_ANGLE | LIGHT_ATTEN_DISTANCE)
\r
73 #define LIGHT_WOLF_DEFAULT (LIGHT_ATTEN_LINEAR | LIGHT_ATTEN_DISTANCE)
\r
75 float CalculateEnvelopeForLight(entity_t * e, float fFalloffTolerance)
\r
77 float fEnvelope = 0.f;
\r
78 int iSpawnFlags = atoi(ValueForKey(e, "spawnflags"));
\r
79 int iLightFlags = 0;
\r
81 float fIntensity, fPhotons;
\r
83 const char *gameFile = g_FuncTable.m_pfnGetGameFile();
\r
85 // These variables are tweakable on the q3map2 console, setting to q3map2
\r
86 // default here as there is no way to find out what the user actually uses
\r
87 // right now. Maybe move them to worldspawn?
\r
88 float fPointScale = 7500.f;
\r
89 float fLinearScale = 1.f / 8000.f;
\r
90 //float fFalloffTolerance = 1.f; // Need it as parameter
\r
92 // Arnout: HACK for per-game radii - really need to move this to a per-game module?
\r
93 if( !strcmp( gameFile, "wolf.game" ) || !strcmp( gameFile, "et.game" ) ) {
\r
98 // set default flags
\r
99 iLightFlags = LIGHT_WOLF_DEFAULT;
\r
101 // inverse distance squared attenuation?
\r
102 if (iSpawnFlags & 1) {
\r
103 iLightFlags &= ~LIGHT_ATTEN_LINEAR;
\r
104 iLightFlags |= LIGHT_ATTEN_ANGLE;
\r
107 if (iSpawnFlags & 2)
\r
108 iLightFlags |= LIGHT_ATTEN_ANGLE;
\r
114 // set default flags
\r
115 iLightFlags = LIGHT_Q3A_DEFAULT;
\r
117 // linear attenuation?
\r
118 if (iSpawnFlags & 1) {
\r
119 iLightFlags |= LIGHT_ATTEN_LINEAR;
\r
120 iLightFlags &= ~LIGHT_ATTEN_ANGLE;
\r
122 // no angle attenuate?
\r
123 if (iSpawnFlags & 2)
\r
124 iLightFlags &= ~LIGHT_ATTEN_ANGLE;
\r
127 // set fade key (from wolf)
\r
128 if (iLightFlags & LIGHT_ATTEN_LINEAR) {
\r
129 fFade = FloatForKey(e, "fade");
\r
133 // set light intensity
\r
134 fIntensity = FloatForKey(e, "_light");
\r
135 if (fIntensity == 0.f)
\r
136 fIntensity = FloatForKey(e, "light");
\r
137 if (fIntensity == 0.f)
\r
138 fIntensity = 300.f;
\r
140 // set light scale (sof2)
\r
141 fScale = FloatForKey(e, "scale");
\r
144 fIntensity *= fScale;
\r
146 // amount of photons
\r
147 fPhotons = fIntensity * fPointScale;
\r
149 // calculate envelope
\r
151 // solve distance for non-distance lights
\r
152 if (!(iLightFlags & LIGHT_ATTEN_DISTANCE))
\r
153 //!\todo (spog) can't access global objects in a module - globals are EVIL - solution: API for querying global settings.
\r
154 fEnvelope = 131072/*g_MaxWorldCoord * 2.f*/;
\r
155 // solve distance for linear lights
\r
156 else if (iLightFlags & LIGHT_ATTEN_LINEAR)
\r
157 fEnvelope = ((fPhotons * fLinearScale) - fFalloffTolerance) / fFade;
\r
158 // solve for inverse square falloff
\r
160 fEnvelope = sqrt(fPhotons / fFalloffTolerance) /* + fRadius */ ; // Arnout radius is always 0, only for area lights
\r
165 float CalculateLightRadius(entity_t * e, bool outer)
\r
167 float fEnvelope = 0.f;
\r
168 int iSpawnFlags = atoi(ValueForKey(e, "spawnflags"));
\r
171 const char *gameFile = g_FuncTable.m_pfnGetGameFile();
\r
173 fIntensity = FloatForKey(e, "light");
\r
174 if (fIntensity == 0.f)
\r
175 fIntensity = 300.f;
\r
177 // Arnout: HACK for per-game radii - really need to move this to a per-game module
\r
178 if( !strcmp( gameFile, "sof2.game" ) || !strcmp( gameFile, "jk2.game" ) || !strcmp( gameFile, "ja.game" )) {
\r
184 if (iSpawnFlags & 2)
\r
187 fIntensity *= .25f;
\r
189 // set light scale (sof2)
\r
190 fScale = FloatForKey(e, "scale");
\r
193 fIntensity *= fScale;
\r
195 fEnvelope = fIntensity;
\r
197 float fPointScale = 7500.f;
\r
200 fEnvelope = sqrt(fIntensity * fPointScale / 48.f);
\r
202 fEnvelope = sqrt(fIntensity * fPointScale / 255.f);
\r
208 void Light_OnIntensityChanged(entity_t* e)
\r
210 e->fLightEnvelope1[0] = CalculateEnvelopeForLight(e, 1.f);
\r
211 e->fLightEnvelope1[1] = CalculateEnvelopeForLight(e, 48.f);
\r
212 e->fLightEnvelope1[2] = CalculateEnvelopeForLight(e, 255.f);
\r
214 e->fLightEnvelope2[0] = CalculateLightRadius(e, TRUE);
\r
215 e->fLightEnvelope2[1] = CalculateLightRadius(e, FALSE);
\r
218 void Light_OnKeyValueChanged(entity_t *e, const char *key, const char* value)
\r
220 if(strcmp(key,"_color") == 0)
\r
222 if (sscanf(ValueForKey(e, "_color"),"%f %f %f",
\r
223 &e->color[0], &e->color[1], &e->color[2]) != 3)
\r
224 VectorSet(e->color, 1, 1, 1);
\r
226 else if(strcmp(key,"spawnflags") == 0 ||
\r
227 strcmp(key,"fade") == 0 ||
\r
228 strcmp(key,"_light") == 0 ||
\r
229 strcmp(key,"light") == 0 ||
\r
230 strcmp(key,"scale") == 0)
\r
232 Light_OnIntensityChanged(e);
\r
236 bool Entity_IsLight(entity_t *e)
\r
238 return e->eclass != NULL && e->eclass->nShowFlags & ECLASS_LIGHT;//strncmp(ValueforKey(e, "classname"), "light") == 0
\r
241 static void DrawLightSphere(entity_t * e, int nGLState, int pref)
\r
243 const char *target = ValueForKey(e, "target");
\r
244 bool bIsSpotLight = !!target[0];
\r
245 //!\todo Write an API for modules to register preference settings, and make this preference module-specific.
\r
246 int nPasses = pref == 1 ? 3 : 2;
\r
248 g_QglTable.m_pfn_qglPushAttrib(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
\r
249 g_QglTable.m_pfn_qglDepthMask(GL_FALSE);
\r
250 g_QglTable.m_pfn_qglEnable(GL_BLEND);
\r
251 g_QglTable.m_pfn_qglBlendFunc(GL_ONE, GL_ONE);
\r
253 // Arnout: TODO: spotlight rendering
\r
254 if (!(bIsSpotLight))
\r
259 g_QglTable.m_pfn_qglColor3f(e->color[0] * .05f,
\r
260 e->color[1] * .05f,
\r
261 e->color[2] * .05f);
\r
262 DrawSphere(e->origin, e->fLightEnvelope1[0], 16, nGLState);
\r
263 DrawSphere(e->origin, e->fLightEnvelope1[1], 16, nGLState);
\r
264 DrawSphere(e->origin, e->fLightEnvelope1[2], 16, nGLState);
\r
267 g_QglTable.m_pfn_qglColor3f(e->color[0] * .15f * .95f,
\r
268 e->color[1] * .15f * .95f,
\r
269 e->color[2] * .15f * .95f);
\r
270 DrawSphere(e->origin, e->fLightEnvelope2[0], 16, nGLState);
\r
271 DrawSphere(e->origin, e->fLightEnvelope2[1], 16, nGLState);
\r
277 g_QglTable.m_pfn_qglPopAttrib();
\r
280 float F = 0.70710678f;
\r
281 // North, East, South, West
\r
282 vec3_t normals[8] = { { 0, F, F }, { F, 0, F }, { 0,-F, F }, {-F, 0, F },
\r
283 { 0, F,-F }, { F, 0,-F }, { 0,-F,-F }, {-F, 0,-F } };
\r
285 unsigned short indices[24] = { 0, 2, 3, 0, 3, 4, 0, 4, 5, 0, 5, 2,
\r
286 1, 2, 5, 1, 5, 4, 1, 4, 3, 1, 3, 2 };
\r
288 void DrawLight(entity_t* e, int nGLState, int pref, int nViewType)
\r
291 // top, bottom, tleft, tright, bright, bleft
\r
293 vec3_t vMid, vMin, vMax;
\r
294 VectorAdd(e->origin, e->eclass->mins, vMin);
\r
295 VectorAdd(e->origin, e->eclass->maxs, vMax);
\r
296 vMid[0] = (vMin[0] + vMax[0]) * 0.5;
\r
297 vMid[1] = (vMin[1] + vMax[1]) * 0.5;
\r
298 vMid[2] = (vMin[2] + vMax[2]) * 0.5;
\r
300 VectorSet(points[0], vMid[0], vMid[1], vMax[2]);
\r
301 VectorSet(points[1], vMid[0], vMid[1], vMin[2]);
\r
302 VectorSet(points[2], vMin[0], vMax[1], vMid[2]);
\r
303 VectorSet(points[3], vMax[0], vMax[1], vMid[2]);
\r
304 VectorSet(points[4], vMax[0], vMin[1], vMid[2]);
\r
305 VectorSet(points[5], vMin[0], vMin[1], vMid[2]);
\r
307 if (nGLState & DRAW_GL_LIGHTING)// && g_PrefsDlg.m_bGLLighting)
\r
309 g_QglTable.m_pfn_qglBegin(GL_TRIANGLES);// NOTE: comment to use gl_triangle_fan instead
\r
310 //g_QglTable.m_pfn_qglBegin(GL_TRIANGLE_FAN);
\r
311 g_QglTable.m_pfn_qglVertex3fv(points[0]);
\r
312 g_QglTable.m_pfn_qglVertex3fv(points[2]);
\r
313 g_QglTable.m_pfn_qglNormal3fv(normals[0]);
\r
314 g_QglTable.m_pfn_qglVertex3fv(points[3]);
\r
316 g_QglTable.m_pfn_qglVertex3fv(points[0]);//
\r
317 g_QglTable.m_pfn_qglVertex3fv(points[3]);//
\r
318 g_QglTable.m_pfn_qglNormal3fv(normals[1]);
\r
319 g_QglTable.m_pfn_qglVertex3fv(points[4]);
\r
321 g_QglTable.m_pfn_qglVertex3fv(points[0]);//
\r
322 g_QglTable.m_pfn_qglVertex3fv(points[4]);//
\r
323 g_QglTable.m_pfn_qglNormal3fv(normals[2]);
\r
324 g_QglTable.m_pfn_qglVertex3fv(points[5]);
\r
326 g_QglTable.m_pfn_qglVertex3fv(points[0]);//
\r
327 g_QglTable.m_pfn_qglVertex3fv(points[5]);//
\r
328 g_QglTable.m_pfn_qglNormal3fv(normals[3]);
\r
329 g_QglTable.m_pfn_qglVertex3fv(points[2]);
\r
331 //g_QglTable.m_pfn_qglEnd();
\r
332 //g_QglTable.m_pfn_qglBegin(GL_TRIANGLE_FAN);
\r
334 g_QglTable.m_pfn_qglVertex3fv(points[1]);
\r
335 g_QglTable.m_pfn_qglVertex3fv(points[2]);
\r
336 g_QglTable.m_pfn_qglNormal3fv(normals[7]);
\r
337 g_QglTable.m_pfn_qglVertex3fv(points[5]);
\r
339 g_QglTable.m_pfn_qglVertex3fv(points[1]);//
\r
340 g_QglTable.m_pfn_qglVertex3fv(points[5]);//
\r
341 g_QglTable.m_pfn_qglNormal3fv(normals[6]);
\r
342 g_QglTable.m_pfn_qglVertex3fv(points[4]);
\r
344 g_QglTable.m_pfn_qglVertex3fv(points[1]);//
\r
345 g_QglTable.m_pfn_qglVertex3fv(points[4]);//
\r
346 g_QglTable.m_pfn_qglNormal3fv(normals[5]);
\r
347 g_QglTable.m_pfn_qglVertex3fv(points[3]);
\r
349 g_QglTable.m_pfn_qglVertex3fv(points[1]);//
\r
350 g_QglTable.m_pfn_qglVertex3fv(points[3]);//
\r
351 g_QglTable.m_pfn_qglNormal3fv(normals[4]);
\r
352 g_QglTable.m_pfn_qglVertex3fv(points[2]);
\r
354 g_QglTable.m_pfn_qglEnd();
\r
356 else if (nGLState & DRAW_GL_FILL)
\r
359 VectorScale(e->color, 0.95, colors[0]);
\r
360 VectorScale(colors[0], 0.95, colors[1]);
\r
361 VectorScale(colors[1], 0.95, colors[2]);
\r
362 VectorScale(colors[2], 0.95, colors[3]);
\r
363 g_QglTable.m_pfn_qglBegin(GL_TRIANGLES);// NOTE: comment to use gl_triangle_fan instead
\r
364 //g_QglTable.m_pfn_qglBegin(GL_TRIANGLE_FAN);
\r
365 g_QglTable.m_pfn_qglColor3fv(colors[0]);
\r
366 g_QglTable.m_pfn_qglVertex3fv(points[0]);
\r
367 g_QglTable.m_pfn_qglVertex3fv(points[2]);
\r
368 g_QglTable.m_pfn_qglVertex3fv(points[3]);
\r
370 g_QglTable.m_pfn_qglColor3fv(colors[1]);
\r
371 g_QglTable.m_pfn_qglVertex3fv(points[0]);//
\r
372 g_QglTable.m_pfn_qglVertex3fv(points[3]);//
\r
373 g_QglTable.m_pfn_qglVertex3fv(points[4]);
\r
375 g_QglTable.m_pfn_qglColor3fv(colors[2]);
\r
376 g_QglTable.m_pfn_qglVertex3fv(points[0]);//
\r
377 g_QglTable.m_pfn_qglVertex3fv(points[4]);//
\r
378 g_QglTable.m_pfn_qglVertex3fv(points[5]);
\r
380 g_QglTable.m_pfn_qglColor3fv(colors[3]);
\r
381 g_QglTable.m_pfn_qglVertex3fv(points[0]);//
\r
382 g_QglTable.m_pfn_qglVertex3fv(points[5]);//
\r
383 g_QglTable.m_pfn_qglVertex3fv(points[2]);
\r
385 //g_QglTable.m_pfn_qglEnd();
\r
386 //g_QglTable.m_pfn_qglBegin(GL_TRIANGLE_FAN);
\r
388 g_QglTable.m_pfn_qglColor3fv(colors[0]);
\r
389 g_QglTable.m_pfn_qglVertex3fv(points[1]);
\r
390 g_QglTable.m_pfn_qglVertex3fv(points[2]);
\r
391 g_QglTable.m_pfn_qglVertex3fv(points[5]);
\r
393 g_QglTable.m_pfn_qglColor3fv(colors[1]);
\r
394 g_QglTable.m_pfn_qglVertex3fv(points[1]);//
\r
395 g_QglTable.m_pfn_qglVertex3fv(points[5]);//
\r
396 g_QglTable.m_pfn_qglVertex3fv(points[4]);
\r
398 g_QglTable.m_pfn_qglColor3fv(colors[2]);
\r
399 g_QglTable.m_pfn_qglVertex3fv(points[1]);//
\r
400 g_QglTable.m_pfn_qglVertex3fv(points[4]);//
\r
401 g_QglTable.m_pfn_qglVertex3fv(points[3]);
\r
403 g_QglTable.m_pfn_qglColor3fv(colors[3]);
\r
404 g_QglTable.m_pfn_qglVertex3fv(points[1]);//
\r
405 g_QglTable.m_pfn_qglVertex3fv(points[3]);//
\r
406 g_QglTable.m_pfn_qglVertex3fv(points[2]);
\r
408 g_QglTable.m_pfn_qglEnd();
\r
412 g_QglTable.m_pfn_qglVertexPointer(3, GL_FLOAT, 0, points);
\r
413 g_QglTable.m_pfn_qglDrawElements(GL_TRIANGLES, 24, GL_UNSIGNED_SHORT, indices);
\r
417 // NOTE: prolly not relevant until some time..
\r
418 // check for DOOM lights
\r
419 if (strlen(ValueForKey(e, "light_right")) > 0) {
\r
420 vec3_t vRight, vUp, vTarget, vTemp;
\r
421 GetVectorForKey (e, "light_right", vRight);
\r
422 GetVectorForKey (e, "light_up", vUp);
\r
423 GetVectorForKey (e, "light_target", vTarget);
\r
425 g_QglTable.m_pfn_qglColor3f(0, 1, 0);
\r
426 g_QglTable.m_pfn_qglBegin(GL_LINE_LOOP);
\r
427 VectorAdd(vTarget, e->origin, vTemp);
\r
428 VectorAdd(vTemp, vRight, vTemp);
\r
429 VectorAdd(vTemp, vUp, vTemp);
\r
430 g_QglTable.m_pfn_qglVertex3fv(e->origin);
\r
431 g_QglTable.m_pfn_qglVertex3fv(vTemp);
\r
432 VectorAdd(vTarget, e->origin, vTemp);
\r
433 VectorAdd(vTemp, vUp, vTemp);
\r
434 VectorSubtract(vTemp, vRight, vTemp);
\r
435 g_QglTable.m_pfn_qglVertex3fv(e->origin);
\r
436 g_QglTable.m_pfn_qglVertex3fv(vTemp);
\r
437 VectorAdd(vTarget, e->origin, vTemp);
\r
438 VectorAdd(vTemp, vRight, vTemp);
\r
439 VectorSubtract(vTemp, vUp, vTemp);
\r
440 g_QglTable.m_pfn_qglVertex3fv(e->origin);
\r
441 g_QglTable.m_pfn_qglVertex3fv(vTemp);
\r
442 VectorAdd(vTarget, e->origin, vTemp);
\r
443 VectorSubtract(vTemp, vUp, vTemp);
\r
444 VectorSubtract(vTemp, vRight, vTemp);
\r
445 g_QglTable.m_pfn_qglVertex3fv(e->origin);
\r
446 g_QglTable.m_pfn_qglVertex3fv(vTemp);
\r
447 g_QglTable.m_pfn_qglEnd();
\r
451 if(nGLState & DRAW_GL_FILL)
\r
453 DrawLightSphere(e, nGLState, pref);
\r
457 // Arnout: FIXME: clean this up a bit
\r
458 // now draw lighting radius stuff...
\r
461 bool bDrawSpotlightArc = false;
\r
462 int nPasses = pref == 1 ? 3 : 2;
\r
464 const char *target = ValueForKey(e, "target");
\r
465 bool bIsSpotLight = !!target[0];
\r
467 /*!\todo Spotlight..
\r
470 // find the origin of the target...
\r
471 entity_t *e = FindEntity("targetname", target);
\r
474 bDrawSpotlightArc = true;
\r
478 g_QglTable.m_pfn_qglPushAttrib(GL_LINE_BIT);
\r
479 g_QglTable.m_pfn_qglLineStipple(8, 0xAAAA);
\r
480 g_QglTable.m_pfn_qglEnable(GL_LINE_STIPPLE);
\r
482 float* envelope = (pref == 1) ? e->fLightEnvelope1 : e->fLightEnvelope2;
\r
483 for (int iPass = 0; iPass < nPasses; iPass++)
\r
485 float fRadius = envelope[iPass];
\r
487 g_QglTable.m_pfn_qglBegin(GL_LINE_LOOP);
\r
491 if (bDrawSpotlightArc)
\r
493 // I give up on this, it's beyond me
\r
503 for (i = 0; i <= 24; i++)
\r
505 ds = sin((i * 2 * Q_PI) / 24);
\r
506 dc = cos((i * 2 * Q_PI) / 24);
\r
511 g_QglTable.m_pfn_qglVertex3f(e->origin[0] + fRadius * dc,
\r
512 e->origin[1] + fRadius * ds,
\r
516 g_QglTable.m_pfn_qglVertex3f(e->origin[0] + fRadius * dc,
\r
518 e->origin[2] + fRadius * ds);
\r
521 g_QglTable.m_pfn_qglVertex3f(e->origin[0],
\r
522 e->origin[1] + fRadius * dc,
\r
523 e->origin[2] + fRadius * ds);
\r
529 g_QglTable.m_pfn_qglEnd();
\r
531 g_QglTable.m_pfn_qglPopAttrib();
\r