From 882348ac0a9f43abb0200ba2f95950866944ea3d Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Sat, 30 Jul 2016 00:40:45 -0600 Subject: [PATCH] light: use embree ray stream api --- include/light/light.hh | 34 +++- light/ltface.cc | 379 ++++++++++++++++++++++++++--------------- light/trace.cc | 122 +++++++++++++ light/trace_embree.cc | 271 +++++++++++++++++++++++++---- 4 files changed, 631 insertions(+), 175 deletions(-) diff --git a/include/light/light.hh b/include/light/light.hh index b59c9bec..cfc831f2 100644 --- a/include/light/light.hh +++ b/include/light/light.hh @@ -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 { diff --git a/light/ltface.cc b/light/ltface.cc index 2041b07b..a583713e 100644 --- a/light/ltface.cc +++ b/light/ltface.cc @@ -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; jstream; + 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); } diff --git a/light/trace.cc b/light/trace.cc index ad4c7ea3..9c5379ec 100644 --- a/light/trace.cc +++ b/light/trace.cc @@ -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 _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 diff --git a/light/trace_embree.cc b/light/trace_embree.cc index 0ba23661..41cf59e6 100644 --- a/light/trace_embree.cc +++ b/light/trace_embree.cc @@ -22,8 +22,13 @@ #include #include #include +#include #include +#ifdef _MSC_VER +#include +#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 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); + rtcSetOcclusionFilterFunctionN(scene, fencegeom.geomID, Embree_FilterFuncN); + rtcSetIntersectionFilterFunctionN(scene, selfshadowgeom.geomID, Embree_FilterFuncN); + rtcSetOcclusionFilterFunctionN(scene, selfshadowgeom.geomID, Embree_FilterFuncN); + 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(q_aligned_malloc(16, sizeof(RTCRay) * maxRays)) }, + _rays_maxdist { new float[maxRays] }, + _point_indices { new int[maxRays] }, + _ray_colors { static_cast(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}; +}