light: experimental support for switchable bmodel shadows:

Current specs/limitations:
- only supported for sunlight and regular lights
- enabled with "_dynamicshadow" "1"
- writes the lightstyle to a "dynshadowstyle" entity key, hardcoded
- only handles one dynamic occluder for a given lightmap sample
- styled lights passing through a dynamic occluder turn non-styled
This commit is contained in:
Eric Wasylishen 2017-06-25 20:06:46 -06:00
parent 7236e47ef4
commit b71f769c0a
7 changed files with 119 additions and 28 deletions

View File

@ -167,6 +167,7 @@ extern byte thepalette[768];
/* tracelist is a std::vector of pointers to modelinfo_t to use for LOS tests */ /* tracelist is a std::vector of pointers to modelinfo_t to use for LOS tests */
extern std::vector<const modelinfo_t *> tracelist; extern std::vector<const modelinfo_t *> tracelist;
extern std::vector<const modelinfo_t *> selfshadowlist; extern std::vector<const modelinfo_t *> selfshadowlist;
extern std::vector<const modelinfo_t *> dynamicshadowlist;
extern int numDirtVectors; extern int numDirtVectors;
@ -196,7 +197,7 @@ public:
vec3_t offset; vec3_t offset;
public: public:
lockable_vec_t minlight, shadow, shadowself, dirt, phong, phong_angle, alpha; lockable_vec_t minlight, shadow, shadowself, dynamicshadow, dynshadowstyle, dirt, phong, phong_angle, alpha;
lockable_string_t minlight_exclude; lockable_string_t minlight_exclude;
lockable_vec3_t minlight_color; lockable_vec3_t minlight_color;
lockable_bool_t lightignore; lockable_bool_t lightignore;
@ -219,6 +220,8 @@ public:
minlight { "minlight", 0 }, minlight { "minlight", 0 },
shadow { "shadow", 0 }, shadow { "shadow", 0 },
shadowself { "shadowself", 0 }, shadowself { "shadowself", 0 },
dynamicshadow { "dynamicshadow", 0 },
dynshadowstyle { "dynshadowstyle", 0},
dirt { "dirt", 0 }, dirt { "dirt", 0 },
phong { "phong", 0 }, phong { "phong", 0 },
phong_angle { "phong_angle", 0 }, phong_angle { "phong_angle", 0 },
@ -232,7 +235,7 @@ public:
settingsdict_t settings() { settingsdict_t settings() {
return {{ return {{
&minlight, &shadow, &shadowself, &dirt, &phong, &phong_angle, &alpha, &minlight, &shadow, &shadowself, &dynamicshadow, &dynshadowstyle, &dirt, &phong, &phong_angle, &alpha,
&minlight_exclude, &minlight_color, &lightignore &minlight_exclude, &minlight_color, &lightignore
}}; }};
} }

View File

@ -68,6 +68,7 @@ public:
virtual int getPushedRayPointIndex(size_t j) = 0; virtual int getPushedRayPointIndex(size_t j) = 0;
virtual void getPushedRayColor(size_t j, vec3_t out) = 0; virtual void getPushedRayColor(size_t j, vec3_t out) = 0;
virtual void getPushedRayNormalContrib(size_t j, vec3_t out) = 0; virtual void getPushedRayNormalContrib(size_t j, vec3_t out) = 0;
virtual int getPushedRayDynamicStyle(size_t j) = 0;
virtual void clearPushedRays() = 0; virtual void clearPushedRays() = 0;
virtual ~raystream_t() {}; virtual ~raystream_t() {};

View File

@ -57,7 +57,7 @@ const char * light_t::classname() const {
* ============================================================================ * ============================================================================
*/ */
static std::map<std::string, int> lightstyleForTargetname; static std::vector<std::pair<std::string, int>> lightstyleForTargetname;
static bool IsSwitchableLightstyle(int style) { static bool IsSwitchableLightstyle(int style) {
return style >= LIGHT_TARGETS_START return style >= LIGHT_TARGETS_START
@ -85,16 +85,17 @@ std::string WorldValueForKey(const std::string &key)
/** /**
* Assigns a lightstyle number for the given non-empty targetname string * Assigns a lightstyle number for the given non-empty targetname string
* Reuses the existing lightstyle if this targetname was already assigned. * Reuses the existing lightstyle if this targetname was already assigned.
*
* Pass an empty string to generate a new unique lightstyle.
*/ */
static int static int
LightStyleForTargetname(const std::string &targetname) LightStyleForTargetname(const std::string &targetname)
{ {
Q_assert(targetname.size() > 0);
// check if already assigned // check if already assigned
auto it = lightstyleForTargetname.find(targetname); for (const auto &pr : lightstyleForTargetname) {
if (it != lightstyleForTargetname.end()) { if (pr.first == targetname && targetname.size() > 0) {
return it->second; return pr.second;
}
} }
// check if full // check if full
@ -104,7 +105,7 @@ LightStyleForTargetname(const std::string &targetname)
// generate a new style number and return it // generate a new style number and return it
const int newStylenum = LIGHT_TARGETS_START + lightstyleForTargetname.size(); const int newStylenum = LIGHT_TARGETS_START + lightstyleForTargetname.size();
lightstyleForTargetname[targetname] = newStylenum; lightstyleForTargetname.push_back(std::make_pair(targetname, newStylenum));
if (verbose_log) { if (verbose_log) {
logprint("%s: Allocated lightstyle %d for targetname '%s'\n", __func__, newStylenum, targetname.c_str()); logprint("%s: Allocated lightstyle %d for targetname '%s'\n", __func__, newStylenum, targetname.c_str());
@ -1008,6 +1009,15 @@ LoadEntities(const globalconfig_t &cfg, const bsp2_t *bsp)
} }
} }
// setup light styles for dynamic shadow entities
if (EntDict_StringForKey(entdict, "_dynamicshadow") == "1") {
std::string targetname = EntDict_StringForKey(entdict, "targetname");
// if targetname is "", generates a new unique lightstyle
const int style = LightStyleForTargetname(targetname);
// TODO: Configurable key?
entdict["dynshadowstyle"] = std::to_string(style);
}
// parse escape sequences // parse escape sequences
for (auto &epair : entdict) { for (auto &epair : entdict) {
epair.second = ParseEscapeSequences(epair.second); epair.second = ParseEscapeSequences(epair.second);

View File

@ -81,6 +81,7 @@ static byte *lux_file_end; // end of space for luxfile data
std::vector<modelinfo_t *> modelinfo; std::vector<modelinfo_t *> modelinfo;
std::vector<const modelinfo_t *> tracelist; std::vector<const modelinfo_t *> tracelist;
std::vector<const modelinfo_t *> selfshadowlist; std::vector<const modelinfo_t *> selfshadowlist;
std::vector<const modelinfo_t *> dynamicshadowlist;
int oversample = 1; int oversample = 1;
int write_litfile = 0; /* 0 for none, 1 for .lit, 2 for bspx, 3 for both */ int write_litfile = 0; /* 0 for none, 1 for .lit, 2 for bspx, 3 for both */
@ -274,6 +275,7 @@ FindModelInfo(const bsp2_t *bsp, const char *lmscaleoverride)
Q_assert(modelinfo.size() == 0); Q_assert(modelinfo.size() == 0);
Q_assert(tracelist.size() == 0); Q_assert(tracelist.size() == 0);
Q_assert(selfshadowlist.size() == 0); Q_assert(selfshadowlist.size() == 0);
Q_assert(dynamicshadowlist.size() == 0);
if (!bsp->nummodels) { if (!bsp->nummodels) {
Error("Corrupt .BSP: bsp->nummodels is 0!"); Error("Corrupt .BSP: bsp->nummodels is 0!");
@ -324,7 +326,12 @@ FindModelInfo(const bsp2_t *bsp, const char *lmscaleoverride)
info->settings().setSettings(*entdict, false); info->settings().setSettings(*entdict, false);
/* Check if this model will cast shadows (shadow => shadowself) */ /* Check if this model will cast shadows (shadow => shadowself) */
if (info->shadow.boolValue()) { if (info->dynamicshadow.boolValue()) {
Q_assert(info->dynshadowstyle.intValue() != 0);
logprint("Found a bmodel using dynamic shadow lightstyle: %d\n", info->dynshadowstyle.intValue());
dynamicshadowlist.push_back(info);
} else if (info->shadow.boolValue()) {
tracelist.push_back(info); tracelist.push_back(info);
} else if (info->shadowself.boolValue()){ } else if (info->shadowself.boolValue()){
selfshadowlist.push_back(info); selfshadowlist.push_back(info);

View File

@ -1328,8 +1328,6 @@ LightFace_Entity(const bsp2_t *bsp,
/* /*
* Check it for real * Check it for real
*/ */
bool hit = false;
lightmap_t *lightmap = Lightmap_ForStyle(lightmaps, entity->style.intValue(), lightsurf);
const dmodel_t *shadowself = modelinfo->shadowself.boolValue() ? modelinfo->model : NULL; const dmodel_t *shadowself = modelinfo->shadowself.boolValue() ? modelinfo->model : NULL;
raystream_t *rs = lightsurf->stream; raystream_t *rs = lightsurf->stream;
@ -1362,6 +1360,8 @@ LightFace_Entity(const bsp2_t *bsp,
rs->tracePushedRaysOcclusion(); rs->tracePushedRaysOcclusion();
total_light_rays += rs->numPushedRays(); total_light_rays += rs->numPushedRays();
lightmap_t *lightmap_world = Lightmap_ForStyle(lightmaps, entity->style.intValue(), lightsurf);
const int N = rs->numPushedRays(); const int N = rs->numPushedRays();
for (int j = 0; j < N; j++) { for (int j = 0; j < N; j++) {
if (rs->getPushedRayOccluded(j)) { if (rs->getPushedRayOccluded(j)) {
@ -1371,6 +1371,19 @@ LightFace_Entity(const bsp2_t *bsp,
total_light_ray_hits++; total_light_ray_hits++;
int i = rs->getPushedRayPointIndex(j); int i = rs->getPushedRayPointIndex(j);
// check if we hit a dynamic shadow caster
lightmap_t *lightmap;
int style;
if (rs->getPushedRayDynamicStyle(j) != 0) {
style = rs->getPushedRayDynamicStyle(j);
lightmap = Lightmap_ForStyle(lightmaps, style, lightsurf);
} else {
// regular case
style = entity->style.intValue();
lightmap = lightmap_world;
}
lightsample_t *sample = &lightmap->samples[i]; lightsample_t *sample = &lightmap->samples[i];
vec3_t color, normalcontrib; vec3_t color, normalcontrib;
@ -1380,11 +1393,8 @@ LightFace_Entity(const bsp2_t *bsp,
VectorAdd(sample->color, color, sample->color); VectorAdd(sample->color, color, sample->color);
VectorAdd(sample->direction, normalcontrib, sample->direction); VectorAdd(sample->direction, normalcontrib, sample->direction);
hit = true; Lightmap_Save(lightmaps, lightsurf, lightmap, style);
} }
if (hit)
Lightmap_Save(lightmaps, lightsurf, lightmap, entity->style.intValue());
} }
/* /*
@ -1404,16 +1414,12 @@ LightFace_Sky(const sun_t *sun, const lightsurf_t *lightsurf, lightmapdict_t *li
if (DotProduct(sun->sunvec, plane->normal) < -ANGLE_EPSILON && !lightsurf->curved && !lightsurf->twosided) { if (DotProduct(sun->sunvec, plane->normal) < -ANGLE_EPSILON && !lightsurf->curved && !lightsurf->twosided) {
return; return;
} }
/* if sunlight is set, use a style 0 light map */
lightmap_t *lightmap = Lightmap_ForStyle(lightmaps, 0, lightsurf);
vec3_t incoming; vec3_t incoming;
VectorCopy(sun->sunvec, incoming); VectorCopy(sun->sunvec, incoming);
VectorNormalize(incoming); VectorNormalize(incoming);
/* Check each point... */ /* Check each point... */
bool hit = false;
const dmodel_t *shadowself = modelinfo->shadowself.boolValue() ? modelinfo->model : NULL; const dmodel_t *shadowself = modelinfo->shadowself.boolValue() ? modelinfo->model : NULL;
raystream_t *rs = lightsurf->stream; raystream_t *rs = lightsurf->stream;
@ -1457,6 +1463,9 @@ LightFace_Sky(const sun_t *sun, const lightsurf_t *lightsurf, lightmapdict_t *li
rs->tracePushedRaysIntersection(); rs->tracePushedRaysIntersection();
/* if sunlight is set, use a style 0 light map */
lightmap_t *lightmap_world = Lightmap_ForStyle(lightmaps, 0, lightsurf);
const int N = rs->numPushedRays(); const int N = rs->numPushedRays();
for (int j = 0; j < N; j++) { for (int j = 0; j < N; j++) {
if (rs->getPushedRayHitType(j) != hittype_t::SKY) { if (rs->getPushedRayHitType(j) != hittype_t::SKY) {
@ -1464,6 +1473,17 @@ LightFace_Sky(const sun_t *sun, const lightsurf_t *lightsurf, lightmapdict_t *li
} }
const int i = rs->getPushedRayPointIndex(j); const int i = rs->getPushedRayPointIndex(j);
// check if we hit a dynamic shadow caster
lightmap_t *lightmap;
const int style = rs->getPushedRayDynamicStyle(j);
if (style != 0) {
lightmap = Lightmap_ForStyle(lightmaps, style, lightsurf);
} else {
// regular case
lightmap = lightmap_world;
}
lightsample_t *sample = &lightmap->samples[i]; lightsample_t *sample = &lightmap->samples[i];
vec3_t color, normalcontrib; vec3_t color, normalcontrib;
@ -1473,11 +1493,8 @@ LightFace_Sky(const sun_t *sun, const lightsurf_t *lightsurf, lightmapdict_t *li
VectorAdd(sample->color, color, sample->color); VectorAdd(sample->color, color, sample->color);
VectorAdd(sample->direction, normalcontrib, sample->direction); VectorAdd(sample->direction, normalcontrib, sample->direction);
hit = true; Lightmap_Save(lightmaps, lightsurf, lightmap, style);
} }
if (hit)
Lightmap_Save(lightmaps, lightsurf, lightmap, 0);
} }
/* /*

View File

@ -888,6 +888,10 @@ public:
VectorCopy(_rays.at(j)._normalcontrib, out); VectorCopy(_rays.at(j)._normalcontrib, out);
} }
virtual int getPushedRayDynamicStyle(size_t j) {
return 0; // not supported
}
virtual void clearPushedRays() { virtual void clearPushedRays() {
_rays.clear(); _rays.clear();
} }

View File

@ -159,7 +159,7 @@ CreateGeometryFromWindings(RTCScene scene, const std::vector<winding_t *> &windi
RTCDevice device; RTCDevice device;
RTCScene scene; RTCScene scene;
/* global shadow casters */ /* global shadow casters */
sceneinfo skygeom, solidgeom, fencegeom, selfshadowgeom; sceneinfo skygeom, solidgeom, fencegeom, selfshadowgeom, dynamicshadowgeom;
static const bsp2_t *bsp_static; static const bsp2_t *bsp_static;
@ -179,6 +179,8 @@ Embree_SceneinfoForGeomID(unsigned int geomID)
return fencegeom; return fencegeom;
} else if (geomID == selfshadowgeom.geomID) { } else if (geomID == selfshadowgeom.geomID) {
return selfshadowgeom; return selfshadowgeom;
} else if (geomID == dynamicshadowgeom.geomID) {
return dynamicshadowgeom;
} else { } else {
Error("unexpected geomID"); Error("unexpected geomID");
} }
@ -223,6 +225,8 @@ enum class filtertype_t {
void AddGlassToRay(const RTCIntersectContext* context, unsigned rayIndex, float opacity, const vec3_t glasscolor); void AddGlassToRay(const RTCIntersectContext* context, unsigned rayIndex, float opacity, const vec3_t glasscolor);
void AddDynamicOccluderToRay(const RTCIntersectContext* context, unsigned rayIndex, int style);
// called to evaluate transparency // called to evaluate transparency
template<filtertype_t filtertype> template<filtertype_t filtertype>
static void static void
@ -257,6 +261,17 @@ Embree_FilterFuncN(int* valid,
valid[i] = INVALID; valid[i] = INVALID;
continue; continue;
} }
} else if (geomID == dynamicshadowgeom.geomID) {
// we hit a dynamic shadow caster. reject the hit, but store the
// info about what we hit.
const modelinfo_t *modelinfo = Embree_LookupModelinfo(geomID, primID);
int style = modelinfo->dynshadowstyle.intValue();
AddDynamicOccluderToRay(context, rayIndex, style);
// reject hit
valid[i] = INVALID;
continue;
} else { } else {
// test fence textures and glass // test fence textures and glass
const bsp2_dface_t *face = Embree_LookupFace(geomID, primID); const bsp2_dface_t *face = Embree_LookupFace(geomID, primID);
@ -511,7 +526,7 @@ Embree_TraceInit(const bsp2_t *bsp)
bsp_static = bsp; bsp_static = bsp;
Q_assert(device == nullptr); Q_assert(device == nullptr);
std::vector<const bsp2_dface_t *> skyfaces, solidfaces, fencefaces, selfshadowfaces; std::vector<const bsp2_dface_t *> skyfaces, solidfaces, fencefaces, selfshadowfaces, dynamicshadowfaces;
/* Check against the list of global shadow casters */ /* Check against the list of global shadow casters */
for (const modelinfo_t *model : tracelist) { for (const modelinfo_t *model : tracelist) {
@ -552,6 +567,14 @@ Embree_TraceInit(const bsp2_t *bsp)
selfshadowfaces.push_back(face); selfshadowfaces.push_back(face);
} }
} }
/* Dynamic-shadow models */
for (const modelinfo_t *model : dynamicshadowlist) {
for (int i=0; i<model->model->numfaces; i++) {
const bsp2_dface_t *face = BSP_GetFace(bsp, model->model->firstface + i);
dynamicshadowfaces.push_back(face);
}
}
/* Special handling of skip-textured bmodels */ /* Special handling of skip-textured bmodels */
std::vector<winding_t *> skipwindings; std::vector<winding_t *> skipwindings;
@ -584,6 +607,7 @@ Embree_TraceInit(const bsp2_t *bsp)
solidgeom = CreateGeometry(bsp, scene, solidfaces); solidgeom = CreateGeometry(bsp, scene, solidfaces);
fencegeom = CreateGeometry(bsp, scene, fencefaces); fencegeom = CreateGeometry(bsp, scene, fencefaces);
selfshadowgeom = CreateGeometry(bsp, scene, selfshadowfaces); selfshadowgeom = CreateGeometry(bsp, scene, selfshadowfaces);
dynamicshadowgeom = CreateGeometry(bsp, scene, dynamicshadowfaces);
CreateGeometryFromWindings(scene, skipwindings); CreateGeometryFromWindings(scene, skipwindings);
rtcSetIntersectionFilterFunctionN(scene, fencegeom.geomID, Embree_FilterFuncN<filtertype_t::INTERSECTION>); rtcSetIntersectionFilterFunctionN(scene, fencegeom.geomID, Embree_FilterFuncN<filtertype_t::INTERSECTION>);
@ -592,13 +616,17 @@ Embree_TraceInit(const bsp2_t *bsp)
rtcSetIntersectionFilterFunctionN(scene, selfshadowgeom.geomID, Embree_FilterFuncN<filtertype_t::INTERSECTION>); rtcSetIntersectionFilterFunctionN(scene, selfshadowgeom.geomID, Embree_FilterFuncN<filtertype_t::INTERSECTION>);
rtcSetOcclusionFilterFunctionN(scene, selfshadowgeom.geomID, Embree_FilterFuncN<filtertype_t::OCCLUSION>); rtcSetOcclusionFilterFunctionN(scene, selfshadowgeom.geomID, Embree_FilterFuncN<filtertype_t::OCCLUSION>);
rtcSetIntersectionFilterFunctionN(scene, dynamicshadowgeom.geomID, Embree_FilterFuncN<filtertype_t::INTERSECTION>);
rtcSetOcclusionFilterFunctionN(scene, dynamicshadowgeom.geomID, Embree_FilterFuncN<filtertype_t::OCCLUSION>);
rtcCommit (scene); rtcCommit (scene);
logprint("Embree_TraceInit: %d skyfaces %d solidfaces %d fencefaces %d selfshadowfaces %d skipwindings\n", logprint("Embree_TraceInit: %d skyfaces %d solidfaces %d fencefaces %d selfshadowfaces %d dynamicshadowfaces %d skipwindings\n",
(int)skyfaces.size(), (int)skyfaces.size(),
(int)solidfaces.size(), (int)solidfaces.size(),
(int)fencefaces.size(), (int)fencefaces.size(),
(int)selfshadowfaces.size(), (int)selfshadowfaces.size(),
(int)dynamicshadowfaces.size(),
(int)skipwindings.size()); (int)skipwindings.size());
FreeWindings(skipwindings); FreeWindings(skipwindings);
@ -732,6 +760,13 @@ public:
int *_point_indices; int *_point_indices;
vec3_t *_ray_colors; vec3_t *_ray_colors;
vec3_t *_ray_normalcontribs; vec3_t *_ray_normalcontribs;
// This is set to the modelinfo's dynshadowstyle if the ray hit
// a dynamic shadow caster. (note that for rays that hit dynamic
// shadow casters, all of the other hit data is assuming the ray went
// straight through).
int *_ray_dynamic_styles;
int _numrays; int _numrays;
int _maxrays; int _maxrays;
// streamstate_t _state; // streamstate_t _state;
@ -743,6 +778,7 @@ public:
_point_indices { new int[maxRays] }, _point_indices { new int[maxRays] },
_ray_colors { static_cast<vec3_t *>(calloc(maxRays, sizeof(vec3_t))) }, _ray_colors { static_cast<vec3_t *>(calloc(maxRays, sizeof(vec3_t))) },
_ray_normalcontribs { static_cast<vec3_t *>(calloc(maxRays, sizeof(vec3_t))) }, _ray_normalcontribs { static_cast<vec3_t *>(calloc(maxRays, sizeof(vec3_t))) },
_ray_dynamic_styles { new int[maxRays] },
_numrays { 0 }, _numrays { 0 },
_maxrays { maxRays } {} _maxrays { maxRays } {}
//, //,
@ -754,6 +790,7 @@ public:
delete[] _point_indices; delete[] _point_indices;
free(_ray_colors); free(_ray_colors);
free(_ray_normalcontribs); free(_ray_normalcontribs);
delete[] _ray_dynamic_styles;
} }
virtual void pushRay(int i, const vec_t *origin, const vec3_t dir, float dist, const dmodel_t *selfshadow, const vec_t *color = nullptr, const vec_t *normalcontrib = nullptr) { virtual void pushRay(int i, const vec_t *origin, const vec3_t dir, float dist, const dmodel_t *selfshadow, const vec_t *color = nullptr, const vec_t *normalcontrib = nullptr) {
@ -767,6 +804,7 @@ public:
if (normalcontrib) { if (normalcontrib) {
VectorCopy(normalcontrib, _ray_normalcontribs[_numrays]); VectorCopy(normalcontrib, _ray_normalcontribs[_numrays]);
} }
_ray_dynamic_styles[_numrays] = 0;
_numrays++; _numrays++;
} }
@ -865,6 +903,11 @@ public:
VectorCopy(_ray_normalcontribs[j], out); VectorCopy(_ray_normalcontribs[j], out);
} }
virtual int getPushedRayDynamicStyle(size_t j) {
Q_assert(j < _maxrays);
return _ray_dynamic_styles[j];
}
virtual void clearPushedRays() { virtual void clearPushedRays() {
_numrays = 0; _numrays = 0;
//_state = streamstate_t::READY; //_state = streamstate_t::READY;
@ -911,3 +954,9 @@ void AddGlassToRay(const RTCIntersectContext* context, unsigned rayIndex, float
// use the lerped color // use the lerped color
VectorCopy(lerped, rs->_ray_colors[rayIndex]); VectorCopy(lerped, rs->_ray_colors[rayIndex]);
} }
void AddDynamicOccluderToRay(const RTCIntersectContext* context, unsigned rayIndex, int style)
{
raystream_embree_t *rs = static_cast<raystream_embree_t *>(context->userRayExt);
rs->_ray_dynamic_styles[rayIndex] = style;
}