/* base-vis.c PVS PHS generator tool Copyright (C) 1996-1997 Id Software, Inc. Copyright (C) 2002 Colin Thompson 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: Free Software Foundation, Inc. 59 Temple Place - Suite 330 Boston, MA 02111-1307, USA */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_IO_H # include #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #include #include #include #include #include "QF/bspfile.h" #include "QF/cmd.h" #include "QF/mathlib.h" #include "QF/quakefs.h" #include "QF/sys.h" #include "tools/qfvis/include/vis.h" #include "tools/qfvis/include/options.h" /* This is a rough first-order aproximation that is used to trivially reject some of the final calculations. */ static void SimpleFlood (basethread_t *thread, portal_t *srcportal, int clusternum) { int i; cluster_t *cluster; portal_t *portal; if (set_is_member (srcportal->mightsee, clusternum)) return; set_add (srcportal->mightsee, clusternum); thread->clustersee++; cluster = &clusters[clusternum]; for (i = 0; i < cluster->numportals; i++) { portal = &cluster->portals[i]; if (!set_is_member (thread->portalsee, portal - portals)) continue; SimpleFlood (thread, srcportal, portal->cluster); } } static inline int test_sphere (const vspheref_t *sphere, vec4f_t plane) { #ifdef __SSE3__ const vec4f_t zero = {}; float r = sphere->radius; vec4f_t eps = { r, r, r, r }; vec4f_t d = _mm_addsub_ps (zero, dotf (sphere->center, plane)); vec4i_t c = (d - eps) >= 0; c = (vec4i_t) _mm_hsub_epi32 ((__m128i) c, (__m128i) c); return c[0]; #else float d = DotProduct (sphere->center, plane) + plane[3]; int front = (d >= sphere->radius); int back = (d <= -sphere->radius); return front - back; #endif } static int __attribute__ ((pure)) test_winding_front (const winding_t *winding, vec4f_t plane) { for (unsigned i = 0; i < winding->numpoints; i++) { vec4f_t d = dotf (winding->points[i], plane); if (d[0] > ON_EPSILON) return 1; } return 0; } static int __attribute__ ((pure)) test_winding_back (const winding_t *winding, vec4f_t plane) { for (unsigned i = 0; i < winding->numpoints; i++) { vec4f_t d = dotf (winding->points[i], plane); if (d[0] < -ON_EPSILON) return 1; } return 0; } void PortalBase (basethread_t *thread, portal_t *portal) { portal_t *tp; cluster_t *cluster; int tp_side, portal_side; for (cluster = clusters; cluster < clusters + portalclusters; cluster++) { int side = test_sphere (&cluster->sphere, portal->plane); if (side < 0) { thread->clustertest += cluster->numportals; // The cluster is entirely behind the portal's plane, thus every // portal in the cluster is also behind the portal's plane and // cannot be seen at all. thread->clustercull += cluster->numportals; } else if (side > 0) { // The cluster is entirely in front of the portal's plane, thus // every portal in the cluster is also in front of the portal's // plane and may be seen. However, as portals are one-way (ie, // can see out the portal along its plane normal, but not into // the portal against its plane normal), the current portal // must be behind the cluster's portal, or straddle its plane, // for a cluster's portal to be considered visible. thread->clustertest += cluster->numportals; for (int i = 0; i < cluster->numportals; i++) { tp = cluster->portals + i; if (tp == portal) { thread->selfcull++; continue; } thread->spheretest++; portal_side = test_sphere (&portal->sphere, tp->plane); if (portal_side > 0) { // The portal definitely is entirely in front of the test // portal's plane. (cannot see into a portal from its // front side) thread->spherecull++; } else if (portal_side < 0) { // The portal's sphere is behind the test portal's plane, // so the portal itself is entirely behind the plane // thus the test portal is potentially visible. thread->spherepass++; set_add (thread->portalsee, tp - portals); } else { // The portal's sphere straddle's the test portal's // plane, so need to do a more refined check. thread->windingtest++; if (test_winding_back (portal->winding, tp->plane)) { // The portal is at least partially behind the test // portal's plane, so the test portal is potentially // visible. thread->windingpass++; set_add (thread->portalsee, tp - portals); } else { thread->windingcull++; } } } } else { thread->clustertest += cluster->numportals; // The cluster's sphere straddle's the portal's plane, thus each // portal in the cluster must be tested individually. for (int i = 0; i < cluster->numportals; i++) { tp = cluster->portals + i; if (tp == portal) { thread->selfcull++; continue; } thread->spheretest++; // If the target portal is behind the portals's plane, then // it can't possibly be seen by the portal. // // If the portal is in front of the target's plane, then the // target is of no interest as it is facing counter to the // flow of visibility. // First check using the bounding spheres of the two portals. tp_side = test_sphere (&tp->sphere, portal->plane); if (tp_side < 0) { // The test portal definitely is entirely behind the // portal's plane. thread->spherecull++; continue; // entirely behind } portal_side = test_sphere (&portal->sphere, tp->plane); if (portal_side > 0) { // The portal definitely is entirely in front of the // test portal's plane. (cannot see into a portal from // its front side) thread->spherecull++; continue; // entirely in front } if (tp_side > 0 && portal_side < 0) { thread->spherepass++; set_add (thread->portalsee, tp - portals); continue; } thread->windingtest++; if (tp_side == 0) { // The test portal's sphere touches the portal's plane, // so do a more refined check. if (!test_winding_front (tp->winding, portal->plane)) { thread->windingcull++; continue; } } if (portal_side == 0) { // The portal's sphere touches the test portal's plane, // so do a more refined check. if (!test_winding_back (portal->winding, tp->plane)) { thread->windingcull++; continue; } } thread->windingpass++; set_add (thread->portalsee, tp - portals); } } } SimpleFlood (thread, portal, portal->cluster); portal->nummightsee = thread->clustersee; }