light: use embree ray stream api

This commit is contained in:
Eric Wasylishen 2016-07-30 00:40:45 -06:00
parent 225a155efd
commit 882348ac0a
4 changed files with 631 additions and 175 deletions

View File

@ -39,10 +39,10 @@
#define ON_EPSILON 0.1
#define ANGLE_EPSILON 0.001
enum class hittype_t {
NONE,
SOLID,
SKY
enum class hittype_t : uint8_t {
NONE = 0,
SOLID = 1,
SKY = 2
};
/*
@ -53,12 +53,33 @@ enum class hittype_t {
qboolean TestSky(const vec3_t start, const vec3_t dirn, const dmodel_t *self);
qboolean TestLight(const vec3_t start, const vec3_t stop, const dmodel_t *self);
hittype_t DirtTrace(const vec3_t start, const vec3_t dirn, vec_t dist, const dmodel_t *self, vec_t *hitdist_out, plane_t *hitplane_out, const bsp2_dface_t **face_out);
class raystream_t {
public:
virtual void pushRay(int i, const vec_t *origin, const vec3_t dir, float dist, const dmodel_t *selfshadow, const vec_t *color = nullptr) = 0;
virtual size_t numPushedRays() = 0;
virtual void tracePushedRaysOcclusion() = 0;
virtual void tracePushedRaysIntersection() = 0;
virtual bool getPushedRayOccluded(size_t j) = 0;
virtual float getPushedRayDist(size_t j) = 0;
virtual float getPushedRayHitDist(size_t j) = 0;
virtual hittype_t getPushedRayHitType(size_t j) = 0;
virtual void getPushedRayDir(size_t j, vec3_t out) = 0;
virtual int getPushedRayPointIndex(size_t j) = 0;
virtual void getPushedRayColor(size_t j, vec3_t out) = 0;
virtual void clearPushedRays() = 0;
virtual ~raystream_t() {};
};
raystream_t *MakeRayStream(int maxrays);
void Embree_TraceInit(const bsp2_t *bsp);
qboolean Embree_TestSky(const vec3_t start, const vec3_t dirn, const dmodel_t *self);
qboolean Embree_TestLight(const vec3_t start, const vec3_t stop, const dmodel_t *self);
hittype_t Embree_DirtTrace(const vec3_t start, const vec3_t dirn, vec_t dist, const dmodel_t *self, vec_t *hitdist_out, plane_t *hitplane_out, const bsp2_dface_t **face_out);
raystream_t *Embree_MakeRayStream(int maxrays);
int
SampleTexture(const bsp2_dface_t *face, const bsp2_t *bsp, const vec3_t point);
@ -153,6 +174,9 @@ typedef struct {
/* for lit water. receive light from either front or back. */
bool twosided;
// ray batch stuff
raystream_t *stream;
} lightsurf_t;
typedef struct {

View File

@ -910,6 +910,8 @@ Lightsurf_Init(const modelinfo_t *modelinfo, const bsp2_dface_t *face,
/* Setup vis data */
CalcPvs(bsp, lightsurf);
lightsurf->stream = MakeRayStream(lightsurf->numpoints);
}
static void
@ -1319,16 +1321,13 @@ extern int totalmissed;
static void
LightFace_Entity(const bsp2_t *bsp,
const light_t *entity,
const lightsurf_t *lightsurf, lightmap_t *lightmaps)
lightsurf_t *lightsurf, lightmap_t *lightmaps)
{
const modelinfo_t *modelinfo = lightsurf->modelinfo;
const plane_t *plane = &lightsurf->plane;
const dmodel_t *shadowself;
const vec_t *surfpoint, *surfnorm;
int i;
qboolean hit;
vec_t planedist, add, angle, spotscale;
lightsample_t *sample;
lightmap_t *lightmap;
/* vis cull */
@ -1336,7 +1335,7 @@ LightFace_Entity(const bsp2_t *bsp,
return;
}
planedist = DotProduct(*entity->origin.vec3Value(), plane->normal) - plane->dist;
const float planedist = DotProduct(*entity->origin.vec3Value(), plane->normal) - plane->dist;
/* don't bother with lights behind the surface.
@ -1358,10 +1357,14 @@ LightFace_Entity(const bsp2_t *bsp,
hit = false;
lightmap = Lightmap_ForStyle(lightmaps, entity->style.intValue(), lightsurf);
shadowself = modelinfo->shadowself.boolValue() ? modelinfo->model : NULL;
sample = lightmap->samples;
surfpoint = lightsurf->points[0];
surfnorm = lightsurf->normals[0];
for (i = 0; i < lightsurf->numpoints; i++, sample++, surfpoint += 3, surfnorm += 3) {
raystream_t *rs = lightsurf->stream;
rs->clearPushedRays();
for (int i = 0; i < lightsurf->numpoints; i++) {
const vec_t *surfpoint = lightsurf->points[i];
const vec_t *surfnorm = lightsurf->normals[i];
vec3_t surfpointToLightDir;
VectorSubtract(*entity->origin.vec3Value(), surfpoint, surfpointToLightDir);
vec_t surfpointToLightDist = VectorNormalize(surfpointToLightDir);
@ -1370,7 +1373,7 @@ LightFace_Entity(const bsp2_t *bsp,
if (fabs(GetLightValue(entity->light.floatValue(), entity, surfpointToLightDist)) <= fadegate)
continue;
angle = DotProduct(surfpointToLightDir, surfnorm);
float angle = DotProduct(surfpointToLightDir, surfnorm);
if (entity->bleed.boolValue() || lightsurf->twosided) {
if (angle < 0) {
angle = -angle; // ericw -- support "_bleed" option
@ -1380,9 +1383,39 @@ LightFace_Entity(const bsp2_t *bsp,
/* Light behind sample point? Zero contribution, period. */
if (angle < 0)
continue;
rs->pushRay(i, surfpoint, surfpointToLightDir, surfpointToLightDist, shadowself);
}
rs->tracePushedRaysOcclusion();
const int N = rs->numPushedRays();
for (int j = 0; j < N; j++) {
if (rs->getPushedRayOccluded(j))
continue;
int i = rs->getPushedRayPointIndex(j);
lightsample_t *sample = &lightmap->samples[i];
const vec_t *surfpoint = lightsurf->points[i];
const vec_t *surfnorm = lightsurf->normals[i];
float surfpointToLightDist = rs->getPushedRayDist(j);
vec3_t surfpointToLightDir;
rs->getPushedRayDir(j, surfpointToLightDir);
// FIXME: duplicated from above
float angle = DotProduct(surfpointToLightDir, surfnorm);
if (entity->bleed.boolValue() || lightsurf->twosided) {
if (angle < 0) {
angle = -angle; // ericw -- support "_bleed" option
}
}
angle = (1.0 - entity->anglescale.floatValue()) + entity->anglescale.floatValue() * angle;
/* Check spotlight cone */
spotscale = 1;
float spotscale = 1;
if (entity->spotlight) {
vec_t falloff = DotProduct(entity->spotvec, surfpointToLightDir);
if (falloff > entity->spotfalloff)
@ -1395,11 +1428,7 @@ LightFace_Entity(const bsp2_t *bsp,
}
}
if (!TestLight(*entity->origin.vec3Value(), surfpoint, shadowself))
continue;
angle = (1.0 - entity->anglescale.floatValue()) + entity->anglescale.floatValue() * angle;
add = GetLightValue(entity->light.floatValue(), entity, surfpointToLightDist) * angle * spotscale;
float add = GetLightValue(entity->light.floatValue(), entity, surfpointToLightDist) * angle * spotscale;
add *= Dirt_GetScaleFactor(lightsurf->occlusion[i], entity, lightsurf);
if (entity->projectedmip)
@ -1419,7 +1448,7 @@ LightFace_Entity(const bsp2_t *bsp,
if (!hit && (sample->light >= 1 || entity->generated))
hit = true;
}
if (hit)
Lightmap_Save(lightmaps, lightsurf, lightmap, entity->style.intValue());
}
@ -1432,42 +1461,37 @@ LightFace_Entity(const bsp2_t *bsp,
static void
LightFace_Sky(const sun_t *sun, const lightsurf_t *lightsurf, lightmap_t *lightmaps)
{
constexpr float MAX_SKY_DIST = 65536.0f;
const modelinfo_t *modelinfo = lightsurf->modelinfo;
const plane_t *plane = &lightsurf->plane;
const dmodel_t *shadowself;
const vec_t *surfpoint, *surfnorm;
int i;
qboolean hit;
vec3_t incoming;
vec_t angle;
lightsample_t *sample;
lightmap_t *lightmap;
qboolean curved = lightsurf->curved;
/* If vis data says we can't see any sky faces, skip raytracing */
if (!lightsurf->skyvisible)
return;
/* Don't bother if surface facing away from sun */
if (DotProduct(sun->sunvec, plane->normal) < -ANGLE_EPSILON && !curved && !lightsurf->twosided)
if (DotProduct(sun->sunvec, plane->normal) < -ANGLE_EPSILON && !lightsurf->curved && !lightsurf->twosided)
return;
/* if sunlight is set, use a style 0 light map */
lightmap = Lightmap_ForStyle(lightmaps, 0, lightsurf);
lightmap_t *lightmap = Lightmap_ForStyle(lightmaps, 0, lightsurf);
vec3_t incoming;
VectorCopy(sun->sunvec, incoming);
VectorNormalize(incoming);
/* Check each point... */
hit = false;
shadowself = modelinfo->shadowself.boolValue() ? modelinfo->model : NULL;
sample = lightmap->samples;
surfpoint = lightsurf->points[0];
surfnorm = lightsurf->normals[0];
for (i = 0; i < lightsurf->numpoints; i++, sample++, surfpoint += 3, surfnorm += 3) {
vec_t value;
bool hit = false;
const dmodel_t *shadowself = modelinfo->shadowself.boolValue() ? modelinfo->model : NULL;
angle = DotProduct(incoming, surfnorm);
raystream_t *rs = lightsurf->stream;
rs->clearPushedRays();
for (int i = 0; i < lightsurf->numpoints; i++) {
const vec_t *surfpoint = lightsurf->points[i];
const vec_t *surfnorm = lightsurf->normals[i];
float angle = DotProduct(incoming, surfnorm);
if (lightsurf->twosided) {
if (angle < 0) {
angle = -angle;
@ -1476,14 +1500,34 @@ LightFace_Sky(const sun_t *sun, const lightsurf_t *lightsurf, lightmap_t *lightm
if (angle < 0)
continue;
if (!TestSky(surfpoint, sun->sunvec, shadowself))
rs->pushRay(i, surfpoint, incoming, MAX_SKY_DIST, shadowself);
}
rs->tracePushedRaysIntersection();
const int N = rs->numPushedRays();
for (int j = 0; j < N; j++) {
if (rs->getPushedRayHitType(j) != hittype_t::SKY)
continue;
const int i = rs->getPushedRayPointIndex(j);
const vec_t *surfnorm = lightsurf->normals[i];
// FIXME: don't recompute this: compute before tracing, check gate, and store color in ray
float angle = DotProduct(incoming, surfnorm);
if (lightsurf->twosided) {
if (angle < 0) {
angle = -angle;
}
}
angle = (1.0 - sun->anglescale) + sun->anglescale * angle;
value = angle * sun->sunlight.light;
float value = angle * sun->sunlight.light;
if (sun->dirt)
value *= Dirt_GetScaleFactor(lightsurf->occlusion[i], NULL, lightsurf);
lightsample_t *sample = &lightmap->samples[i];
Light_Add(sample, value, sun->sunlight.color, sun->sunvec);
if (!hit/* && (sample->light >= 1)*/)
hit = true;
@ -1493,6 +1537,12 @@ LightFace_Sky(const sun_t *sun, const lightsurf_t *lightsurf, lightmap_t *lightm
Lightmap_Save(lightmaps, lightsurf, lightmap, 0);
}
static vec_t GetDir(const vec3_t start, const vec3_t stop, vec3_t dir)
{
VectorSubtract(stop, start, dir);
return VectorNormalize(dir);
}
/*
* ============
* LightFace_Min
@ -1504,24 +1554,18 @@ LightFace_Min(const bsp2_t *bsp, const bsp2_dface_t *face,
const lightsurf_t *lightsurf, lightmap_t *lightmaps)
{
const modelinfo_t *modelinfo = lightsurf->modelinfo;
const dmodel_t *shadowself;
light_t **entity;
const vec_t *surfpoint;
qboolean hit, trace;
int i, j;
lightsample_t *sample;
lightmap_t *lightmap;
const char *texname = Face_TextureName(bsp, face);
if (texname[0] != '\0' && modelinfo->minlight_exclude.stringValue() == std::string{ texname })
return; /* this texture is excluded from minlight */
/* Find a style 0 lightmap */
lightmap = Lightmap_ForStyle(lightmaps, 0, lightsurf);
lightmap_t *lightmap = Lightmap_ForStyle(lightmaps, 0, lightsurf);
hit = false;
sample = lightmap->samples;
for (i = 0; i < lightsurf->numpoints; i++, sample++) {
bool hit = false;
for (int i = 0; i < lightsurf->numpoints; i++) {
lightsample_t *sample = &lightmap->samples[i];
vec_t value = light->light;
if (minlightDirt.boolValue())
value *= Dirt_GetScaleFactor(lightsurf->occlusion[i], NULL, lightsurf);
@ -1537,28 +1581,45 @@ LightFace_Min(const bsp2_t *bsp, const bsp2_dface_t *face,
Lightmap_Save(lightmaps, lightsurf, lightmap, 0);
/* Cast rays for local minlight entities */
shadowself = modelinfo->shadowself.boolValue() ? modelinfo->model : NULL;
const dmodel_t *shadowself = modelinfo->shadowself.boolValue() ? modelinfo->model : NULL;
for (const auto &entity : GetLights()) {
if (entity.getFormula() != LF_LOCALMIN)
continue;
raystream_t *rs = lightsurf->stream;
rs->clearPushedRays();
lightmap = Lightmap_ForStyle(lightmaps, entity.style.intValue(), lightsurf);
hit = false;
sample = lightmap->samples;
surfpoint = lightsurf->points[0];
for (j = 0; j < lightsurf->numpoints; j++, sample++, surfpoint += 3) {
for (int i = 0; i < lightsurf->numpoints; i++) {
const lightsample_t *sample = &lightmap->samples[i];
const vec_t *surfpoint = lightsurf->points[i];
if (addminlight.boolValue() || sample->light < entity.light.floatValue()) {
vec_t value = entity.light.floatValue();
trace = TestLight(*entity.origin.vec3Value(), surfpoint, shadowself);
if (!trace)
continue;
value *= Dirt_GetScaleFactor(lightsurf->occlusion[j], &entity, lightsurf);
if (addminlight.boolValue())
Light_Add(sample, value, *entity.color.vec3Value(), vec3_origin);
else
Light_ClampMin(sample, value, *entity.color.vec3Value());
vec3_t surfpointToLightDir;
vec_t surfpointToLightDist = GetDir(surfpoint, *entity.origin.vec3Value(), surfpointToLightDir);
rs->pushRay(i, surfpoint, surfpointToLightDir, surfpointToLightDist, shadowself);
}
}
rs->tracePushedRaysOcclusion();
const int N = rs->numPushedRays();
for (int j = 0; j < N; j++) {
if (rs->getPushedRayOccluded(j))
continue;
int i = rs->getPushedRayPointIndex(j);
vec_t value = entity.light.floatValue();
lightsample_t *sample = &lightmap->samples[i];
value *= Dirt_GetScaleFactor(lightsurf->occlusion[i], &entity, lightsurf);
if (addminlight.boolValue())
Light_Add(sample, value, *entity.color.vec3Value(), vec3_origin);
else
Light_ClampMin(sample, value, *entity.color.vec3Value());
if (!hit && sample->light >= 1)
hit = true;
}
@ -1645,15 +1706,18 @@ BounceLight_ColorAtDist(const bouncelight_t *vpl, vec_t dist, vec3_t color)
VectorScale(color, 255 * scale, color);
}
// dir: vpl -> sample point direction
// returns color in [0,255]
static inline void
GetIndirectLighting (const bouncelight_t *vpl, const vec3_t origin, const vec3_t normal, vec3_t color)
GetIndirectLighting (const bouncelight_t *vpl, const vec3_t dir, vec_t dist, const vec3_t origin, const vec3_t normal, vec3_t color)
{
VectorSet(color, 0, 0, 0);
#if 0
vec3_t dir;
VectorSubtract(origin, vpl->pos, dir); // vpl -> sample point
vec_t dist = VectorNormalize(dir);
#endif
const vec_t dp1 = DotProduct(vpl->surfnormal, dir);
if (dp1 < 0)
@ -1695,7 +1759,7 @@ BounceLight_SphereCull(const bsp2_t *bsp, const bouncelight_t *vpl, const lights
void
LightFace_Bounce(const bsp2_t *bsp, const bsp2_dface_t *face, const lightsurf_t *lightsurf, lightmap_t *lightmaps)
{
//const dmodel_t *shadowself = lightsurf->modelinfo->shadowself.boolValue() ? lightsurf->modelinfo->model : NULL;
lightmap_t *lightmap;
if (!bounce.boolValue())
@ -1718,16 +1782,35 @@ LightFace_Bounce(const bsp2_t *bsp, const bsp2_dface_t *face, const lightsurf_t
if (BounceLight_SphereCull(bsp, vpl, lightsurf))
continue;
raystream_t *rs = lightsurf->stream;
rs->clearPushedRays();
for (int i = 0; i < lightsurf->numpoints; i++) {
vec3_t dir; // vpl -> sample point
VectorSubtract(lightsurf->points[i], vpl->pos, dir);
vec_t dist = VectorNormalize(dir);
vec3_t indirect = {0};
GetIndirectLighting(vpl, lightsurf->points[i], lightsurf->normals[i], indirect);
GetIndirectLighting(vpl, dir, dist, lightsurf->points[i], lightsurf->normals[i], indirect);
if (((indirect[0] + indirect[1] + indirect[2]) / 3) < 0.25)
continue;
if (!TestLight(vpl->pos, lightsurf->points[i], NULL))
rs->pushRay(i, vpl->pos, dir, dist, /*shadowself*/ nullptr, indirect);
}
rs->tracePushedRaysOcclusion();
const int N = rs->numPushedRays();
for (int j = 0; j < N; j++) {
if (rs->getPushedRayOccluded(j))
continue;
const int i = rs->getPushedRayPointIndex(j);
vec3_t indirect = {0};
rs->getPushedRayColor(j, indirect);
assert(((indirect[0] + indirect[1] + indirect[2]) / 3) >= 0.25);
/* Use dirt scaling on the indirect lighting.
* Except, not in bouncedebug mode.
*/
@ -1842,27 +1925,10 @@ Lightmap_ForStyle_ReadOnly(const struct ltface_ctx *ctx, const int style)
return NULL;
}
/*
* ============
* DirtForSample
* ============
*/
static vec_t
DirtForSample(const dmodel_t *model, const vec3_t origin, const vec3_t normal){
int i;
float gatherDirt, angle, elevation, ooDepth;
vec3_t worldUp, myUp, myRt, temp, direction;
vec_t traceHitdist;
/* dummy check */
if ( !dirt_in_use ) {
return 1.0f;
}
/* setup */
gatherDirt = 0.0f;
ooDepth = 1.0f / dirtDepth.floatValue();
// from q3map2
static void
GetUpRtVecs(const vec3_t normal, vec3_t myUp, vec3_t myRt)
{
/* check if the normal is aligned to the world-up */
if ( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f ) {
if ( normal[ 2 ] == 1.0f ) {
@ -1873,60 +1939,40 @@ DirtForSample(const dmodel_t *model, const vec3_t origin, const vec3_t normal){
VectorSet( myUp, 0.0f, 1.0f, 0.0f );
}
} else {
vec3_t worldUp;
VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
CrossProduct( normal, worldUp, myRt );
VectorNormalize( myRt );
CrossProduct( myRt, normal, myUp );
VectorNormalize( myUp );
}
/* 1 = random mode, 0 (well everything else) = non-random mode */
if ( dirtMode.intValue() == 1 ) {
/* iterate */
for ( i = 0; i < numDirtVectors; i++ ) {
/* get random vector */
angle = Random() * DEG2RAD( 360.0f );
elevation = Random() * DEG2RAD( dirtAngle.floatValue() );
temp[ 0 ] = cos( angle ) * sin( elevation );
temp[ 1 ] = sin( angle ) * sin( elevation );
temp[ 2 ] = cos( elevation );
/* transform into tangent space */
direction[ 0 ] = myRt[ 0 ] * temp[ 0 ] + myUp[ 0 ] * temp[ 1 ] + normal[ 0 ] * temp[ 2 ];
direction[ 1 ] = myRt[ 1 ] * temp[ 0 ] + myUp[ 1 ] * temp[ 1 ] + normal[ 1 ] * temp[ 2 ];
direction[ 2 ] = myRt[ 2 ] * temp[ 0 ] + myUp[ 2 ] * temp[ 1 ] + normal[ 2 ] * temp[ 2 ];
/* trace */
if (hittype_t::SOLID == DirtTrace(origin, direction, dirtDepth.floatValue(), model, &traceHitdist, NULL, NULL)) {
gatherDirt += 1.0f - ooDepth * traceHitdist;
}
}
} else {
/* iterate through ordered vectors */
for ( i = 0; i < numDirtVectors; i++ ) {
/* transform vector into tangent space */
direction[ 0 ] = myRt[ 0 ] * dirtVectors[ i ][ 0 ] + myUp[ 0 ] * dirtVectors[ i ][ 1 ] + normal[ 0 ] * dirtVectors[ i ][ 2 ];
direction[ 1 ] = myRt[ 1 ] * dirtVectors[ i ][ 0 ] + myUp[ 1 ] * dirtVectors[ i ][ 1 ] + normal[ 1 ] * dirtVectors[ i ][ 2 ];
direction[ 2 ] = myRt[ 2 ] * dirtVectors[ i ][ 0 ] + myUp[ 2 ] * dirtVectors[ i ][ 1 ] + normal[ 2 ] * dirtVectors[ i ][ 2 ];
/* trace */
if (hittype_t::SOLID == DirtTrace(origin, direction, dirtDepth.floatValue(), model, &traceHitdist, NULL, NULL)) {
gatherDirt += 1.0f - ooDepth * traceHitdist;
}
}
}
/* trace */
if (hittype_t::SOLID == DirtTrace(origin, direction, dirtDepth.floatValue(), model, &traceHitdist, NULL, NULL)) {
gatherDirt += 1.0f - ooDepth * traceHitdist;
}
/* save gatherDirt, the rest of the scaling of the dirt value is done
per-light */
return gatherDirt / ( numDirtVectors + 1 );
}
// from q3map2
static void
TransformToTangentSpace(const vec3_t normal, const vec3_t myUp, const vec3_t myRt, const vec3_t inputvec, vec3_t outputvec)
{
for (int i=0; i<3; i++)
outputvec[i] = myRt[i] * inputvec[0] + myUp[i] * inputvec[1] + normal[i] * inputvec[2];
}
// from q3map2
static inline void
GetDirtVector(int i, vec3_t out)
{
assert(i < numDirtVectors);
if ( dirtMode.intValue() == 1 ) {
/* get random vector */
float angle = Random() * DEG2RAD( 360.0f );
float elevation = Random() * DEG2RAD( dirtAngle.floatValue() );
out[ 0 ] = cos( angle ) * sin( elevation );
out[ 1 ] = sin( angle ) * sin( elevation );
out[ 2 ] = cos( elevation );
} else {
VectorCopy(dirtVectors[i], out);
}
}
/*
* ============
@ -1936,10 +1982,65 @@ DirtForSample(const dmodel_t *model, const vec3_t origin, const vec3_t normal){
static void
LightFace_CalculateDirt(lightsurf_t *lightsurf)
{
assert(dirt_in_use);
const dmodel_t *selfshadow = lightsurf->modelinfo->shadowself.boolValue() ? lightsurf->modelinfo->model : NULL;
// batch implementation:
vec3_t *myUps = (vec3_t *) calloc(lightsurf->numpoints, sizeof(vec3_t));
vec3_t *myRts = (vec3_t *) calloc(lightsurf->numpoints, sizeof(vec3_t));
// init
for (int i = 0; i < lightsurf->numpoints; i++) {
lightsurf->occlusion[i] = DirtForSample(selfshadow, lightsurf->points[i], lightsurf->normals[i]);
lightsurf->occlusion[i] = 0;
}
// this stuff is just per-point
for (int i = 0; i < lightsurf->numpoints; i++) {
GetUpRtVecs(lightsurf->normals[i], myUps[i], myRts[i]);
}
for (int j=0; j<numDirtVectors; j++) {
raystream_t *rs = lightsurf->stream;
rs->clearPushedRays();
// fill in input buffers
for (int i = 0; i < lightsurf->numpoints; i++) {
vec3_t dirtvec;
GetDirtVector(j, dirtvec);
vec3_t dir;
TransformToTangentSpace(lightsurf->normals[i], myUps[i], myRts[i], dirtvec, dir);
rs->pushRay(i, lightsurf->points[i], dir, dirtDepth.floatValue(), selfshadow);
}
assert(rs->numPushedRays() == lightsurf->numpoints);
// trace the batch
rs->tracePushedRaysIntersection();
// accumulate hitdists
for (int i = 0; i < lightsurf->numpoints; i++) {
if (rs->getPushedRayHitType(i) == hittype_t::SOLID) {
float dist = rs->getPushedRayHitDist(i);
lightsurf->occlusion[i] += qmin(dirtDepth.floatValue(), dist);
} else {
lightsurf->occlusion[i] += dirtDepth.floatValue();
}
}
}
// process the results.
for (int i = 0; i < lightsurf->numpoints; i++) {
vec_t avgHitdist = lightsurf->occlusion[i] / (float)numDirtVectors;
lightsurf->occlusion[i] = 1 - (avgHitdist / dirtDepth.floatValue());
}
free(myUps);
free(myRts);
}
@ -2108,6 +2209,8 @@ void LightFaceShutdown(struct ltface_ctx *ctx)
if (ctx->lightsurf->pvs)
free(ctx->lightsurf->pvs);
delete ctx->lightsurf->stream;
free(ctx->lightsurf);
}

View File

@ -822,6 +822,128 @@ hittype_t DirtTrace(const vec3_t start, const vec3_t dirn, vec_t dist, const dmo
Error("no backend available");
}
class bsp_ray_t {
public:
int _pointindex;
vec3_t _origin, _dir;
float _maxdist;
const dmodel_t *_selfshadow;
vec3_t _color;
// hit info
float _hitdist;
hittype_t _hittype;
bool _hit_occluded;
bsp_ray_t(int i, const vec_t *origin, const vec3_t dir, float dist, const dmodel_t *selfshadow, const vec_t *color) :
_pointindex{i},
_origin{origin[0], origin[1], origin[2]},
_dir{dir[0], dir[1], dir[2]},
_maxdist{dist},
_selfshadow{selfshadow},
_hitdist{dist},
_hittype{hittype_t::NONE},
_hit_occluded{false} {
if (color != nullptr) {
VectorCopy(color, _color);
}
}
};
class raystream_bsp_t : public raystream_t {
private:
std::vector<bsp_ray_t> _rays;
int _maxrays;
public:
raystream_bsp_t(int maxRays) :
_maxrays { maxRays } {}
raystream_bsp_t() {}
virtual void pushRay(int i, const vec_t *origin, const vec3_t dir, float dist, const dmodel_t *selfshadow, const vec_t *color = nullptr) {
bsp_ray_t r { i, origin, dir, dist, selfshadow, color };
_rays.push_back(r);
assert(_rays.size() <= _maxrays);
}
virtual size_t numPushedRays() {
return _rays.size();
}
virtual void tracePushedRaysOcclusion() {
if (!_rays.size())
return;
for (bsp_ray_t &ray : _rays) {
vec3_t stop;
VectorMA(ray._origin, ray._maxdist, ray._dir, stop);
ray._hit_occluded = !BSP_TestLight(ray._origin, stop, ray._selfshadow);
}
}
virtual void tracePushedRaysIntersection() {
if (!_rays.size())
return;
for (bsp_ray_t &ray : _rays) {
ray._hittype = BSP_DirtTrace(ray._origin, ray._dir, ray._maxdist, ray._selfshadow, &ray._hitdist, nullptr, nullptr);
}
}
virtual bool getPushedRayOccluded(size_t j) {
return _rays.at(j)._hit_occluded;
}
virtual float getPushedRayDist(size_t j) {
return _rays.at(j)._maxdist;
}
virtual float getPushedRayHitDist(size_t j) {
return _rays.at(j)._hitdist;
}
virtual hittype_t getPushedRayHitType(size_t j) {
return _rays.at(j)._hittype;
}
virtual void getPushedRayDir(size_t j, vec3_t out) {
for (int i=0; i<3; i++) {
out[i] = _rays.at(j)._dir[i];
}
}
virtual int getPushedRayPointIndex(size_t j) {
return _rays.at(j)._pointindex;
}
virtual void getPushedRayColor(size_t j, vec3_t out) {
VectorCopy(_rays.at(j)._color, out);
}
virtual void clearPushedRays() {
_rays.clear();
}
};
raystream_t *BSP_MakeRayStream(int maxrays)
{
return new raystream_bsp_t{maxrays};
}
raystream_t *MakeRayStream(int maxrays)
{
#ifdef HAVE_EMBREE
if (rtbackend == backend_embree) {
return Embree_MakeRayStream(maxrays);
}
#endif
if (rtbackend == backend_bsp) {
return BSP_MakeRayStream(maxrays);
}
Error("no backend available");
}
void MakeTnodes(const bsp2_t *bsp)
{
#ifdef HAVE_EMBREE

View File

@ -22,8 +22,13 @@
#include <embree2/rtcore_ray.h>
#include <vector>
#include <cassert>
#include <cstdlib>
#include <limits>
#ifdef _MSC_VER
#include <malloc.h>
#endif
static constexpr float MAX_SKY_RAY_DEPTH = 8192.0f;
/**
@ -130,45 +135,96 @@ const bsp2_dface_t *Embree_LookupFace(unsigned int geomID, unsigned int primID)
return info.triToFace.at(primID);
}
void Embree_RayEndpoint(const RTCRay& ray, vec3_t endpoint)
static void
Embree_RayEndpoint(struct RTCRayN* ray, const struct RTCHitN* potentialHit, size_t N, size_t i, vec3_t endpoint)
{
vec3_t dir;
VectorCopy(ray.dir, dir);
dir[0] = RTCRayN_dir_x(ray, N, i);
dir[1] = RTCRayN_dir_y(ray, N, i);
dir[2] = RTCRayN_dir_z(ray, N, i);
VectorNormalize(dir);
VectorMA(ray.org, ray.tfar, dir, endpoint);
vec3_t org;
org[0] = RTCRayN_org_x(ray, N, i);
org[1] = RTCRayN_org_y(ray, N, i);
org[2] = RTCRayN_org_z(ray, N, i);
// N.B.: we want the distance to the potential hit, not RTCRayN_tfar (stopping dist?)
float tfar = RTCHitN_t(potentialHit, N, i);
VectorMA(org, tfar, dir, endpoint);
}
enum class filtertype_t {
INTERSECTION, OCCLUSION
};
// called to evaluate transparency
template<filtertype_t filtertype>
static void
Embree_FilterFunc(void* userDataPtr, RTCRay& ray)
Embree_FilterFuncN(int* valid,
void* userDataPtr,
const RTCIntersectContext* context,
struct RTCRayN* ray,
const struct RTCHitN* potentialHit,
const size_t N)
{
// bail if we hit a selfshadow face, but the ray is not coming from within that model
if (ray.mask == 0 && ray.geomID == selfshadowgeom.geomID) {
// reject hit
ray.geomID = RTC_INVALID_GEOMETRY_ID;
return;
}
constexpr int VALID = -1;
constexpr int INVALID = 0;
// test fence texture
const bsp2_dface_t *face = Embree_LookupFace(ray.geomID, ray.primID);
// bail if it's not a fence
const char *name = Face_TextureName(bsp_static, face);
if (name[0] != '{')
return;
vec3_t hitpoint;
Embree_RayEndpoint(ray, hitpoint);
const int sample = SampleTexture(face, bsp_static, hitpoint);
if (sample == 255) {
// reject hit
ray.geomID = RTC_INVALID_GEOMETRY_ID;
for (size_t i=0; i<N; i++) {
if (valid[i] != VALID) {
// we only need to handle valid rays
continue;
}
const unsigned &mask = RTCRayN_mask(ray, N, i);
const unsigned &geomID = RTCHitN_geomID(potentialHit, N, i);
const unsigned &primID = RTCHitN_primID(potentialHit, N, i);
// bail if we hit a selfshadow face, but the ray is not coming from within that model
if (mask == 0 && geomID == selfshadowgeom.geomID) {
// reject hit
valid[i] = INVALID;
continue;
}
// test fence texture
const bsp2_dface_t *face = Embree_LookupFace(geomID, primID);
const char *name = Face_TextureName(bsp_static, face);
if (name[0] == '{') {
vec3_t hitpoint;
Embree_RayEndpoint(ray, potentialHit, N, i, hitpoint);
const int sample = SampleTexture(face, bsp_static, hitpoint);
if (sample == 255) {
// reject hit
valid[i] = INVALID;
continue;
}
}
// accept hit
if (filtertype == filtertype_t::OCCLUSION) {
RTCRayN_geomID(ray, N, i) = 0;
} else {
RTCRayN_Ng_x(ray, N, i) = RTCHitN_Ng_x(potentialHit, N, i);
RTCRayN_Ng_y(ray, N, i) = RTCHitN_Ng_y(potentialHit, N, i);
RTCRayN_Ng_z(ray, N, i) = RTCHitN_Ng_z(potentialHit, N, i);
RTCRayN_instID(ray, N, i) = RTCHitN_instID(potentialHit, N, i);
RTCRayN_geomID(ray, N, i) = RTCHitN_geomID(potentialHit, N, i);
RTCRayN_primID(ray, N, i) = RTCHitN_primID(potentialHit, N, i);
RTCRayN_u(ray, N, i) = RTCHitN_u(potentialHit, N, i);
RTCRayN_v(ray, N, i) = RTCHitN_v(potentialHit, N, i);
RTCRayN_tfar(ray, N, i) = RTCHitN_t(potentialHit, N, i);
}
}
}
void
Embree_TraceInit(const bsp2_t *bsp)
{
@ -211,18 +267,18 @@ Embree_TraceInit(const bsp2_t *bsp)
Error("embree must be built with ray masks disabled");
}
scene = rtcDeviceNewScene(device, RTC_SCENE_STATIC | RTC_SCENE_COHERENT, RTC_INTERSECT1);
scene = rtcDeviceNewScene(device, RTC_SCENE_STATIC | RTC_SCENE_COHERENT, RTC_INTERSECT1 | RTC_INTERSECT_STREAM);
skygeom = CreateGeometry(bsp, scene, skyfaces);
solidgeom = CreateGeometry(bsp, scene, solidfaces);
fencegeom = CreateGeometry(bsp, scene, fencefaces);
selfshadowgeom = CreateGeometry(bsp, scene, selfshadowfaces);
rtcSetIntersectionFilterFunction(scene, fencegeom.geomID, Embree_FilterFunc);
rtcSetOcclusionFilterFunction(scene, fencegeom.geomID, Embree_FilterFunc);
rtcSetIntersectionFilterFunction(scene, selfshadowgeom.geomID, Embree_FilterFunc);
rtcSetOcclusionFilterFunction(scene, selfshadowgeom.geomID, Embree_FilterFunc);
rtcSetIntersectionFilterFunctionN(scene, fencegeom.geomID, Embree_FilterFuncN<filtertype_t::INTERSECTION>);
rtcSetOcclusionFilterFunctionN(scene, fencegeom.geomID, Embree_FilterFuncN<filtertype_t::OCCLUSION>);
rtcSetIntersectionFilterFunctionN(scene, selfshadowgeom.geomID, Embree_FilterFuncN<filtertype_t::INTERSECTION>);
rtcSetOcclusionFilterFunctionN(scene, selfshadowgeom.geomID, Embree_FilterFuncN<filtertype_t::OCCLUSION>);
rtcCommit (scene);
logprint("Embree_TraceInit: %d skyfaces %d solidfaces %d fencefaces %d selfshadowfaces\n",
@ -323,3 +379,154 @@ hittype_t Embree_DirtTrace(const vec3_t start, const vec3_t dirn, vec_t dist, co
return hittype_t::SOLID;
}
}
//enum class streamstate_t {
// READY, DID_OCCLUDE, DID_INTERSECT
//};
static void *q_aligned_malloc(size_t align, size_t size)
{
#ifdef _MSC_VER
return _aligned_malloc(size, align);
#else
void *ptr;
if (0 != posix_memalign(&ptr, align, size)) {
return nullptr;
}
return ptr;
#endif
}
static void q_aligned_free(void *ptr)
{
#ifdef _MSC_VER
_aligned_free(ptr);
#else
free(ptr);
#endif
}
class raystream_embree_t : public raystream_t {
private:
RTCRay *_rays;
float *_rays_maxdist;
int *_point_indices;
vec3_t *_ray_colors;
int _numrays;
int _maxrays;
// streamstate_t _state;
public:
raystream_embree_t(int maxRays) :
_rays { static_cast<RTCRay *>(q_aligned_malloc(16, sizeof(RTCRay) * maxRays)) },
_rays_maxdist { new float[maxRays] },
_point_indices { new int[maxRays] },
_ray_colors { static_cast<vec3_t *>(calloc(maxRays, sizeof(vec3_t))) },
_numrays { 0 },
_maxrays { maxRays } {}
//,
//_state { streamstate_t::READY } {}
~raystream_embree_t() {
q_aligned_free(_rays);
delete[] _rays_maxdist;
delete[] _point_indices;
free(_ray_colors);
}
virtual void pushRay(int i, const vec_t *origin, const vec3_t dir, float dist, const dmodel_t *selfshadow, const vec_t *color = nullptr) {
assert(_numrays<_maxrays);
_rays[_numrays] = SetupRay(origin, dir, dist, selfshadow);
_rays_maxdist[_numrays] = dist;
_point_indices[_numrays] = i;
if (color) {
VectorCopy(color, _ray_colors[_numrays]);
}
_numrays++;
}
virtual size_t numPushedRays() {
return _numrays;
}
virtual void tracePushedRaysOcclusion() {
//assert(_state == streamstate_t::READY);
if (!_numrays)
return;
const RTCIntersectContext ctx = {
.flags = RTC_INTERSECT_COHERENT,
.userRayExt = nullptr
};
rtcOccluded1M(scene, &ctx, _rays, _numrays, sizeof(RTCRay));
}
virtual void tracePushedRaysIntersection() {
if (!_numrays)
return;
const RTCIntersectContext ctx = {
.flags = RTC_INTERSECT_COHERENT,
.userRayExt = nullptr
};
rtcIntersect1M(scene, &ctx, _rays, _numrays, sizeof(RTCRay));
}
virtual bool getPushedRayOccluded(size_t j) {
assert(j < _maxrays);
return (_rays[j].geomID != RTC_INVALID_GEOMETRY_ID);
}
virtual float getPushedRayDist(size_t j) {
assert(j < _maxrays);
return _rays_maxdist[j];
}
virtual float getPushedRayHitDist(size_t j) {
assert(j < _maxrays);
return _rays[j].tfar;
}
virtual hittype_t getPushedRayHitType(size_t j) {
assert(j < _maxrays);
if (_rays[j].geomID == RTC_INVALID_GEOMETRY_ID) {
return hittype_t::NONE;
} else if (_rays[j].geomID == skygeom.geomID) {
return hittype_t::SKY;
} else {
return hittype_t::SOLID;
}
}
virtual void getPushedRayDir(size_t j, vec3_t out) {
assert(j < _maxrays);
for (int i=0; i<3; i++) {
out[i] = _rays[j].dir[i];
}
}
virtual int getPushedRayPointIndex(size_t j) {
// assert(_state != streamstate_t::READY);
assert(j < _maxrays);
return _point_indices[j];
}
virtual void getPushedRayColor(size_t j, vec3_t out) {
assert(j < _maxrays);
VectorCopy(_ray_colors[j], out);
}
virtual void clearPushedRays() {
_numrays = 0;
//_state = streamstate_t::READY;
}
};
raystream_t *Embree_MakeRayStream(int maxrays)
{
return new raystream_embree_t{maxrays};
}