diff --git a/include/light/light.hh b/include/light/light.hh index 0839145a..640f7439 100644 --- a/include/light/light.hh +++ b/include/light/light.hh @@ -74,6 +74,7 @@ public: float anglescale; int style; std::string suntexture; + const img::texture *suntexture_value; }; /* for vanilla this would be 18. some engines allow higher limits though, which will be needed if we're scaling lightmap diff --git a/include/light/trace.hh b/include/light/trace.hh index 72a67d3d..b9901fde 100644 --- a/include/light/trace.hh +++ b/include/light/trace.hh @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -42,7 +43,7 @@ enum class hittype_t : uint8_t }; uint32_t clamp_texcoord(vec_t in, uint32_t width); -qvec4b SampleTexture(const mface_t *face, const mbsp_t *bsp, const qvec3d &point); // mxd. Palette index -> RGBA +qvec4b SampleTexture(const mface_t *face, const mtexinfo_t *tex, const img::texture *texture, const mbsp_t *bsp, const qvec3d &point); // mxd. Palette index -> RGBA class modelinfo_t; diff --git a/include/light/trace_embree.hh b/include/light/trace_embree.hh index ec90b97d..8d331321 100644 --- a/include/light/trace_embree.hh +++ b/include/light/trace_embree.hh @@ -142,13 +142,27 @@ struct ray_source_info : public RTCIntersectContext } }; -class sceneinfo +struct triinfo +{ + const modelinfo_t *modelinfo; + const mface_t *face; + const mtexinfo_t *texinfo; + + const img::texture *texture; + float alpha; + bool is_fence, is_glass; + + bool shadowworldonly; + bool shadowself; + bool switchableshadow; + int32_t switchshadstyle; +}; + +struct sceneinfo { -public: unsigned geomID; - std::vector triToFace; - std::vector triToModelinfo; + std::vector triInfo; }; extern sceneinfo skygeom; // sky. always occludes. @@ -240,17 +254,18 @@ public: } } - inline const mface_t *getPushedRayHitFace(size_t j) + inline const triinfo *getPushedRayHitFaceInfo(size_t j) { Q_assert(j < _maxrays); const RTCRayHit &ray = _rays[j]; - if (ray.hit.geomID == RTC_INVALID_GEOMETRY_ID) + if (ray.hit.geomID == RTC_INVALID_GEOMETRY_ID) { return nullptr; + } const sceneinfo &si = Embree_SceneinfoForGeomID(ray.hit.geomID); - const mface_t *face = si.triToFace.at(ray.hit.primID); + const triinfo *face = &si.triInfo.at(ray.hit.primID); Q_assert(face != nullptr); return face; diff --git a/light/entities.cc b/light/entities.cc index 26871143..007070a1 100644 --- a/light/entities.cc +++ b/light/entities.cc @@ -276,6 +276,7 @@ static void AddSun(const settings::worldspawn_keys &cfg, const qvec3d &sunvec, v sun.dirt = Dirt_ResolveFlag(cfg, dirtInt); sun.style = style; sun.suntexture = suntexture; + sun.suntexture_value = img::find(suntexture); // fmt::print( "sun is using vector {} {} {} light {} color {} {} {} anglescale {} dirt {} resolved to {}\n", // sun->sunvec[0], sun->sunvec[1], sun->sunvec[2], sun->sunlight.light, diff --git a/light/ltface.cc b/light/ltface.cc index d4c9298f..854e29f8 100644 --- a/light/ltface.cc +++ b/light/ltface.cc @@ -1620,11 +1620,9 @@ static void LightFace_Sky(const sun_t *sun, lightsurf_t *lightsurf, lightmapdict } // check if we hit the wrong texture - // TODO: this could be faster! - if (!sun->suntexture.empty()) { - const mface_t *face = rs.getPushedRayHitFace(j); - const char *facetex = Face_TextureName(lightsurf->bsp, face); - if (sun->suntexture != facetex) { + if (sun->suntexture_value) { + const triinfo *face = rs.getPushedRayHitFaceInfo(j); + if (sun->suntexture_value != face->texture) { continue; } } diff --git a/light/trace.cc b/light/trace.cc index a45afcf0..677e0c47 100644 --- a/light/trace.cc +++ b/light/trace.cc @@ -76,16 +76,12 @@ uint32_t clamp_texcoord(vec_t in, uint32_t width) } } -qvec4b SampleTexture(const mface_t *face, const mbsp_t *bsp, const qvec3d &point) +qvec4b SampleTexture(const mface_t *face, const mtexinfo_t *tex, const img::texture *texture, const mbsp_t *bsp, const qvec3d &point) { - const auto *texture = Face_Texture(bsp, face); - if (texture == nullptr || !texture->meta.width) { return {}; } - const mtexinfo_t *tex = &bsp->texinfo[face->texinfo]; - qvec2d texcoord = WorldToTexCoord(point, tex); const uint32_t x = clamp_texcoord(texcoord[0], texture->meta.width); diff --git a/light/trace_embree.cc b/light/trace_embree.cc index 9ff97ba3..fdbcecac 100644 --- a/light/trace_embree.cc +++ b/light/trace_embree.cc @@ -116,8 +116,37 @@ sceneinfo CreateGeometry( tri->v2 = Face_VertexAtIndex(bsp, face, 0); tri_index++; - s.triToFace.push_back(face); - s.triToModelinfo.push_back(modelinfo); + triinfo info; + + info.face = face; + info.modelinfo = modelinfo; + info.texinfo = &bsp->texinfo[face->texinfo]; + + info.texture = Face_Texture(bsp, face); + + info.shadowworldonly = modelinfo->shadowworldonly.boolValue(); + info.shadowself = modelinfo->shadowself.boolValue(); + info.switchableshadow = modelinfo->switchableshadow.boolValue(); + info.switchshadstyle = modelinfo->switchshadstyle.value(); + + info.alpha = Face_Alpha(modelinfo, face); + + // mxd + if (bsp->loadversion->game->id == GAME_QUAKE_II) { + const int surf_flags = Face_ContentsOrSurfaceFlags(bsp, face); + info.is_fence = ((surf_flags & Q2_SURF_TRANSLUCENT) == + Q2_SURF_TRANSLUCENT); // KMQuake 2-specific. Use texture alpha chanel when both flags are set. + info.is_glass = !info.is_fence && (surf_flags & Q2_SURF_TRANSLUCENT); + if (info.is_glass) { + info.alpha = (surf_flags & Q2_SURF_TRANS33 ? 0.33f : 0.66f); + } + } else { + const char *name = Face_TextureName(bsp, face); + info.is_fence = (name[0] == '{'); + info.is_glass = (info.alpha < 1.0f); + } + + s.triInfo.push_back(info); } } @@ -202,16 +231,10 @@ void ErrorCallback(void *userptr, const RTCError code, const char *str) fmt::print("RTC Error {}: {}\n", code, str); } -const mface_t *Embree_LookupFace(unsigned int geomID, unsigned int primID) +const triinfo &Embree_LookupTriangleInfo(unsigned int geomID, unsigned int primID) { const sceneinfo &info = Embree_SceneinfoForGeomID(geomID); - return info.triToFace.at(primID); -} - -const modelinfo_t *Embree_LookupModelinfo(unsigned int geomID, unsigned int primID) -{ - const sceneinfo &info = Embree_SceneinfoForGeomID(geomID); - return info.triToModelinfo.at(primID); + return info.triInfo.at(primID); } inline qvec3f Embree_RayEndpoint(RTCRayN *ray, const qvec3f &dir, size_t N, size_t i) @@ -260,15 +283,16 @@ static void Embree_FilterFuncN(const struct RTCFilterFunctionNArguments *args) const unsigned rayIndex = rayID; const modelinfo_t *source_modelinfo = rsi->self; - const modelinfo_t *hit_modelinfo = Embree_LookupModelinfo(geomID, primID); - if (!hit_modelinfo) { + const triinfo &hit_triinfo = Embree_LookupTriangleInfo(geomID, primID); + + if (!hit_triinfo.modelinfo) { // we hit a "skip" face with no associated model // reject hit (???) valid[i] = INVALID; continue; } - if (hit_modelinfo->shadowworldonly.boolValue()) { + if (hit_triinfo.shadowworldonly) { // we hit "_shadowworldonly" "1" geometry. Ignore the hit unless we are from world. if (!source_modelinfo || !source_modelinfo->isWorld()) { // reject hit @@ -277,20 +301,20 @@ static void Embree_FilterFuncN(const struct RTCFilterFunctionNArguments *args) } } - if (hit_modelinfo->shadowself.boolValue()) { + if (hit_triinfo.shadowself) { // only casts shadows on itself - if (source_modelinfo != hit_modelinfo) { + if (source_modelinfo != hit_triinfo.modelinfo) { // reject hit valid[i] = INVALID; continue; } } - if (hit_modelinfo->switchableshadow.boolValue()) { + if (hit_triinfo.switchableshadow) { // we hit a dynamic shadow caster. reject the hit, but store the // info about what we hit. - const int style = hit_modelinfo->switchshadstyle.value(); + const int style = hit_triinfo.switchshadstyle; AddDynamicOccluderToRay(context, rayIndex, style); @@ -299,32 +323,16 @@ static void Embree_FilterFuncN(const struct RTCFilterFunctionNArguments *args) continue; } + float alpha = hit_triinfo.alpha; + // test fence textures and glass - const mface_t *face = Embree_LookupFace(geomID, primID); - float alpha = Face_Alpha(hit_modelinfo, face); - - // mxd - bool isFence, isGlass; - if (bsp_static->loadversion->game->id == GAME_QUAKE_II) { - const int surf_flags = Face_ContentsOrSurfaceFlags(bsp_static, face); - isFence = ((surf_flags & Q2_SURF_TRANSLUCENT) == - Q2_SURF_TRANSLUCENT); // KMQuake 2-specific. Use texture alpha chanel when both flags are set. - isGlass = !isFence && (surf_flags & Q2_SURF_TRANSLUCENT); - if (isGlass) - alpha = (surf_flags & Q2_SURF_TRANS33 ? 0.33f : 0.66f); - } else { - const char *name = Face_TextureName(bsp_static, face); - isFence = (name[0] == '{'); - isGlass = (alpha < 1.0f); - } - - if (isFence || isGlass) { + if (hit_triinfo.is_fence || hit_triinfo.is_glass) { qvec3f rayDir = qv::normalize(qvec3f{RTCRayN_dir_x(ray, N, i), RTCRayN_dir_y(ray, N, i), RTCRayN_dir_z(ray, N, i)}); qvec3f hitpoint = Embree_RayEndpoint(ray, rayDir, N, i); - const qvec4b sample = SampleTexture(face, bsp_static, hitpoint); // mxd. Palette index -> color_rgba + const qvec4b sample = SampleTexture(hit_triinfo.face, hit_triinfo.texinfo, hit_triinfo.texture, bsp_static, hitpoint); // mxd. Palette index -> color_rgba - if (isGlass) { + if (hit_triinfo.is_glass) { // hit glass... // mxd. Adjust alpha by texture alpha? @@ -347,7 +355,7 @@ static void Embree_FilterFuncN(const struct RTCFilterFunctionNArguments *args) continue; } - if (isFence) { + if (hit_triinfo.is_fence) { if (sample[3] < 255) { // reject hit valid[i] = INVALID; @@ -620,7 +628,7 @@ hitresult_t TestSky(const qvec3d &start, const qvec3d &dirn, const modelinfo_t * if (face_out) { if (hit_sky) { const sceneinfo &si = Embree_SceneinfoForGeomID(ray.hit.geomID); - *face_out = si.triToFace.at(ray.hit.primID); + *face_out = si.triInfo.at(ray.hit.primID).face; } else { *face_out = nullptr; }