light: surface light support via "_surface" "texturename" light key

This commit is contained in:
Eric Wasylishen 2015-04-26 02:24:57 -06:00
parent d77a7d768c
commit a33c4b0136
3 changed files with 285 additions and 5 deletions

View File

@ -28,6 +28,12 @@ static entity_t *entities_tail;
static int num_entities;
static int num_lights;
/* surface lights */
#define MAX_SURFLIGHT_TEMPLATES 256
entity_t *surfacelight_templates[MAX_SURFLIGHT_TEMPLATES];
int num_surfacelight_templates;
static void MakeSurfaceLights(const bsp2_t *bsp);
/* temporary storage for sunlight settings before the sun_t objects are
created. */
static lightsample_t sunlight = { 0, { 255, 255, 255 } };
@ -551,6 +557,19 @@ JitterEntity(entity_t *entity)
}
}
static void
JitterEntities()
{
entity_t *entity;
for (entity = entities; entity; entity = entity->next) {
if (strncmp(entity->classname, "light", 5))
continue;
JitterEntity(entity);
}
}
/*
* ==================
* LoadEntities
@ -712,8 +731,6 @@ LoadEntities(const bsp2_t *bsp)
if (!strncmp(entity->classname, "light", 5)) {
CheckEntityFields(entity);
num_lights++;
JitterEntity(entity);
}
if (!strcmp(entity->classname, "light")) {
if (entity->targetname[0] && !entity->style) {
@ -811,7 +828,12 @@ LoadEntities(const bsp2_t *bsp)
}
logprint("%d entities read, %d are lights.\n", num_entities, num_lights);
// Creates more light entities, needs to be done before the rest
MakeSurfaceLights(bsp);
MatchTargets();
JitterEntities();
SetupSpotlights();
SetupSuns();
SetupSkyDome();
@ -929,3 +951,252 @@ WriteEntitiesToString(bsp2_t *bsp)
space -= length;
}
}
/*
* =======================================================================
* SURFACE LIGHTS
* =======================================================================
*/
static entity_t *DuplicateEntity(const entity_t *src)
{
epair_t *ep;
entity_t *entity = (entity_t *)malloc(sizeof(entity_t));
memcpy(entity, src, sizeof(entity_t));
/* also copy epairs */
entity->epairs = NULL;
for (ep = src->epairs; ep; ep = ep->next)
SetKeyValue(entity, ep->key, ep->value);
/* also insert into the entity list */
entity->next = NULL;
Entities_Insert(entity);
return entity;
}
static void CreateSurfaceLight(const vec3_t origin, const entity_t *surflight_template)
{
entity_t *entity = DuplicateEntity(surflight_template);
VectorCopy(origin, entity->origin);
/* don't write to bsp */
entity->generated = true;
CheckEntityFields(entity);
num_lights++;
}
static void CreateSurfaceLightOnFaceSubdivision(const bsp2_dface_t *face, const entity_t *surflight_template, const bsp2_t *bsp, int numverts, const vec_t *verts)
{
int i;
vec3_t midpoint = {0, 0, 0};
vec3_t normal;
vec_t offset;
for (i=0; i<numverts; i++)
{
VectorAdd(midpoint, verts + (i * 3), midpoint);
}
midpoint[0] /= numverts;
midpoint[1] /= numverts;
midpoint[2] /= numverts;
VectorCopy(bsp->dplanes[face->planenum].normal, normal);
vec_t dist = bsp->dplanes[face->planenum].dist;
/* Nudge 2 units (by default) along face normal */
if (face->side) {
dist = -dist;
VectorSubtract(vec3_origin, normal, normal);
}
offset = atof(ValueForKey(surflight_template, "_surface_offset"));
if (offset <= 0)
offset = 2.0;
VectorMA(midpoint, offset, normal, midpoint);
CreateSurfaceLight(midpoint, surflight_template);
}
static void BoundPoly (int numverts, float *verts, vec3_t mins, vec3_t maxs)
{
int i, j;
float *v;
mins[0] = mins[1] = mins[2] = 9999;
maxs[0] = maxs[1] = maxs[2] = -9999;
v = verts;
for (i=0 ; i<numverts ; i++)
for (j=0 ; j<3 ; j++, v++)
{
if (*v < mins[j])
mins[j] = *v;
if (*v > maxs[j])
maxs[j] = *v;
}
}
/*
================
SubdividePolygon - from GLQuake
================
*/
static void SubdividePolygon (const bsp2_dface_t *face, const bsp2_t *bsp, int numverts, vec_t *verts, float subdivide_size)
{
int i, j, k;
vec3_t mins, maxs;
float m;
float *v;
vec3_t front[64], back[64];
int f, b;
float dist[64];
float frac;
//glpoly_t *poly;
//float s, t;
if (numverts > 60)
Error ("numverts = %i", numverts);
BoundPoly (numverts, verts, mins, maxs);
for (i=0 ; i<3 ; i++)
{
m = (mins[i] + maxs[i]) * 0.5;
m = subdivide_size * floor (m/subdivide_size + 0.5);
if (maxs[i] - m < 8)
continue;
if (m - mins[i] < 8)
continue;
// cut it
v = verts + i;
for (j=0 ; j<numverts ; j++, v+= 3)
dist[j] = *v - m;
// wrap cases
dist[j] = dist[0];
v-=i;
VectorCopy (verts, v);
f = b = 0;
v = verts;
for (j=0 ; j<numverts ; j++, v+= 3)
{
if (dist[j] >= 0)
{
VectorCopy (v, front[f]);
f++;
}
if (dist[j] <= 0)
{
VectorCopy (v, back[b]);
b++;
}
if (dist[j] == 0 || dist[j+1] == 0)
continue;
if ( (dist[j] > 0) != (dist[j+1] > 0) )
{
// clip point
frac = dist[j] / (dist[j] - dist[j+1]);
for (k=0 ; k<3 ; k++)
front[f][k] = back[b][k] = v[k] + frac*(v[3+k] - v[k]);
f++;
b++;
}
}
SubdividePolygon (face, bsp, f, front[0], subdivide_size);
SubdividePolygon (face, bsp, b, back[0], subdivide_size);
return;
}
const texinfo_t *tex = &bsp->texinfo[face->texinfo];
const int offset = bsp->dtexdata.header->dataofs[tex->miptex];
const miptex_t *miptex = (const miptex_t *)(bsp->dtexdata.base + offset);
const char *texname = miptex->name;
for (i=0; i<num_surfacelight_templates; i++) {
if (!strcmp(texname, ValueForKey(surfacelight_templates[i], "_surface"))) {
CreateSurfaceLightOnFaceSubdivision(face, surfacelight_templates[i], bsp, numverts, verts);
}
}
}
/*
================
GL_SubdivideSurface - from GLQuake
================
*/
static void GL_SubdivideSurface (const bsp2_dface_t *face, const bsp2_t *bsp)
{
int i;
vec3_t verts[64];
for (i = 0; i < face->numedges; i++) {
dvertex_t *v;
int edgenum = bsp->dsurfedges[face->firstedge + i];
if (edgenum >= 0) {
v = bsp->dvertexes + bsp->dedges[edgenum].v[0];
} else {
v = bsp->dvertexes + bsp->dedges[-edgenum].v[1];
}
VectorCopy(v->point, verts[i]);
}
SubdividePolygon (face, bsp, face->numedges, verts[0], 128);
}
static void MakeSurfaceLights(const bsp2_t *bsp)
{
entity_t *entity;
int i, k;
for (entity = entities; entity; entity = entity->next) {
const char *tex = ValueForKey(entity, "_surface");
if (strcmp(tex, "") != 0) {
/* Add to template list */
if (num_surfacelight_templates == MAX_SURFLIGHT_TEMPLATES)
Error("num_surfacelight_templates == MAX_SURFLIGHT_TEMPLATES");
surfacelight_templates[num_surfacelight_templates++] = entity;
printf("Creating surface lights for texture \"%s\" from template at (%s)\n",
tex, ValueForKey(entity, "origin"));
}
}
if (!num_surfacelight_templates)
return;
/* Create the surface lights */
for (i=0; i<bsp->numleafs; i++) {
const bsp2_dleaf_t *leaf = bsp->dleafs + i;
const bsp2_dface_t *surf;
int ofs;
qboolean underwater = leaf->contents != CONTENTS_EMPTY;
for (k = 0; k < leaf->nummarksurfaces; k++) {
const texinfo_t *info;
const miptex_t *miptex;
surf = &bsp->dfaces[bsp->dmarksurfaces[leaf->firstmarksurface + k]];
info = &bsp->texinfo[surf->texinfo];
ofs = bsp->dtexdata.header->dataofs[info->miptex];
miptex = (const miptex_t *)(bsp->dtexdata.base + ofs);
/* Ignore the underwater side of liquid surfaces */
if (miptex->name[0] == '*' && underwater)
continue;
GL_SubdivideSurface(surf, bsp);
}
}
/* Hack: clear templates light value to 0 so they don't cast light */
for (i=0;i<num_surfacelight_templates;i++) {
surfacelight_templates[i]->light.light = 0;
}
}

View File

@ -852,9 +852,9 @@ LightFace_Entity(const entity_t *entity, const lightsample_t *light,
Light_Add(sample, add, light->color);
/* Check if we really hit, ignore tiny lights */
/* ericw -- don't ignore tiny lights if the light was split up
for a penumbra; the small light values are important in that case */
if (!hit && (sample->light >= 1 || entity->num_samples > 1))
/* ericw -- never ignore generated lights, which can be tiny and need
the additive effect of lots hitting */
if (!hit && (sample->light >= 1 || entity->generated))
hit = true;
}

View File

@ -269,6 +269,15 @@ Convert the light into a sphere of randomly positioned lights with radius "n" (i
.IP "\fB""_samples"" ""n""\fP"
Number of lights to use for "_deviance". Use 16 as a starting point, more for higher quality. Default 1.
.IP "\fB""_surface"" ""texturename""\fP"
Makes surfaces with the given texture name emit light, by using this light as a
template which is copied across those surfaces. Lights are spaced
about 128 units (though possibly closer due to bsp splitting) apart and positioned 2 units above
the surfaces.
.IP "\fB""_surface_offset"" ""texturename""\fP"
Controls the offset lights are placed above surfaces for "_surface". Default 2.
.SH AUTHOR
Written by Kevin Shanahan (aka Tyrann)
.br