-\r
-#include "plugin.h"\r
-#include "entity.h"\r
-#include "light.h"\r
-\r
-void DrawSphere(vec3_t center, float radius, int sides, int nGLState)\r
-{\r
- int i, j;\r
- float dt = (float) (2 * Q_PI / (float) sides);\r
- float dp = (float) (Q_PI / (float) sides);\r
- float t, p;\r
- vec3_t v;\r
-\r
- if (radius <= 0)\r
- return;\r
-\r
- g_QglTable.m_pfn_qglBegin(GL_TRIANGLES);\r
- for (i = 0; i <= sides - 1; i++) {\r
- for (j = 0; j <= sides - 2; j++) {\r
- t = i * dt;\r
- p = (float) ((j * dp) - (Q_PI / 2));\r
-\r
- VectorPolar(v, radius, t, p);\r
- VectorAdd(v, center, v);\r
- g_QglTable.m_pfn_qglVertex3fv(v);\r
-\r
- VectorPolar(v, radius, t, p + dp);\r
- VectorAdd(v, center, v);\r
- g_QglTable.m_pfn_qglVertex3fv(v);\r
-\r
- VectorPolar(v, radius, t + dt, p + dp);\r
- VectorAdd(v, center, v);\r
- g_QglTable.m_pfn_qglVertex3fv(v);\r
-\r
- VectorPolar(v, radius, t, p);\r
- VectorAdd(v, center, v);\r
- g_QglTable.m_pfn_qglVertex3fv(v);\r
-\r
- VectorPolar(v, radius, t + dt, p + dp);\r
- VectorAdd(v, center, v);\r
- g_QglTable.m_pfn_qglVertex3fv(v);\r
-\r
- VectorPolar(v, radius, t + dt, p);\r
- VectorAdd(v, center, v);\r
- g_QglTable.m_pfn_qglVertex3fv(v);\r
- }\r
- }\r
-\r
- p = (float) ((sides - 1) * dp - (Q_PI / 2));\r
- for (i = 0; i <= sides - 1; i++) {\r
- t = i * dt;\r
-\r
- VectorPolar(v, radius, t, p);\r
- VectorAdd(v, center, v);\r
- g_QglTable.m_pfn_qglVertex3fv(v);\r
-\r
- VectorPolar(v, radius, t + dt, p + dp);\r
- VectorAdd(v, center, v);\r
- g_QglTable.m_pfn_qglVertex3fv(v);\r
-\r
- VectorPolar(v, radius, t + dt, p);\r
- VectorAdd(v, center, v);\r
- g_QglTable.m_pfn_qglVertex3fv(v);\r
- }\r
- g_QglTable.m_pfn_qglEnd();\r
-}\r
-\r
-#define LIGHT_ATTEN_LINEAR 1\r
-#define LIGHT_ATTEN_ANGLE 2\r
-#define LIGHT_ATTEN_DISTANCE 4\r
-\r
-#define LIGHT_Q3A_DEFAULT (LIGHT_ATTEN_ANGLE | LIGHT_ATTEN_DISTANCE)\r
-#define LIGHT_WOLF_DEFAULT (LIGHT_ATTEN_LINEAR | LIGHT_ATTEN_DISTANCE)\r
-\r
-float CalculateEnvelopeForLight(entity_t * e, float fFalloffTolerance)\r
-{\r
- float fEnvelope = 0.f;\r
- int iSpawnFlags = atoi(ValueForKey(e, "spawnflags"));\r
- int iLightFlags = 0;\r
- float fFade = 1.f;\r
- float fIntensity, fPhotons;\r
- float fScale;\r
- const char *gameFile = g_FuncTable.m_pfnGetGameFile();\r
-\r
- // These variables are tweakable on the q3map2 console, setting to q3map2\r
- // default here as there is no way to find out what the user actually uses\r
- // right now. Maybe move them to worldspawn?\r
- float fPointScale = 7500.f;\r
- float fLinearScale = 1.f / 8000.f;\r
- //float fFalloffTolerance = 1.f; // Need it as parameter\r
-\r
- // Arnout: HACK for per-game radii - really need to move this to a per-game module?\r
- if( !strcmp( gameFile, "wolf.game" ) || !strcmp( gameFile, "et.game" ) ) {\r
- // Spawnflags :\r
- // 1: nonlinear\r
- // 2: angle\r
-\r
- // set default flags\r
- iLightFlags = LIGHT_WOLF_DEFAULT;\r
-\r
- // inverse distance squared attenuation?\r
- if (iSpawnFlags & 1) {\r
- iLightFlags &= ~LIGHT_ATTEN_LINEAR;\r
- iLightFlags |= LIGHT_ATTEN_ANGLE;\r
- }\r
- // angle attenuate\r
- if (iSpawnFlags & 2)\r
- iLightFlags |= LIGHT_ATTEN_ANGLE;\r
- } else {\r
- // Spawnflags :\r
- // 1: linear\r
- // 2: no angle\r
-\r
- // set default flags\r
- iLightFlags = LIGHT_Q3A_DEFAULT;\r
-\r
- // linear attenuation?\r
- if (iSpawnFlags & 1) {\r
- iLightFlags |= LIGHT_ATTEN_LINEAR;\r
- iLightFlags &= ~LIGHT_ATTEN_ANGLE;\r
- }\r
- // no angle attenuate?\r
- if (iSpawnFlags & 2)\r
- iLightFlags &= ~LIGHT_ATTEN_ANGLE;\r
- }\r
-\r
- // set fade key (from wolf)\r
- if (iLightFlags & LIGHT_ATTEN_LINEAR) {\r
- fFade = FloatForKey(e, "fade");\r
- if (fFade <= 0.f)\r
- fFade = 1.f;\r
- }\r
- // set light intensity\r
- fIntensity = FloatForKey(e, "_light");\r
- if (fIntensity == 0.f)\r
- fIntensity = FloatForKey(e, "light");\r
- if (fIntensity == 0.f)\r
- fIntensity = 300.f;\r
-\r
- // set light scale (sof2)\r
- fScale = FloatForKey(e, "scale");\r
- if (fScale <= 0.f)\r
- fScale = 1.f;\r
- fIntensity *= fScale;\r
-\r
- // amount of photons\r
- fPhotons = fIntensity * fPointScale;\r
-\r
- // calculate envelope\r
-\r
- // solve distance for non-distance lights\r
- if (!(iLightFlags & LIGHT_ATTEN_DISTANCE))\r
- //!\todo (spog) can't access global objects in a module - globals are EVIL - solution: API for querying global settings.\r
- fEnvelope = 131072/*g_MaxWorldCoord * 2.f*/;\r
- // solve distance for linear lights\r
- else if (iLightFlags & LIGHT_ATTEN_LINEAR)\r
- fEnvelope = ((fPhotons * fLinearScale) - fFalloffTolerance) / fFade;\r
- // solve for inverse square falloff\r
- else\r
- fEnvelope = sqrt(fPhotons / fFalloffTolerance) /* + fRadius */ ; // Arnout radius is always 0, only for area lights\r
-\r
- return fEnvelope;\r
-}\r
-\r
-float CalculateLightRadius(entity_t * e, bool outer)\r
-{\r
- float fEnvelope = 0.f;\r
- int iSpawnFlags = atoi(ValueForKey(e, "spawnflags"));\r
- float fIntensity;\r
- float fScale;\r
- const char *gameFile = g_FuncTable.m_pfnGetGameFile();\r
-\r
- fIntensity = FloatForKey(e, "light");\r
- if (fIntensity == 0.f)\r
- fIntensity = 300.f;\r
-\r
- // Arnout: HACK for per-game radii - really need to move this to a per-game module\r
- if( !strcmp( gameFile, "sof2.game" ) || !strcmp( gameFile, "jk2.game" ) || !strcmp( gameFile, "ja.game" )) {\r
- // Spawnflags :\r
- // 1: linear\r
- // 2: noincidence\r
-\r
- if (!outer) {\r
- if (iSpawnFlags & 2)\r
- fIntensity *= .9;\r
- else\r
- fIntensity *= .25f;\r
- }\r
- // set light scale (sof2)\r
- fScale = FloatForKey(e, "scale");\r
- if (fScale <= 0.f)\r
- fScale = 1.f;\r
- fIntensity *= fScale;\r
-\r
- fEnvelope = fIntensity;\r
- } else {\r
- float fPointScale = 7500.f;\r
-\r
- if (outer)\r
- fEnvelope = sqrt(fIntensity * fPointScale / 48.f);\r
- else\r
- fEnvelope = sqrt(fIntensity * fPointScale / 255.f);\r
- }\r
-\r
- return fEnvelope;\r
-}\r
-\r
-void Light_OnIntensityChanged(entity_t* e)\r
-{\r
- e->fLightEnvelope1[0] = CalculateEnvelopeForLight(e, 1.f);\r
- e->fLightEnvelope1[1] = CalculateEnvelopeForLight(e, 48.f);\r
- e->fLightEnvelope1[2] = CalculateEnvelopeForLight(e, 255.f);\r
-\r
- e->fLightEnvelope2[0] = CalculateLightRadius(e, TRUE);\r
- e->fLightEnvelope2[1] = CalculateLightRadius(e, FALSE);\r
-}\r
-\r
-void Light_OnKeyValueChanged(entity_t *e, const char *key, const char* value)\r
-{\r
- if(strcmp(key,"_color") == 0)\r
- {\r
- if (sscanf(ValueForKey(e, "_color"),"%f %f %f",\r
- &e->color[0], &e->color[1], &e->color[2]) != 3)\r
- VectorSet(e->color, 1, 1, 1);\r
- }\r
- else if(strcmp(key,"spawnflags") == 0 ||\r
- strcmp(key,"fade") == 0 ||\r
- strcmp(key,"_light") == 0 ||\r
- strcmp(key,"light") == 0 ||\r
- strcmp(key,"scale") == 0)\r
- {\r
- Light_OnIntensityChanged(e);\r
- }\r
-}\r
-\r
-bool Entity_IsLight(entity_t *e)\r
-{\r
- return e->eclass != NULL && e->eclass->nShowFlags & ECLASS_LIGHT;//strncmp(ValueforKey(e, "classname"), "light") == 0\r
-}\r
-\r
-static void DrawLightSphere(entity_t * e, int nGLState, int pref)\r
-{\r
- const char *target = ValueForKey(e, "target");\r
- bool bIsSpotLight = !!target[0];\r
- //!\todo Write an API for modules to register preference settings, and make this preference module-specific.\r
- int nPasses = pref == 1 ? 3 : 2;\r
-\r
- g_QglTable.m_pfn_qglPushAttrib(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);\r
- g_QglTable.m_pfn_qglDepthMask(GL_FALSE);\r
- g_QglTable.m_pfn_qglEnable(GL_BLEND);\r
- g_QglTable.m_pfn_qglBlendFunc(GL_ONE, GL_ONE);\r
-\r
- // Arnout: TODO: spotlight rendering\r
- if (!(bIsSpotLight))\r
- {\r
- switch(pref)\r
- {\r
- case 1:\r
- g_QglTable.m_pfn_qglColor3f(e->color[0] * .05f,\r
- e->color[1] * .05f,\r
- e->color[2] * .05f);\r
- DrawSphere(e->origin, e->fLightEnvelope1[0], 16, nGLState);\r
- DrawSphere(e->origin, e->fLightEnvelope1[1], 16, nGLState);\r
- DrawSphere(e->origin, e->fLightEnvelope1[2], 16, nGLState);\r
- break;\r
- case 2:\r
- g_QglTable.m_pfn_qglColor3f(e->color[0] * .15f * .95f,\r
- e->color[1] * .15f * .95f,\r
- e->color[2] * .15f * .95f);\r
- DrawSphere(e->origin, e->fLightEnvelope2[0], 16, nGLState);\r
- DrawSphere(e->origin, e->fLightEnvelope2[1], 16, nGLState);\r
- break;\r
-\r
- }\r
- }\r
-\r
- g_QglTable.m_pfn_qglPopAttrib();\r
-}\r
-\r
-float F = 0.70710678f;\r
-// North, East, South, West\r
-vec3_t normals[8] = { { 0, F, F }, { F, 0, F }, { 0,-F, F }, {-F, 0, F },\r
- { 0, F,-F }, { F, 0,-F }, { 0,-F,-F }, {-F, 0,-F } };\r
-\r
-unsigned short indices[24] = { 0, 2, 3, 0, 3, 4, 0, 4, 5, 0, 5, 2,\r
- 1, 2, 5, 1, 5, 4, 1, 4, 3, 1, 3, 2 };\r
-\r
-void DrawLight(entity_t* e, int nGLState, int pref, int nViewType)\r
-{\r
- int i;\r
- // top, bottom, tleft, tright, bright, bleft\r
- vec3_t points[6];\r
- vec3_t vMid, vMin, vMax;\r
- VectorAdd(e->origin, e->eclass->mins, vMin);\r
- VectorAdd(e->origin, e->eclass->maxs, vMax);\r
- vMid[0] = (vMin[0] + vMax[0]) * 0.5;\r
- vMid[1] = (vMin[1] + vMax[1]) * 0.5;\r
- vMid[2] = (vMin[2] + vMax[2]) * 0.5;\r
-\r
- VectorSet(points[0], vMid[0], vMid[1], vMax[2]);\r
- VectorSet(points[1], vMid[0], vMid[1], vMin[2]);\r
- VectorSet(points[2], vMin[0], vMax[1], vMid[2]);\r
- VectorSet(points[3], vMax[0], vMax[1], vMid[2]);\r
- VectorSet(points[4], vMax[0], vMin[1], vMid[2]);\r
- VectorSet(points[5], vMin[0], vMin[1], vMid[2]);\r
-\r
- if (nGLState & DRAW_GL_LIGHTING)// && g_PrefsDlg.m_bGLLighting)\r
- {\r
- g_QglTable.m_pfn_qglBegin(GL_TRIANGLES);// NOTE: comment to use gl_triangle_fan instead\r
- //g_QglTable.m_pfn_qglBegin(GL_TRIANGLE_FAN);\r
- g_QglTable.m_pfn_qglVertex3fv(points[0]);\r
- g_QglTable.m_pfn_qglVertex3fv(points[2]);\r
- g_QglTable.m_pfn_qglNormal3fv(normals[0]);\r
- g_QglTable.m_pfn_qglVertex3fv(points[3]);\r
-\r
- g_QglTable.m_pfn_qglVertex3fv(points[0]);//\r
- g_QglTable.m_pfn_qglVertex3fv(points[3]);//\r
- g_QglTable.m_pfn_qglNormal3fv(normals[1]);\r
- g_QglTable.m_pfn_qglVertex3fv(points[4]);\r
-\r
- g_QglTable.m_pfn_qglVertex3fv(points[0]);//\r
- g_QglTable.m_pfn_qglVertex3fv(points[4]);//\r
- g_QglTable.m_pfn_qglNormal3fv(normals[2]);\r
- g_QglTable.m_pfn_qglVertex3fv(points[5]);\r
-\r
- g_QglTable.m_pfn_qglVertex3fv(points[0]);//\r
- g_QglTable.m_pfn_qglVertex3fv(points[5]);//\r
- g_QglTable.m_pfn_qglNormal3fv(normals[3]);\r
- g_QglTable.m_pfn_qglVertex3fv(points[2]);\r
-\r
- //g_QglTable.m_pfn_qglEnd();\r
- //g_QglTable.m_pfn_qglBegin(GL_TRIANGLE_FAN);\r
-\r
- g_QglTable.m_pfn_qglVertex3fv(points[1]);\r
- g_QglTable.m_pfn_qglVertex3fv(points[2]);\r
- g_QglTable.m_pfn_qglNormal3fv(normals[7]);\r
- g_QglTable.m_pfn_qglVertex3fv(points[5]);\r
-\r
- g_QglTable.m_pfn_qglVertex3fv(points[1]);//\r
- g_QglTable.m_pfn_qglVertex3fv(points[5]);//\r
- g_QglTable.m_pfn_qglNormal3fv(normals[6]);\r
- g_QglTable.m_pfn_qglVertex3fv(points[4]);\r
-\r
- g_QglTable.m_pfn_qglVertex3fv(points[1]);//\r
- g_QglTable.m_pfn_qglVertex3fv(points[4]);//\r
- g_QglTable.m_pfn_qglNormal3fv(normals[5]);\r
- g_QglTable.m_pfn_qglVertex3fv(points[3]);\r
-\r
- g_QglTable.m_pfn_qglVertex3fv(points[1]);//\r
- g_QglTable.m_pfn_qglVertex3fv(points[3]);//\r
- g_QglTable.m_pfn_qglNormal3fv(normals[4]);\r
- g_QglTable.m_pfn_qglVertex3fv(points[2]);\r
-\r
- g_QglTable.m_pfn_qglEnd();\r
- }\r
- else if (nGLState & DRAW_GL_FILL)\r
- {\r
- vec3_t colors[4];\r
- VectorScale(e->color, 0.95, colors[0]);\r
- VectorScale(colors[0], 0.95, colors[1]);\r
- VectorScale(colors[1], 0.95, colors[2]);\r
- VectorScale(colors[2], 0.95, colors[3]);\r
- g_QglTable.m_pfn_qglBegin(GL_TRIANGLES);// NOTE: comment to use gl_triangle_fan instead\r
- //g_QglTable.m_pfn_qglBegin(GL_TRIANGLE_FAN);\r
- g_QglTable.m_pfn_qglColor3fv(colors[0]);\r
- g_QglTable.m_pfn_qglVertex3fv(points[0]);\r
- g_QglTable.m_pfn_qglVertex3fv(points[2]);\r
- g_QglTable.m_pfn_qglVertex3fv(points[3]);\r
-\r
- g_QglTable.m_pfn_qglColor3fv(colors[1]);\r
- g_QglTable.m_pfn_qglVertex3fv(points[0]);//\r
- g_QglTable.m_pfn_qglVertex3fv(points[3]);//\r
- g_QglTable.m_pfn_qglVertex3fv(points[4]);\r
-\r
- g_QglTable.m_pfn_qglColor3fv(colors[2]);\r
- g_QglTable.m_pfn_qglVertex3fv(points[0]);//\r
- g_QglTable.m_pfn_qglVertex3fv(points[4]);//\r
- g_QglTable.m_pfn_qglVertex3fv(points[5]);\r
-\r
- g_QglTable.m_pfn_qglColor3fv(colors[3]);\r
- g_QglTable.m_pfn_qglVertex3fv(points[0]);//\r
- g_QglTable.m_pfn_qglVertex3fv(points[5]);//\r
- g_QglTable.m_pfn_qglVertex3fv(points[2]);\r
-\r
- //g_QglTable.m_pfn_qglEnd();\r
- //g_QglTable.m_pfn_qglBegin(GL_TRIANGLE_FAN);\r
-\r
- g_QglTable.m_pfn_qglColor3fv(colors[0]);\r
- g_QglTable.m_pfn_qglVertex3fv(points[1]);\r
- g_QglTable.m_pfn_qglVertex3fv(points[2]);\r
- g_QglTable.m_pfn_qglVertex3fv(points[5]);\r
-\r
- g_QglTable.m_pfn_qglColor3fv(colors[1]);\r
- g_QglTable.m_pfn_qglVertex3fv(points[1]);//\r
- g_QglTable.m_pfn_qglVertex3fv(points[5]);//\r
- g_QglTable.m_pfn_qglVertex3fv(points[4]);\r
-\r
- g_QglTable.m_pfn_qglColor3fv(colors[2]);\r
- g_QglTable.m_pfn_qglVertex3fv(points[1]);//\r
- g_QglTable.m_pfn_qglVertex3fv(points[4]);//\r
- g_QglTable.m_pfn_qglVertex3fv(points[3]);\r
-\r
- g_QglTable.m_pfn_qglColor3fv(colors[3]);\r
- g_QglTable.m_pfn_qglVertex3fv(points[1]);//\r
- g_QglTable.m_pfn_qglVertex3fv(points[3]);//\r
- g_QglTable.m_pfn_qglVertex3fv(points[2]);\r
-\r
- g_QglTable.m_pfn_qglEnd();\r
- }\r
- else\r
- {\r
- g_QglTable.m_pfn_qglVertexPointer(3, GL_FLOAT, 0, points);\r
- g_QglTable.m_pfn_qglDrawElements(GL_TRIANGLES, 24, GL_UNSIGNED_SHORT, indices);\r
- }\r
-\r
-\r
- // NOTE: prolly not relevant until some time..\r
- // check for DOOM lights\r
- if (strlen(ValueForKey(e, "light_right")) > 0) {\r
- vec3_t vRight, vUp, vTarget, vTemp;\r
- GetVectorForKey (e, "light_right", vRight);\r
- GetVectorForKey (e, "light_up", vUp);\r
- GetVectorForKey (e, "light_target", vTarget);\r
-\r
- g_QglTable.m_pfn_qglColor3f(0, 1, 0);\r
- g_QglTable.m_pfn_qglBegin(GL_LINE_LOOP);\r
- VectorAdd(vTarget, e->origin, vTemp);\r
- VectorAdd(vTemp, vRight, vTemp);\r
- VectorAdd(vTemp, vUp, vTemp);\r
- g_QglTable.m_pfn_qglVertex3fv(e->origin);\r
- g_QglTable.m_pfn_qglVertex3fv(vTemp);\r
- VectorAdd(vTarget, e->origin, vTemp);\r
- VectorAdd(vTemp, vUp, vTemp);\r
- VectorSubtract(vTemp, vRight, vTemp);\r
- g_QglTable.m_pfn_qglVertex3fv(e->origin);\r
- g_QglTable.m_pfn_qglVertex3fv(vTemp);\r
- VectorAdd(vTarget, e->origin, vTemp);\r
- VectorAdd(vTemp, vRight, vTemp);\r
- VectorSubtract(vTemp, vUp, vTemp);\r
- g_QglTable.m_pfn_qglVertex3fv(e->origin);\r
- g_QglTable.m_pfn_qglVertex3fv(vTemp);\r
- VectorAdd(vTarget, e->origin, vTemp);\r
- VectorSubtract(vTemp, vUp, vTemp);\r
- VectorSubtract(vTemp, vRight, vTemp);\r
- g_QglTable.m_pfn_qglVertex3fv(e->origin);\r
- g_QglTable.m_pfn_qglVertex3fv(vTemp);\r
- g_QglTable.m_pfn_qglEnd();\r
-\r
- }\r
-\r
- if(nGLState & DRAW_GL_FILL)\r
- {\r
- DrawLightSphere(e, nGLState, pref);\r
- }\r
- else\r
- {\r
- // Arnout: FIXME: clean this up a bit\r
- // now draw lighting radius stuff...\r
- if (pref)\r
- {\r
- bool bDrawSpotlightArc = false;\r
- int nPasses = pref == 1 ? 3 : 2;\r
-\r
- const char *target = ValueForKey(e, "target");\r
- bool bIsSpotLight = !!target[0];\r
-\r
- /*!\todo Spotlight..\r
- if (bIsSpotLight)\r
- {\r
- // find the origin of the target...\r
- entity_t *e = FindEntity("targetname", target);\r
-\r
- if (e)\r
- bDrawSpotlightArc = true;\r
- }\r
- */\r
-\r
- g_QglTable.m_pfn_qglPushAttrib(GL_LINE_BIT);\r
- g_QglTable.m_pfn_qglLineStipple(8, 0xAAAA);\r
- g_QglTable.m_pfn_qglEnable(GL_LINE_STIPPLE);\r
-\r
- float* envelope = (pref == 1) ? e->fLightEnvelope1 : e->fLightEnvelope2; \r
- for (int iPass = 0; iPass < nPasses; iPass++)\r
- {\r
- float fRadius = envelope[iPass];\r
-\r
- g_QglTable.m_pfn_qglBegin(GL_LINE_LOOP);\r
-\r
- if (bIsSpotLight)\r
- {\r
- if (bDrawSpotlightArc)\r
- {\r
- // I give up on this, it's beyond me\r
- }\r
- }\r
- else\r
- {\r
- if (fRadius > 0)\r
- {\r
- int i;\r
- float ds, dc;\r
-\r
- for (i = 0; i <= 24; i++)\r
- {\r
- ds = sin((i * 2 * Q_PI) / 24);\r
- dc = cos((i * 2 * Q_PI) / 24);\r
-\r
- switch (nViewType)\r
- {\r
- case 2:\r
- g_QglTable.m_pfn_qglVertex3f(e->origin[0] + fRadius * dc,\r
- e->origin[1] + fRadius * ds,\r
- e->origin[2]);\r
- break;\r
- case 1:\r
- g_QglTable.m_pfn_qglVertex3f(e->origin[0] + fRadius * dc,\r
- e->origin[1],\r
- e->origin[2] + fRadius * ds);\r
- break;\r
- case 0:\r
- g_QglTable.m_pfn_qglVertex3f(e->origin[0],\r
- e->origin[1] + fRadius * dc,\r
- e->origin[2] + fRadius * ds);\r
- break;\r
- }\r
- }\r
- }\r
- }\r
- g_QglTable.m_pfn_qglEnd();\r
- }\r
- g_QglTable.m_pfn_qglPopAttrib();\r
- }\r
- }\r
-}\r
-\r
-\r
+
+#include "plugin.h"
+#include "entity.h"
+#include "light.h"
+
+void DrawSphere(vec3_t center, float radius, int sides, int nGLState)
+{
+ int i, j;
+ float dt = (float) (2 * Q_PI / (float) sides);
+ float dp = (float) (Q_PI / (float) sides);
+ float t, p;
+ vec3_t v;
+
+ if (radius <= 0)
+ return;
+
+ g_QglTable.m_pfn_qglBegin(GL_TRIANGLES);
+ for (i = 0; i <= sides - 1; i++) {
+ for (j = 0; j <= sides - 2; j++) {
+ t = i * dt;
+ p = (float) ((j * dp) - (Q_PI / 2));
+
+ VectorPolar(v, radius, t, p);
+ VectorAdd(v, center, v);
+ g_QglTable.m_pfn_qglVertex3fv(v);
+
+ VectorPolar(v, radius, t, p + dp);
+ VectorAdd(v, center, v);
+ g_QglTable.m_pfn_qglVertex3fv(v);
+
+ VectorPolar(v, radius, t + dt, p + dp);
+ VectorAdd(v, center, v);
+ g_QglTable.m_pfn_qglVertex3fv(v);
+
+ VectorPolar(v, radius, t, p);
+ VectorAdd(v, center, v);
+ g_QglTable.m_pfn_qglVertex3fv(v);
+
+ VectorPolar(v, radius, t + dt, p + dp);
+ VectorAdd(v, center, v);
+ g_QglTable.m_pfn_qglVertex3fv(v);
+
+ VectorPolar(v, radius, t + dt, p);
+ VectorAdd(v, center, v);
+ g_QglTable.m_pfn_qglVertex3fv(v);
+ }
+ }
+
+ p = (float) ((sides - 1) * dp - (Q_PI / 2));
+ for (i = 0; i <= sides - 1; i++) {
+ t = i * dt;
+
+ VectorPolar(v, radius, t, p);
+ VectorAdd(v, center, v);
+ g_QglTable.m_pfn_qglVertex3fv(v);
+
+ VectorPolar(v, radius, t + dt, p + dp);
+ VectorAdd(v, center, v);
+ g_QglTable.m_pfn_qglVertex3fv(v);
+
+ VectorPolar(v, radius, t + dt, p);
+ VectorAdd(v, center, v);
+ g_QglTable.m_pfn_qglVertex3fv(v);
+ }
+ g_QglTable.m_pfn_qglEnd();
+}
+
+#define LIGHT_ATTEN_LINEAR 1
+#define LIGHT_ATTEN_ANGLE 2
+#define LIGHT_ATTEN_DISTANCE 4
+
+#define LIGHT_Q3A_DEFAULT (LIGHT_ATTEN_ANGLE | LIGHT_ATTEN_DISTANCE)
+#define LIGHT_WOLF_DEFAULT (LIGHT_ATTEN_LINEAR | LIGHT_ATTEN_DISTANCE)
+
+float CalculateEnvelopeForLight(entity_t * e, float fFalloffTolerance)
+{
+ float fEnvelope = 0.f;
+ int iSpawnFlags = atoi(ValueForKey(e, "spawnflags"));
+ int iLightFlags = 0;
+ float fFade = 1.f;
+ float fIntensity, fPhotons;
+ float fScale;
+ const char *gameFile = g_FuncTable.m_pfnGetGameFile();
+
+ // These variables are tweakable on the q3map2 console, setting to q3map2
+ // default here as there is no way to find out what the user actually uses
+ // right now. Maybe move them to worldspawn?
+ float fPointScale = 7500.f;
+ float fLinearScale = 1.f / 8000.f;
+ //float fFalloffTolerance = 1.f; // Need it as parameter
+
+ // Arnout: HACK for per-game radii - really need to move this to a per-game module?
+ if( !strcmp( gameFile, "wolf.game" ) || !strcmp( gameFile, "et.game" ) ) {
+ // Spawnflags :
+ // 1: nonlinear
+ // 2: angle
+
+ // set default flags
+ iLightFlags = LIGHT_WOLF_DEFAULT;
+
+ // inverse distance squared attenuation?
+ if (iSpawnFlags & 1) {
+ iLightFlags &= ~LIGHT_ATTEN_LINEAR;
+ iLightFlags |= LIGHT_ATTEN_ANGLE;
+ }
+ // angle attenuate
+ if (iSpawnFlags & 2)
+ iLightFlags |= LIGHT_ATTEN_ANGLE;
+ } else {
+ // Spawnflags :
+ // 1: linear
+ // 2: no angle
+
+ // set default flags
+ iLightFlags = LIGHT_Q3A_DEFAULT;
+
+ // linear attenuation?
+ if (iSpawnFlags & 1) {
+ iLightFlags |= LIGHT_ATTEN_LINEAR;
+ iLightFlags &= ~LIGHT_ATTEN_ANGLE;
+ }
+ // no angle attenuate?
+ if (iSpawnFlags & 2)
+ iLightFlags &= ~LIGHT_ATTEN_ANGLE;
+ }
+
+ // set fade key (from wolf)
+ if (iLightFlags & LIGHT_ATTEN_LINEAR) {
+ fFade = FloatForKey(e, "fade");
+ if (fFade <= 0.f)
+ fFade = 1.f;
+ }
+ // set light intensity
+ fIntensity = FloatForKey(e, "_light");
+ if (fIntensity == 0.f)
+ fIntensity = FloatForKey(e, "light");
+ if (fIntensity == 0.f)
+ fIntensity = 300.f;
+
+ // set light scale (sof2)
+ fScale = FloatForKey(e, "scale");
+ if (fScale <= 0.f)
+ fScale = 1.f;
+ fIntensity *= fScale;
+
+ // amount of photons
+ fPhotons = fIntensity * fPointScale;
+
+ // calculate envelope
+
+ // solve distance for non-distance lights
+ if (!(iLightFlags & LIGHT_ATTEN_DISTANCE))
+ //!\todo (spog) can't access global objects in a module - globals are EVIL - solution: API for querying global settings.
+ fEnvelope = 131072/*g_MaxWorldCoord * 2.f*/;
+ // solve distance for linear lights
+ else if (iLightFlags & LIGHT_ATTEN_LINEAR)
+ fEnvelope = ((fPhotons * fLinearScale) - fFalloffTolerance) / fFade;
+ // solve for inverse square falloff
+ else
+ fEnvelope = sqrt(fPhotons / fFalloffTolerance) /* + fRadius */ ; // Arnout radius is always 0, only for area lights
+
+ return fEnvelope;
+}
+
+float CalculateLightRadius(entity_t * e, bool outer)
+{
+ float fEnvelope = 0.f;
+ int iSpawnFlags = atoi(ValueForKey(e, "spawnflags"));
+ float fIntensity;
+ float fScale;
+ const char *gameFile = g_FuncTable.m_pfnGetGameFile();
+
+ fIntensity = FloatForKey(e, "light");
+ if (fIntensity == 0.f)
+ fIntensity = 300.f;
+
+ // Arnout: HACK for per-game radii - really need to move this to a per-game module
+ if( !strcmp( gameFile, "sof2.game" ) || !strcmp( gameFile, "jk2.game" ) || !strcmp( gameFile, "ja.game" )) {
+ // Spawnflags :
+ // 1: linear
+ // 2: noincidence
+
+ if (!outer) {
+ if (iSpawnFlags & 2)
+ fIntensity *= .9;
+ else
+ fIntensity *= .25f;
+ }
+ // set light scale (sof2)
+ fScale = FloatForKey(e, "scale");
+ if (fScale <= 0.f)
+ fScale = 1.f;
+ fIntensity *= fScale;
+
+ fEnvelope = fIntensity;
+ } else {
+ float fPointScale = 7500.f;
+
+ if (outer)
+ fEnvelope = sqrt(fIntensity * fPointScale / 48.f);
+ else
+ fEnvelope = sqrt(fIntensity * fPointScale / 255.f);
+ }
+
+ return fEnvelope;
+}
+
+void Light_OnIntensityChanged(entity_t* e)
+{
+ e->fLightEnvelope1[0] = CalculateEnvelopeForLight(e, 1.f);
+ e->fLightEnvelope1[1] = CalculateEnvelopeForLight(e, 48.f);
+ e->fLightEnvelope1[2] = CalculateEnvelopeForLight(e, 255.f);
+
+ e->fLightEnvelope2[0] = CalculateLightRadius(e, TRUE);
+ e->fLightEnvelope2[1] = CalculateLightRadius(e, FALSE);
+}
+
+void Light_OnKeyValueChanged(entity_t *e, const char *key, const char* value)
+{
+ if(strcmp(key,"_color") == 0)
+ {
+ if (sscanf(ValueForKey(e, "_color"),"%f %f %f",
+ &e->color[0], &e->color[1], &e->color[2]) != 3)
+ VectorSet(e->color, 1, 1, 1);
+ }
+ else if(strcmp(key,"spawnflags") == 0 ||
+ strcmp(key,"fade") == 0 ||
+ strcmp(key,"_light") == 0 ||
+ strcmp(key,"light") == 0 ||
+ strcmp(key,"scale") == 0)
+ {
+ Light_OnIntensityChanged(e);
+ }
+}
+
+bool Entity_IsLight(entity_t *e)
+{
+ return e->eclass != NULL && e->eclass->nShowFlags & ECLASS_LIGHT;//strncmp(ValueforKey(e, "classname"), "light") == 0
+}
+
+static void DrawLightSphere(entity_t * e, int nGLState, int pref)
+{
+ const char *target = ValueForKey(e, "target");
+ bool bIsSpotLight = !!target[0];
+ //!\todo Write an API for modules to register preference settings, and make this preference module-specific.
+ int nPasses = pref == 1 ? 3 : 2;
+
+ g_QglTable.m_pfn_qglPushAttrib(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+ g_QglTable.m_pfn_qglDepthMask(GL_FALSE);
+ g_QglTable.m_pfn_qglEnable(GL_BLEND);
+ g_QglTable.m_pfn_qglBlendFunc(GL_ONE, GL_ONE);
+
+ // Arnout: TODO: spotlight rendering
+ if (!(bIsSpotLight))
+ {
+ switch(pref)
+ {
+ case 1:
+ g_QglTable.m_pfn_qglColor3f(e->color[0] * .05f,
+ e->color[1] * .05f,
+ e->color[2] * .05f);
+ DrawSphere(e->origin, e->fLightEnvelope1[0], 16, nGLState);
+ DrawSphere(e->origin, e->fLightEnvelope1[1], 16, nGLState);
+ DrawSphere(e->origin, e->fLightEnvelope1[2], 16, nGLState);
+ break;
+ case 2:
+ g_QglTable.m_pfn_qglColor3f(e->color[0] * .15f * .95f,
+ e->color[1] * .15f * .95f,
+ e->color[2] * .15f * .95f);
+ DrawSphere(e->origin, e->fLightEnvelope2[0], 16, nGLState);
+ DrawSphere(e->origin, e->fLightEnvelope2[1], 16, nGLState);
+ break;
+
+ }
+ }
+
+ g_QglTable.m_pfn_qglPopAttrib();
+}
+
+float F = 0.70710678f;
+// North, East, South, West
+vec3_t normals[8] = { { 0, F, F }, { F, 0, F }, { 0,-F, F }, {-F, 0, F },
+ { 0, F,-F }, { F, 0,-F }, { 0,-F,-F }, {-F, 0,-F } };
+
+unsigned short indices[24] = { 0, 2, 3, 0, 3, 4, 0, 4, 5, 0, 5, 2,
+ 1, 2, 5, 1, 5, 4, 1, 4, 3, 1, 3, 2 };
+
+void DrawLight(entity_t* e, int nGLState, int pref, int nViewType)
+{
+ int i;
+ // top, bottom, tleft, tright, bright, bleft
+ vec3_t points[6];
+ vec3_t vMid, vMin, vMax;
+ VectorAdd(e->origin, e->eclass->mins, vMin);
+ VectorAdd(e->origin, e->eclass->maxs, vMax);
+ vMid[0] = (vMin[0] + vMax[0]) * 0.5;
+ vMid[1] = (vMin[1] + vMax[1]) * 0.5;
+ vMid[2] = (vMin[2] + vMax[2]) * 0.5;
+
+ VectorSet(points[0], vMid[0], vMid[1], vMax[2]);
+ VectorSet(points[1], vMid[0], vMid[1], vMin[2]);
+ VectorSet(points[2], vMin[0], vMax[1], vMid[2]);
+ VectorSet(points[3], vMax[0], vMax[1], vMid[2]);
+ VectorSet(points[4], vMax[0], vMin[1], vMid[2]);
+ VectorSet(points[5], vMin[0], vMin[1], vMid[2]);
+
+ if (nGLState & DRAW_GL_LIGHTING)// && g_PrefsDlg.m_bGLLighting)
+ {
+ g_QglTable.m_pfn_qglBegin(GL_TRIANGLES);// NOTE: comment to use gl_triangle_fan instead
+ //g_QglTable.m_pfn_qglBegin(GL_TRIANGLE_FAN);
+ g_QglTable.m_pfn_qglVertex3fv(points[0]);
+ g_QglTable.m_pfn_qglVertex3fv(points[2]);
+ g_QglTable.m_pfn_qglNormal3fv(normals[0]);
+ g_QglTable.m_pfn_qglVertex3fv(points[3]);
+
+ g_QglTable.m_pfn_qglVertex3fv(points[0]);//
+ g_QglTable.m_pfn_qglVertex3fv(points[3]);//
+ g_QglTable.m_pfn_qglNormal3fv(normals[1]);
+ g_QglTable.m_pfn_qglVertex3fv(points[4]);
+
+ g_QglTable.m_pfn_qglVertex3fv(points[0]);//
+ g_QglTable.m_pfn_qglVertex3fv(points[4]);//
+ g_QglTable.m_pfn_qglNormal3fv(normals[2]);
+ g_QglTable.m_pfn_qglVertex3fv(points[5]);
+
+ g_QglTable.m_pfn_qglVertex3fv(points[0]);//
+ g_QglTable.m_pfn_qglVertex3fv(points[5]);//
+ g_QglTable.m_pfn_qglNormal3fv(normals[3]);
+ g_QglTable.m_pfn_qglVertex3fv(points[2]);
+
+ //g_QglTable.m_pfn_qglEnd();
+ //g_QglTable.m_pfn_qglBegin(GL_TRIANGLE_FAN);
+
+ g_QglTable.m_pfn_qglVertex3fv(points[1]);
+ g_QglTable.m_pfn_qglVertex3fv(points[2]);
+ g_QglTable.m_pfn_qglNormal3fv(normals[7]);
+ g_QglTable.m_pfn_qglVertex3fv(points[5]);
+
+ g_QglTable.m_pfn_qglVertex3fv(points[1]);//
+ g_QglTable.m_pfn_qglVertex3fv(points[5]);//
+ g_QglTable.m_pfn_qglNormal3fv(normals[6]);
+ g_QglTable.m_pfn_qglVertex3fv(points[4]);
+
+ g_QglTable.m_pfn_qglVertex3fv(points[1]);//
+ g_QglTable.m_pfn_qglVertex3fv(points[4]);//
+ g_QglTable.m_pfn_qglNormal3fv(normals[5]);
+ g_QglTable.m_pfn_qglVertex3fv(points[3]);
+
+ g_QglTable.m_pfn_qglVertex3fv(points[1]);//
+ g_QglTable.m_pfn_qglVertex3fv(points[3]);//
+ g_QglTable.m_pfn_qglNormal3fv(normals[4]);
+ g_QglTable.m_pfn_qglVertex3fv(points[2]);
+
+ g_QglTable.m_pfn_qglEnd();
+ }
+ else if (nGLState & DRAW_GL_FILL)
+ {
+ vec3_t colors[4];
+ VectorScale(e->color, 0.95, colors[0]);
+ VectorScale(colors[0], 0.95, colors[1]);
+ VectorScale(colors[1], 0.95, colors[2]);
+ VectorScale(colors[2], 0.95, colors[3]);
+ g_QglTable.m_pfn_qglBegin(GL_TRIANGLES);// NOTE: comment to use gl_triangle_fan instead
+ //g_QglTable.m_pfn_qglBegin(GL_TRIANGLE_FAN);
+ g_QglTable.m_pfn_qglColor3fv(colors[0]);
+ g_QglTable.m_pfn_qglVertex3fv(points[0]);
+ g_QglTable.m_pfn_qglVertex3fv(points[2]);
+ g_QglTable.m_pfn_qglVertex3fv(points[3]);
+
+ g_QglTable.m_pfn_qglColor3fv(colors[1]);
+ g_QglTable.m_pfn_qglVertex3fv(points[0]);//
+ g_QglTable.m_pfn_qglVertex3fv(points[3]);//
+ g_QglTable.m_pfn_qglVertex3fv(points[4]);
+
+ g_QglTable.m_pfn_qglColor3fv(colors[2]);
+ g_QglTable.m_pfn_qglVertex3fv(points[0]);//
+ g_QglTable.m_pfn_qglVertex3fv(points[4]);//
+ g_QglTable.m_pfn_qglVertex3fv(points[5]);
+
+ g_QglTable.m_pfn_qglColor3fv(colors[3]);
+ g_QglTable.m_pfn_qglVertex3fv(points[0]);//
+ g_QglTable.m_pfn_qglVertex3fv(points[5]);//
+ g_QglTable.m_pfn_qglVertex3fv(points[2]);
+
+ //g_QglTable.m_pfn_qglEnd();
+ //g_QglTable.m_pfn_qglBegin(GL_TRIANGLE_FAN);
+
+ g_QglTable.m_pfn_qglColor3fv(colors[0]);
+ g_QglTable.m_pfn_qglVertex3fv(points[1]);
+ g_QglTable.m_pfn_qglVertex3fv(points[2]);
+ g_QglTable.m_pfn_qglVertex3fv(points[5]);
+
+ g_QglTable.m_pfn_qglColor3fv(colors[1]);
+ g_QglTable.m_pfn_qglVertex3fv(points[1]);//
+ g_QglTable.m_pfn_qglVertex3fv(points[5]);//
+ g_QglTable.m_pfn_qglVertex3fv(points[4]);
+
+ g_QglTable.m_pfn_qglColor3fv(colors[2]);
+ g_QglTable.m_pfn_qglVertex3fv(points[1]);//
+ g_QglTable.m_pfn_qglVertex3fv(points[4]);//
+ g_QglTable.m_pfn_qglVertex3fv(points[3]);
+
+ g_QglTable.m_pfn_qglColor3fv(colors[3]);
+ g_QglTable.m_pfn_qglVertex3fv(points[1]);//
+ g_QglTable.m_pfn_qglVertex3fv(points[3]);//
+ g_QglTable.m_pfn_qglVertex3fv(points[2]);
+
+ g_QglTable.m_pfn_qglEnd();
+ }
+ else
+ {
+ g_QglTable.m_pfn_qglVertexPointer(3, GL_FLOAT, 0, points);
+ g_QglTable.m_pfn_qglDrawElements(GL_TRIANGLES, 24, GL_UNSIGNED_SHORT, indices);
+ }
+
+
+ // NOTE: prolly not relevant until some time..
+ // check for DOOM lights
+ if (strlen(ValueForKey(e, "light_right")) > 0) {
+ vec3_t vRight, vUp, vTarget, vTemp;
+ GetVectorForKey (e, "light_right", vRight);
+ GetVectorForKey (e, "light_up", vUp);
+ GetVectorForKey (e, "light_target", vTarget);
+
+ g_QglTable.m_pfn_qglColor3f(0, 1, 0);
+ g_QglTable.m_pfn_qglBegin(GL_LINE_LOOP);
+ VectorAdd(vTarget, e->origin, vTemp);
+ VectorAdd(vTemp, vRight, vTemp);
+ VectorAdd(vTemp, vUp, vTemp);
+ g_QglTable.m_pfn_qglVertex3fv(e->origin);
+ g_QglTable.m_pfn_qglVertex3fv(vTemp);
+ VectorAdd(vTarget, e->origin, vTemp);
+ VectorAdd(vTemp, vUp, vTemp);
+ VectorSubtract(vTemp, vRight, vTemp);
+ g_QglTable.m_pfn_qglVertex3fv(e->origin);
+ g_QglTable.m_pfn_qglVertex3fv(vTemp);
+ VectorAdd(vTarget, e->origin, vTemp);
+ VectorAdd(vTemp, vRight, vTemp);
+ VectorSubtract(vTemp, vUp, vTemp);
+ g_QglTable.m_pfn_qglVertex3fv(e->origin);
+ g_QglTable.m_pfn_qglVertex3fv(vTemp);
+ VectorAdd(vTarget, e->origin, vTemp);
+ VectorSubtract(vTemp, vUp, vTemp);
+ VectorSubtract(vTemp, vRight, vTemp);
+ g_QglTable.m_pfn_qglVertex3fv(e->origin);
+ g_QglTable.m_pfn_qglVertex3fv(vTemp);
+ g_QglTable.m_pfn_qglEnd();
+
+ }
+
+ if(nGLState & DRAW_GL_FILL)
+ {
+ DrawLightSphere(e, nGLState, pref);
+ }
+ else
+ {
+ // Arnout: FIXME: clean this up a bit
+ // now draw lighting radius stuff...
+ if (pref)
+ {
+ bool bDrawSpotlightArc = false;
+ int nPasses = pref == 1 ? 3 : 2;
+
+ const char *target = ValueForKey(e, "target");
+ bool bIsSpotLight = !!target[0];
+
+ /*!\todo Spotlight..
+ if (bIsSpotLight)
+ {
+ // find the origin of the target...
+ entity_t *e = FindEntity("targetname", target);
+
+ if (e)
+ bDrawSpotlightArc = true;
+ }
+ */
+
+ g_QglTable.m_pfn_qglPushAttrib(GL_LINE_BIT);
+ g_QglTable.m_pfn_qglLineStipple(8, 0xAAAA);
+ g_QglTable.m_pfn_qglEnable(GL_LINE_STIPPLE);
+
+ float* envelope = (pref == 1) ? e->fLightEnvelope1 : e->fLightEnvelope2;
+ for (int iPass = 0; iPass < nPasses; iPass++)
+ {
+ float fRadius = envelope[iPass];
+
+ g_QglTable.m_pfn_qglBegin(GL_LINE_LOOP);
+
+ if (bIsSpotLight)
+ {
+ if (bDrawSpotlightArc)
+ {
+ // I give up on this, it's beyond me
+ }
+ }
+ else
+ {
+ if (fRadius > 0)
+ {
+ int i;
+ float ds, dc;
+
+ for (i = 0; i <= 24; i++)
+ {
+ ds = sin((i * 2 * Q_PI) / 24);
+ dc = cos((i * 2 * Q_PI) / 24);
+
+ switch (nViewType)
+ {
+ case 2:
+ g_QglTable.m_pfn_qglVertex3f(e->origin[0] + fRadius * dc,
+ e->origin[1] + fRadius * ds,
+ e->origin[2]);
+ break;
+ case 1:
+ g_QglTable.m_pfn_qglVertex3f(e->origin[0] + fRadius * dc,
+ e->origin[1],
+ e->origin[2] + fRadius * ds);
+ break;
+ case 0:
+ g_QglTable.m_pfn_qglVertex3f(e->origin[0],
+ e->origin[1] + fRadius * dc,
+ e->origin[2] + fRadius * ds);
+ break;
+ }
+ }
+ }
+ }
+ g_QglTable.m_pfn_qglEnd();
+ }
+ g_QglTable.m_pfn_qglPopAttrib();
+ }
+ }
+}
+
+