/* Copyright (C) 1996-1997 Id Software, Inc. Copyright (C) 2018 MaxED This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA See file, 'COPYING', for details. */ #include //#include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; using namespace polylib; mutex surfacelights_lock; std::vector surfacelights; std::map> surfacelightsByFacenum; int total_surflight_points = 0; static void MakeSurfaceLightsThread(const mbsp_t *bsp, const settings::worldspawn_keys &cfg, size_t i) { const mface_t *face = BSP_GetFace(bsp, i); // Face casts light? const gtexinfo_t *info = Face_Texinfo(bsp, face); if (info == nullptr) return; if (!(info->flags.native & Q2_SURF_LIGHT) || info->value == 0) { if (info->flags.native & Q2_SURF_LIGHT) { qvec3d wc = winding_t::from_face(bsp, face).center(); logging::print("WARNING: surface light '{}' at [{}] has 0 intensity.\n", Face_TextureName(bsp, face), wc); } return; } // Create face points... auto poly = GLM_FacePoints(bsp, face); const float facearea = qv::PolyArea(poly.begin(), poly.end()); // Avoid small, or zero-area faces if (facearea < 1) return; // Create winding... winding_t winding = winding_t::from_winding_points(poly); winding.remove_colinear(); // Get face normal and midpoint... qvec3d facenormal = Face_Normal(bsp, face); qvec3d facemidpoint = winding.center() + facenormal; // Lift 1 unit // Dice winding... vector points; winding.dice(cfg.surflightsubdivision.value(), [&points](winding_t &w) { points.push_back(w.center()); }); total_surflight_points += points.size(); // Get texture color qvec3f texturecolor = qvec3f(Face_LookupTextureColor(bsp, face)) / 255.f; // Calculate emit color and intensity... // Handle arghrad sky light settings http://www.bspquakeeditor.com/arghrad/sunlight.html#sky if (info->flags.native & Q2_SURF_SKY) { // FIXME: this only handles the "_sky_surface" "red green blue" format. // There are other more complex variants we could handle documented in the link above. // FIXME: we require value to be nonzero, see the check above - not sure if this matches arghrad if (cfg.sky_surface.isChanged()) { texturecolor = cfg.sky_surface.value(); } } texturecolor *= info->value; // Scale by light value // Calculate intensity... float intensity = qv::max(texturecolor); if (intensity == 0.0f) return; // Normalize color... if (intensity > 1.0f) texturecolor *= 1.0f / intensity; // Sanity checks... Q_assert(!points.empty()); // Add surfacelight... surfacelight_t l; l.surfnormal = facenormal; l.omnidirectional = true;//(info->flags.native & Q2_SURF_SKY) ? true : false; l.points = points; l.pos = facemidpoint; // Store surfacelight settings... l.totalintensity = intensity * facearea; l.intensity = l.totalintensity / points.size(); l.color = texturecolor; // Init bbox... l.bounds = qvec3d(0); if (!options.novisapprox.value()) l.bounds = EstimateVisibleBoundsAtPoint(facemidpoint); // Store light... unique_lock lck{surfacelights_lock}; surfacelights.push_back(l); const int index = static_cast(surfacelights.size()) - 1; surfacelightsByFacenum[Face_GetNum(bsp, face)].push_back(index); } const std::vector &SurfaceLights() { return surfacelights; } int TotalSurfacelightPoints() { return total_surflight_points; } // No surflight_debug (yet?), so unused... const std::vector &SurfaceLightsForFaceNum(int facenum) { const auto &vec = surfacelightsByFacenum.find(facenum); if (vec != surfacelightsByFacenum.end()) return vec->second; static std::vector empty; return empty; } void // Quake 2 surface lights MakeSurfaceLights(const settings::worldspawn_keys &cfg, const mbsp_t *bsp) { logging::print("--- MakeSurfaceLights ---\n"); logging::parallel_for(static_cast(0), bsp->dfaces.size(), [&](size_t i) { MakeSurfaceLightsThread(bsp, cfg, i); }); }