diff --git a/include/light/light.hh b/include/light/light.hh index f144cdc5..30a6df9e 100644 --- a/include/light/light.hh +++ b/include/light/light.hh @@ -167,6 +167,7 @@ extern byte thepalette[768]; /* tracelist is a std::vector of pointers to modelinfo_t to use for LOS tests */ extern std::vector tracelist; extern std::vector selfshadowlist; +extern std::vector dynamicshadowlist; extern int numDirtVectors; @@ -196,7 +197,7 @@ public: vec3_t offset; 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_vec3_t minlight_color; lockable_bool_t lightignore; @@ -219,6 +220,8 @@ public: minlight { "minlight", 0 }, shadow { "shadow", 0 }, shadowself { "shadowself", 0 }, + dynamicshadow { "dynamicshadow", 0 }, + dynshadowstyle { "dynshadowstyle", 0}, dirt { "dirt", 0 }, phong { "phong", 0 }, phong_angle { "phong_angle", 0 }, @@ -232,7 +235,7 @@ public: settingsdict_t settings() { return {{ - &minlight, &shadow, &shadowself, &dirt, &phong, &phong_angle, &alpha, + &minlight, &shadow, &shadowself, &dynamicshadow, &dynshadowstyle, &dirt, &phong, &phong_angle, &alpha, &minlight_exclude, &minlight_color, &lightignore }}; } diff --git a/include/light/trace.hh b/include/light/trace.hh index 66e7fd92..a29016ee 100644 --- a/include/light/trace.hh +++ b/include/light/trace.hh @@ -68,6 +68,7 @@ public: virtual int getPushedRayPointIndex(size_t j) = 0; virtual void getPushedRayColor(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 ~raystream_t() {}; diff --git a/light/entities.cc b/light/entities.cc index f3492e64..400fa060 100644 --- a/light/entities.cc +++ b/light/entities.cc @@ -57,7 +57,7 @@ const char * light_t::classname() const { * ============================================================================ */ -static std::map lightstyleForTargetname; +static std::vector> lightstyleForTargetname; static bool IsSwitchableLightstyle(int style) { 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 * Reuses the existing lightstyle if this targetname was already assigned. + * + * Pass an empty string to generate a new unique lightstyle. */ static int LightStyleForTargetname(const std::string &targetname) { - Q_assert(targetname.size() > 0); - // check if already assigned - auto it = lightstyleForTargetname.find(targetname); - if (it != lightstyleForTargetname.end()) { - return it->second; + for (const auto &pr : lightstyleForTargetname) { + if (pr.first == targetname && targetname.size() > 0) { + return pr.second; + } } // check if full @@ -104,7 +105,7 @@ LightStyleForTargetname(const std::string &targetname) // generate a new style number and return it const int newStylenum = LIGHT_TARGETS_START + lightstyleForTargetname.size(); - lightstyleForTargetname[targetname] = newStylenum; + lightstyleForTargetname.push_back(std::make_pair(targetname, newStylenum)); if (verbose_log) { 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 for (auto &epair : entdict) { epair.second = ParseEscapeSequences(epair.second); diff --git a/light/light.cc b/light/light.cc index 077f65ed..f3dcf166 100644 --- a/light/light.cc +++ b/light/light.cc @@ -81,6 +81,7 @@ static byte *lux_file_end; // end of space for luxfile data std::vector modelinfo; std::vector tracelist; std::vector selfshadowlist; +std::vector dynamicshadowlist; int oversample = 1; 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(tracelist.size() == 0); Q_assert(selfshadowlist.size() == 0); + Q_assert(dynamicshadowlist.size() == 0); if (!bsp->nummodels) { Error("Corrupt .BSP: bsp->nummodels is 0!"); @@ -324,7 +326,12 @@ FindModelInfo(const bsp2_t *bsp, const char *lmscaleoverride) info->settings().setSettings(*entdict, false); /* 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); } else if (info->shadowself.boolValue()){ selfshadowlist.push_back(info); diff --git a/light/ltface.cc b/light/ltface.cc index 61c8bbc2..2c427f95 100644 --- a/light/ltface.cc +++ b/light/ltface.cc @@ -1328,8 +1328,6 @@ LightFace_Entity(const bsp2_t *bsp, /* * 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; raystream_t *rs = lightsurf->stream; @@ -1362,6 +1360,8 @@ LightFace_Entity(const bsp2_t *bsp, rs->tracePushedRaysOcclusion(); total_light_rays += rs->numPushedRays(); + lightmap_t *lightmap_world = Lightmap_ForStyle(lightmaps, entity->style.intValue(), lightsurf); + const int N = rs->numPushedRays(); for (int j = 0; j < N; j++) { if (rs->getPushedRayOccluded(j)) { @@ -1371,6 +1371,19 @@ LightFace_Entity(const bsp2_t *bsp, total_light_ray_hits++; 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]; vec3_t color, normalcontrib; @@ -1380,11 +1393,8 @@ LightFace_Entity(const bsp2_t *bsp, VectorAdd(sample->color, color, sample->color); 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) { return; } - - /* if sunlight is set, use a style 0 light map */ - lightmap_t *lightmap = Lightmap_ForStyle(lightmaps, 0, lightsurf); - + vec3_t incoming; VectorCopy(sun->sunvec, incoming); VectorNormalize(incoming); /* Check each point... */ - bool hit = false; const dmodel_t *shadowself = modelinfo->shadowself.boolValue() ? modelinfo->model : NULL; raystream_t *rs = lightsurf->stream; @@ -1457,6 +1463,9 @@ LightFace_Sky(const sun_t *sun, const lightsurf_t *lightsurf, lightmapdict_t *li 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(); for (int j = 0; j < N; j++) { 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); + + // 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]; 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->direction, normalcontrib, sample->direction); - hit = true; + Lightmap_Save(lightmaps, lightsurf, lightmap, style); } - - if (hit) - Lightmap_Save(lightmaps, lightsurf, lightmap, 0); } /* diff --git a/light/trace.cc b/light/trace.cc index 4bd327ef..3c15ef0a 100644 --- a/light/trace.cc +++ b/light/trace.cc @@ -888,6 +888,10 @@ public: VectorCopy(_rays.at(j)._normalcontrib, out); } + virtual int getPushedRayDynamicStyle(size_t j) { + return 0; // not supported + } + virtual void clearPushedRays() { _rays.clear(); } diff --git a/light/trace_embree.cc b/light/trace_embree.cc index ea94dc57..a7d9a503 100644 --- a/light/trace_embree.cc +++ b/light/trace_embree.cc @@ -159,7 +159,7 @@ CreateGeometryFromWindings(RTCScene scene, const std::vector &windi RTCDevice device; RTCScene scene; /* global shadow casters */ -sceneinfo skygeom, solidgeom, fencegeom, selfshadowgeom; +sceneinfo skygeom, solidgeom, fencegeom, selfshadowgeom, dynamicshadowgeom; static const bsp2_t *bsp_static; @@ -179,6 +179,8 @@ Embree_SceneinfoForGeomID(unsigned int geomID) return fencegeom; } else if (geomID == selfshadowgeom.geomID) { return selfshadowgeom; + } else if (geomID == dynamicshadowgeom.geomID) { + return dynamicshadowgeom; } else { 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 AddDynamicOccluderToRay(const RTCIntersectContext* context, unsigned rayIndex, int style); + // called to evaluate transparency template static void @@ -257,6 +261,17 @@ Embree_FilterFuncN(int* valid, valid[i] = INVALID; 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 { // test fence textures and glass const bsp2_dface_t *face = Embree_LookupFace(geomID, primID); @@ -511,7 +526,7 @@ Embree_TraceInit(const bsp2_t *bsp) bsp_static = bsp; Q_assert(device == nullptr); - std::vector skyfaces, solidfaces, fencefaces, selfshadowfaces; + std::vector skyfaces, solidfaces, fencefaces, selfshadowfaces, dynamicshadowfaces; /* Check against the list of global shadow casters */ for (const modelinfo_t *model : tracelist) { @@ -552,6 +567,14 @@ Embree_TraceInit(const bsp2_t *bsp) selfshadowfaces.push_back(face); } } + + /* Dynamic-shadow models */ + for (const modelinfo_t *model : dynamicshadowlist) { + for (int i=0; imodel->numfaces; i++) { + const bsp2_dface_t *face = BSP_GetFace(bsp, model->model->firstface + i); + dynamicshadowfaces.push_back(face); + } + } /* Special handling of skip-textured bmodels */ std::vector skipwindings; @@ -584,6 +607,7 @@ Embree_TraceInit(const bsp2_t *bsp) solidgeom = CreateGeometry(bsp, scene, solidfaces); fencegeom = CreateGeometry(bsp, scene, fencefaces); selfshadowgeom = CreateGeometry(bsp, scene, selfshadowfaces); + dynamicshadowgeom = CreateGeometry(bsp, scene, dynamicshadowfaces); CreateGeometryFromWindings(scene, skipwindings); rtcSetIntersectionFilterFunctionN(scene, fencegeom.geomID, Embree_FilterFuncN); @@ -592,13 +616,17 @@ Embree_TraceInit(const bsp2_t *bsp) rtcSetIntersectionFilterFunctionN(scene, selfshadowgeom.geomID, Embree_FilterFuncN); rtcSetOcclusionFilterFunctionN(scene, selfshadowgeom.geomID, Embree_FilterFuncN); + rtcSetIntersectionFilterFunctionN(scene, dynamicshadowgeom.geomID, Embree_FilterFuncN); + rtcSetOcclusionFilterFunctionN(scene, dynamicshadowgeom.geomID, Embree_FilterFuncN); + 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)solidfaces.size(), (int)fencefaces.size(), (int)selfshadowfaces.size(), + (int)dynamicshadowfaces.size(), (int)skipwindings.size()); FreeWindings(skipwindings); @@ -732,6 +760,13 @@ public: int *_point_indices; vec3_t *_ray_colors; 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 _maxrays; // streamstate_t _state; @@ -743,6 +778,7 @@ public: _point_indices { new int[maxRays] }, _ray_colors { static_cast(calloc(maxRays, sizeof(vec3_t))) }, _ray_normalcontribs { static_cast(calloc(maxRays, sizeof(vec3_t))) }, + _ray_dynamic_styles { new int[maxRays] }, _numrays { 0 }, _maxrays { maxRays } {} //, @@ -754,6 +790,7 @@ public: delete[] _point_indices; free(_ray_colors); 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) { @@ -767,6 +804,7 @@ public: if (normalcontrib) { VectorCopy(normalcontrib, _ray_normalcontribs[_numrays]); } + _ray_dynamic_styles[_numrays] = 0; _numrays++; } @@ -865,6 +903,11 @@ public: VectorCopy(_ray_normalcontribs[j], out); } + virtual int getPushedRayDynamicStyle(size_t j) { + Q_assert(j < _maxrays); + return _ray_dynamic_styles[j]; + } + virtual void clearPushedRays() { _numrays = 0; //_state = streamstate_t::READY; @@ -911,3 +954,9 @@ void AddGlassToRay(const RTCIntersectContext* context, unsigned rayIndex, float // use the lerped color VectorCopy(lerped, rs->_ray_colors[rayIndex]); } + +void AddDynamicOccluderToRay(const RTCIntersectContext* context, unsigned rayIndex, int style) +{ + raystream_embree_t *rs = static_cast(context->userRayExt); + rs->_ray_dynamic_styles[rayIndex] = style; +}