light: new phong shading keys, "_phong_angle" and "_phong".
"_phong" "1" is a shortcut for "_phong_angle" "89". "_phong_angle" sets the max angle in degrees between faces that will get smoothed.
This commit is contained in:
parent
f641e93ca2
commit
492d94c1fe
|
|
@ -211,7 +211,8 @@ typedef struct texinfo_s {
|
|||
} texinfo_t;
|
||||
|
||||
#define TEX_SPECIAL 1 /* sky or slime, no lightmap or 256 subdivision */
|
||||
#define TEX_CURVED 0x800 /* surface is meant to have smoothed lighting, to hide angular edges, giving a curved appearance */
|
||||
#define TEX_PHONG_ANGLE_SHIFT 11
|
||||
#define TEX_PHONG_ANGLE_MASK (255U << TEX_PHONG_ANGLE_SHIFT) /* 8 bit value. if non zero, enables phong shading and gives the angle threshold to use. */
|
||||
|
||||
/*
|
||||
* Note that edge 0 is never used, because negative edge nums are used for
|
||||
|
|
|
|||
|
|
@ -180,6 +180,9 @@ const modelinfo_t *ModelInfoForFace(const bsp2_t *bsp, int facenum);
|
|||
void LightFace(bsp2_dface_t *face, facesup_t *facesup, const modelinfo_t *modelinfo, struct ltface_ctx *ctx);
|
||||
void MakeTnodes(const bsp2_t *bsp);
|
||||
|
||||
/* access the final phong-shaded vertex normal */
|
||||
const vec_t *GetSurfaceVertexNormal(const bsp2_t *bsp, const bsp2_dface_t *f, const int v);
|
||||
|
||||
extern float scaledist;
|
||||
extern float rangescale;
|
||||
extern float anglescale;
|
||||
|
|
|
|||
169
light/light.cc
169
light/light.cc
|
|
@ -18,10 +18,15 @@
|
|||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <light/light.h>
|
||||
#include <light/entities.h>
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
float scaledist = 1.0;
|
||||
float rangescale = 0.5;
|
||||
float anglescale = 0.5;
|
||||
|
|
@ -62,7 +67,6 @@ qboolean testFenceTextures = false;
|
|||
qboolean surflight_dump = false;
|
||||
|
||||
static facesup_t *faces_sup; //lit2/bspx stuff
|
||||
vec3_t *vertex_normals;
|
||||
|
||||
byte *filebase; // start of lightmap data
|
||||
static byte *file_p; // start of free space after data
|
||||
|
|
@ -300,9 +304,20 @@ AngleBetweenPoints(const vec3_t p1, const vec3_t p2, const vec3_t p3)
|
|||
return result;
|
||||
}
|
||||
|
||||
/* given a triangle, just adds the contribution from the triangle to the given vertexes normals, based upon angles at the verts */
|
||||
class vec3_struct_t {
|
||||
public:
|
||||
vec3_t v;
|
||||
vec3_struct_t() {
|
||||
VectorSet(v, 0, 0, 0);
|
||||
}
|
||||
};
|
||||
|
||||
std::map<const bsp2_dface_t *, std::vector<vec3_struct_t>> vertex_normals;
|
||||
|
||||
/* given a triangle, just adds the contribution from the triangle to the given vertexes normals, based upon angles at the verts.
|
||||
* v1, v2, v3 are global vertex indices */
|
||||
static void
|
||||
AddTriangleNormals(const vec_t *norm, const dvertex_t *verts, int v1, int v2, int v3)
|
||||
AddTriangleNormals(std::map<int, vec3_struct_t> &smoothed_normals, const vec_t *norm, const dvertex_t *verts, int v1, int v2, int v3)
|
||||
{
|
||||
const vec_t *p1 = verts[v1].point;
|
||||
const vec_t *p2 = verts[v2].point;
|
||||
|
|
@ -310,13 +325,13 @@ AddTriangleNormals(const vec_t *norm, const dvertex_t *verts, int v1, int v2, in
|
|||
float weight;
|
||||
|
||||
weight = AngleBetweenPoints(p2, p1, p3);
|
||||
VectorMA(vertex_normals[v1], weight, norm, vertex_normals[v1]);
|
||||
VectorMA(smoothed_normals[v1].v, weight, norm, smoothed_normals[v1].v);
|
||||
|
||||
weight = AngleBetweenPoints(p1, p2, p3);
|
||||
VectorMA(vertex_normals[v2], weight, norm, vertex_normals[v2]);
|
||||
VectorMA(smoothed_normals[v2].v, weight, norm, smoothed_normals[v2].v);
|
||||
|
||||
weight = AngleBetweenPoints(p1, p3, p2);
|
||||
VectorMA(vertex_normals[v3], weight, norm, vertex_normals[v3]);
|
||||
VectorMA(smoothed_normals[v3].v, weight, norm, smoothed_normals[v3].v);
|
||||
}
|
||||
/* small helper that just retrieves the correct vertex from face->surfedge->edge lookups */
|
||||
static int GetSurfaceVertex(const bsp2_t *bsp, const bsp2_dface_t *f, int v)
|
||||
|
|
@ -328,41 +343,127 @@ static int GetSurfaceVertex(const bsp2_t *bsp, const bsp2_dface_t *f, int v)
|
|||
return bsp->dedges[edge].v[0];
|
||||
}
|
||||
|
||||
static void
|
||||
Face_Normal(const bsp2_t *bsp, const bsp2_dface_t *f, vec3_t norm)
|
||||
{
|
||||
if (f->side)
|
||||
VectorSubtract(vec3_origin, bsp->dplanes[f->planenum].normal, norm);
|
||||
else
|
||||
VectorCopy(bsp->dplanes[f->planenum].normal, norm);
|
||||
}
|
||||
|
||||
const vec_t *GetSurfaceVertexNormal(const bsp2_t *bsp, const bsp2_dface_t *f, const int vertindex)
|
||||
{
|
||||
const auto &face_normals_vector = vertex_normals[f];
|
||||
return face_normals_vector[vertindex].v;
|
||||
}
|
||||
|
||||
static void
|
||||
CalcualateVertexNormals(const bsp2_t *bsp)
|
||||
{
|
||||
int i, j, v1, v2, v3;
|
||||
bsp2_dface_t *f;
|
||||
vec3_t norm;
|
||||
|
||||
vertex_normals = (vec3_t *)malloc(sizeof(vec3_t) * bsp->numvertexes);
|
||||
memset(vertex_normals, 0, sizeof(vec3_t) * bsp->numvertexes);
|
||||
|
||||
for (i = 0; i < bsp->numfaces; i++)
|
||||
{
|
||||
f = &bsp->dfaces[i];
|
||||
if (!(bsp->texinfo[f->texinfo].flags & TEX_CURVED)) /* we only care about smoothed faces. unsmoothed stuff shouldn't blend. */
|
||||
continue;
|
||||
|
||||
if (f->side)
|
||||
VectorSubtract(vec3_origin, bsp->dplanes[f->planenum].normal, norm);
|
||||
else
|
||||
VectorCopy(bsp->dplanes[f->planenum].normal, norm);
|
||||
|
||||
/* now just walk around the surface as a triangle fan */
|
||||
v1 = GetSurfaceVertex(bsp, f, 0);
|
||||
v2 = GetSurfaceVertex(bsp, f, 1);
|
||||
for (j = 2; j < f->numedges; j++)
|
||||
{
|
||||
v3 = GetSurfaceVertex(bsp, f, j);
|
||||
AddTriangleNormals(norm, bsp->dvertexes, v1, v2, v3);
|
||||
v2 = v3;
|
||||
// build "vert index -> faces" map
|
||||
std::map<int, std::vector<const bsp2_dface_t *>> vertsToFaces;
|
||||
for (int i = 0; i < bsp->numfaces; i++) {
|
||||
const bsp2_dface_t *f = &bsp->dfaces[i];
|
||||
for (int j = 0; j < f->numedges; j++) {
|
||||
const int v = GetSurfaceVertex(bsp, f, j);
|
||||
vertsToFaces[v].push_back(f);
|
||||
}
|
||||
}
|
||||
|
||||
// build the "face -> faces to smooth with" map
|
||||
std::map<const bsp2_dface_t *, std::set<const bsp2_dface_t *>> smoothFaces;
|
||||
for (int i = 0; i < bsp->numfaces; i++) {
|
||||
bsp2_dface_t *f = &bsp->dfaces[i];
|
||||
|
||||
vec3_t f_norm;
|
||||
Face_Normal(bsp, f, f_norm);
|
||||
|
||||
// any face normal within this many degrees can be smoothed with this face
|
||||
const int f_smoothangle = (bsp->texinfo[f->texinfo].flags & TEX_PHONG_ANGLE_MASK) >> TEX_PHONG_ANGLE_SHIFT;
|
||||
if (!f_smoothangle)
|
||||
continue;
|
||||
|
||||
for (int j = 0; j < f->numedges; j++) {
|
||||
const int v = GetSurfaceVertex(bsp, f, j);
|
||||
// walk over all faces incident to f (we will walk over neighbours multiple times, doesn't matter)
|
||||
for (const bsp2_dface_t *f2 : vertsToFaces[v]) {
|
||||
if (f2 == f)
|
||||
continue;
|
||||
|
||||
const int f2_smoothangle = (bsp->texinfo[f2->texinfo].flags & TEX_PHONG_ANGLE_MASK) >> TEX_PHONG_ANGLE_SHIFT;
|
||||
if (!f2_smoothangle)
|
||||
continue;
|
||||
|
||||
vec3_t f2_norm;
|
||||
Face_Normal(bsp, f2, f2_norm);
|
||||
|
||||
for (i = 0; i < bsp->numvertexes; i++)
|
||||
const vec_t angle = acos(DotProduct(f_norm, f2_norm));
|
||||
const vec_t max_angle = DEG2RAD(qmin(f_smoothangle, f2_smoothangle));
|
||||
|
||||
// check the angle between the face normals
|
||||
if (angle < max_angle) {
|
||||
smoothFaces[f].insert(f2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// finally do the smoothing
|
||||
for (int i = 0; i < bsp->numfaces; i++)
|
||||
{
|
||||
VectorNormalize(vertex_normals[i]);
|
||||
const bsp2_dface_t *f = &bsp->dfaces[i];
|
||||
const auto &neighboursToSmooth = smoothFaces[f];
|
||||
|
||||
// gather up f and neighboursToSmooth
|
||||
std::vector<const bsp2_dface_t *> fPlusNeighbours;
|
||||
fPlusNeighbours.push_back(f);
|
||||
for (auto neighbour : neighboursToSmooth) {
|
||||
fPlusNeighbours.push_back(neighbour);
|
||||
}
|
||||
|
||||
// global vertex index -> smoothed normal
|
||||
std::map<int, vec3_struct_t> smoothedNormals;
|
||||
|
||||
// walk fPlusNeighbours
|
||||
for (auto f2 : fPlusNeighbours) {
|
||||
vec3_t f2_norm;
|
||||
Face_Normal(bsp, f2, f2_norm);
|
||||
|
||||
/* now just walk around the surface as a triangle fan */
|
||||
int v1, v2, v3;
|
||||
v1 = GetSurfaceVertex(bsp, f2, 0);
|
||||
v2 = GetSurfaceVertex(bsp, f2, 1);
|
||||
for (int j = 2; j < f2->numedges; j++)
|
||||
{
|
||||
v3 = GetSurfaceVertex(bsp, f2, j);
|
||||
AddTriangleNormals(smoothedNormals, f2_norm, bsp->dvertexes, v1, v2, v3);
|
||||
v2 = v3;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &vertIndexNormalPair : smoothedNormals)
|
||||
{
|
||||
VectorNormalize(vertIndexNormalPair.second.v);
|
||||
}
|
||||
|
||||
// sanity check
|
||||
if (!neighboursToSmooth.size()) {
|
||||
vec3_t f_norm;
|
||||
Face_Normal(bsp, f, f_norm);
|
||||
for (auto vertIndexNormalPair : smoothedNormals)
|
||||
{
|
||||
assert(VectorCompare(vertIndexNormalPair.second.v, f_norm));
|
||||
}
|
||||
}
|
||||
|
||||
// now, record all of the smoothed normals that are actually part of `f`
|
||||
for (int j=0; j<f->numedges; j++) {
|
||||
int v = GetSurfaceVertex(bsp, f, j);
|
||||
assert(smoothedNormals.find(v) != smoothedNormals.end());
|
||||
|
||||
vertex_normals[f].push_back(smoothedNormals[v]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@
|
|||
#include <light/light.h>
|
||||
#include <light/entities.h>
|
||||
|
||||
extern vec3_t *vertex_normals;
|
||||
extern unsigned int lightturb;
|
||||
|
||||
/* ======================================================================== */
|
||||
|
|
@ -427,10 +426,6 @@ vec_t *GetSurfaceVertexPoint(const bsp2_t *bsp, const bsp2_dface_t *f, int v)
|
|||
{
|
||||
return bsp->dvertexes[GetSurfaceVertex(bsp, f, v)].point;
|
||||
}
|
||||
vec_t *GetSurfaceVertexNormal(const bsp2_t *bsp, const bsp2_dface_t *f, int v)
|
||||
{
|
||||
return vertex_normals[GetSurfaceVertex(bsp, f, v)];
|
||||
}
|
||||
|
||||
|
||||
static int ClipPointToTriangle(const vec_t *orig, vec_t *point, const vec_t *norm_, const vec_t *v1, const vec_t *v2, const vec_t *v3)
|
||||
|
|
@ -518,7 +513,7 @@ static void CalcPointNormal(const bsp2_t *bsp, const bsp2_dface_t *face, plane_t
|
|||
|
||||
#if 1
|
||||
int j;
|
||||
vec_t *v1, *v2, *v3;
|
||||
const vec_t *v1, *v2, *v3;
|
||||
int best; //3rd point
|
||||
vec3_t clipped, t;
|
||||
// vec3_t bestp = {point[0],point[1],point[2]};
|
||||
|
|
@ -729,7 +724,7 @@ Lightsurf_Init(const modelinfo_t *modelinfo, const bsp2_dface_t *face,
|
|||
else
|
||||
lightsurf->lightmapscale = modelinfo->lightmapscale;
|
||||
|
||||
if (bsp->texinfo[face->texinfo].flags & TEX_CURVED)
|
||||
if (bsp->texinfo[face->texinfo].flags & TEX_PHONG_ANGLE_MASK)
|
||||
lightsurf->curved = true;
|
||||
else
|
||||
lightsurf->curved = false;
|
||||
|
|
|
|||
22
qbsp/map.c
22
qbsp/map.c
|
|
@ -201,8 +201,23 @@ FindTexinfoEnt(texinfo_t *texinfo, mapentity_t *entity)
|
|||
flags |= TEX_HINT;
|
||||
if (IsSplitName(texname))
|
||||
flags |= TEX_SPECIAL;
|
||||
if (entity->smoothedtexture && !Q_strcasecmp(texname, entity->smoothedtexture->value))
|
||||
flags |= TEX_CURVED;
|
||||
|
||||
vec_t phongangle = atof(ValueForKey(entity, "_phong_angle"));
|
||||
int phong = atoi(ValueForKey(entity, "_phong"));
|
||||
|
||||
if (phong && (phongangle == 0.0)) {
|
||||
phongangle = 89.0; // default _phong_angle
|
||||
}
|
||||
|
||||
if (phongangle) {
|
||||
int phongangle_int = rint(phongangle);
|
||||
|
||||
if (phongangle_int < 0) phongangle_int = 0;
|
||||
if (phongangle_int > 255) phongangle_int = 255;
|
||||
|
||||
flags |= (phongangle_int << TEX_PHONG_ANGLE_SHIFT);
|
||||
}
|
||||
|
||||
return FindTexinfo(texinfo, flags);
|
||||
}
|
||||
|
||||
|
|
@ -226,8 +241,6 @@ ParseEpair(parser_t *parser, mapentity_t *entity)
|
|||
|
||||
if (!Q_strcasecmp(epair->key, "origin")) {
|
||||
GetVectorForKey(entity, epair->key, entity->origin);
|
||||
} else if (!Q_strcasecmp(epair->key, "_smooth")) {
|
||||
entity->smoothedtexture = epair;
|
||||
} else if (!Q_strcasecmp(epair->key, "classname")) {
|
||||
if (!Q_strcasecmp(epair->value, "info_player_start")) {
|
||||
if (rgfStartSpots & info_player_start)
|
||||
|
|
@ -638,7 +651,6 @@ ParseEntity(parser_t *parser, mapentity_t *entity)
|
|||
if (map.numentities == map.maxentities)
|
||||
Error("Internal error: didn't allocate enough entities?");
|
||||
|
||||
entity->smoothedtexture = NULL;
|
||||
entity->mapbrushes = brush = map.brushes + map.numbrushes;
|
||||
do {
|
||||
if (!ParseToken(parser, PARSE_NORMAL))
|
||||
|
|
|
|||
|
|
@ -113,7 +113,9 @@
|
|||
#define TEX_SPECIAL (1U << 0) /* sky or liquid (no lightmap or subdivision */
|
||||
#define TEX_SKIP (1U << 1) /* an invisible surface */
|
||||
#define TEX_HINT (1U << 2) /* hint surface */
|
||||
#define TEX_CURVED (1U << 11) /* surface is meant to have smoothed lighting, to hide angular edges, giving a curved appearance */
|
||||
|
||||
#define TEX_PHONG_ANGLE_SHIFT 11
|
||||
#define TEX_PHONG_ANGLE_MASK (255U << TEX_PHONG_ANGLE_SHIFT) /* 8 bit value. if non zero, enables phong shading and gives the angle threshold to use. */
|
||||
|
||||
/*
|
||||
* The quality of the bsp output is highly sensitive to these epsilon values.
|
||||
|
|
@ -514,7 +516,6 @@ struct lumpdata {
|
|||
|
||||
typedef struct mapentity_s {
|
||||
vec3_t origin;
|
||||
epair_t *smoothedtexture;
|
||||
mapbrush_t *mapbrushes; /* Array */
|
||||
int nummapbrushes;
|
||||
epair_t *epairs;
|
||||
|
|
|
|||
|
|
@ -707,7 +707,7 @@ CleanBSPTexinfoFlags(void)
|
|||
int i;
|
||||
|
||||
for (i = 0; i < num_texinfo; i++, texinfo++)
|
||||
texinfo->flags &= TEX_SPECIAL|TEX_CURVED;
|
||||
texinfo->flags &= TEX_SPECIAL|TEX_PHONG_ANGLE_MASK; // still write TEX_PHONG_ANGLE_MASK
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
Loading…
Reference in New Issue