/* Copyright (C) 2016 Eric Wasylishen 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 /** * i is between 0 and face->numedges - 1 */ static int VertAtIndex(const bsp2_t *bsp, const bsp2_dface_t *face, const int i) { int edge = bsp->dsurfedges[face->firstedge + i]; int vert = (edge >= 0) ? bsp->dedges[edge].v[0] : bsp->dedges[-edge].v[1]; return vert; } static const miptex_t * MiptexForFace(const bsp2_t *bsp, const bsp2_dface_t *face) { const texinfo_t *tex; dmiptexlump_t *miplump = bsp->dtexdata.header; const miptex_t *miptex; tex = &bsp->texinfo[face->texinfo]; int dataofs = miplump->dataofs[tex->miptex]; if (dataofs == -1) return NULL; miptex = (miptex_t*)(bsp->dtexdata.base + dataofs); return miptex; } class sceneinfo { public: unsigned geomID; std::vector triToFace; }; sceneinfo CreateGeometry(const bsp2_t *bsp, RTCScene scene, const std::vector &faces) { // count triangles int numtris = 0; for (const bsp2_dface_t *face : faces) { if (face->numedges < 3) continue; numtris += (face->numedges - 2); } unsigned geomID = rtcNewTriangleMesh(scene, RTC_GEOMETRY_STATIC, numtris, bsp->numvertexes); struct Vertex { float point[4]; }; //4th element is padding struct Triangle { int v0, v1, v2; }; // fill in vertices Vertex* vertices = (Vertex*) rtcMapBuffer(scene, geomID, RTC_VERTEX_BUFFER); for (int i=0; inumvertexes; i++) { const dvertex_t *dvertex = &bsp->dvertexes[i]; Vertex *vert = &vertices[i]; for (int j=0; j<3; j++) { vert->point[j] = dvertex->point[j]; } } rtcUnmapBuffer(scene, geomID, RTC_VERTEX_BUFFER); sceneinfo s; s.geomID = geomID; // fill in triangles Triangle* triangles = (Triangle*) rtcMapBuffer(scene, geomID, RTC_INDEX_BUFFER); int tri_index = 0; for (const bsp2_dface_t *face : faces) { if (face->numedges < 3) continue; for (int j = 2; j < face->numedges; j++) { Triangle *tri = &triangles[tri_index]; tri->v0 = VertAtIndex(bsp, face, j-1); tri->v1 = VertAtIndex(bsp, face, j); tri->v2 = VertAtIndex(bsp, face, 0); tri_index++; s.triToFace.push_back(face); } } rtcUnmapBuffer(scene, geomID, RTC_INDEX_BUFFER); return s; } RTCScene scene; /* global shadow casters */ sceneinfo skygeom, solidgeom; /* self-shadow models for all bmodels which are NOT global shadow casters. * these are only used for: * - dirtmapping * - making bmodels self-shadow if it's explicitly set with the _shadow flag */ std::unordered_map selfshadowSceneForDModel; void ErrorCallback(const RTCError code, const char* str) { printf("RTC Error %d: %s\n", code, str); } void MakeTnodes_embree(const bsp2_t *bsp) { assert(tracelist != NULL); std::vector skyfaces, solidfaces; /* Check against the list of global shadow casters */ for (const modelinfo_t *const *model = tracelist; *model; model++) { for (int i=0; i<(*model)->model->numfaces; i++) { const bsp2_dface_t *face = &bsp->dfaces[(*model)->model->firstface + i]; const miptex_t *miptex = MiptexForFace(bsp, face); if (miptex != NULL && !strncmp("sky", miptex->name, 3)) { skyfaces.push_back(face); } else { solidfaces.push_back(face); } } } rtcInit(NULL); rtcSetErrorFunction(ErrorCallback); scene = rtcNewScene(RTC_SCENE_STATIC | RTC_SCENE_COHERENT | RTC_SCENE_HIGH_QUALITY | RTC_SCENE_ROBUST, RTC_INTERSECT1); skygeom = CreateGeometry(bsp, scene, skyfaces); solidgeom = CreateGeometry(bsp, scene, solidfaces); rtcCommit (scene); printf("MakeTnodes: created sky geometry with %d faces and solid with %d faces\n", (int)skyfaces.size(), (int)solidfaces.size()); /* Create self-shadow models */ for (int i=0; inummodels; i++) { const dmodel_t *model = &bsp->dmodels[i]; // N.B. All faces are considered opaque for the selfshadow models std::vector faces; for (int j=0; jnumfaces; j++) { const bsp2_dface_t *face = &bsp->dfaces[model->firstface + j]; faces.push_back(face); } RTCScene selfshadowscene = rtcNewScene(RTC_SCENE_STATIC | RTC_SCENE_COHERENT | RTC_SCENE_HIGH_QUALITY | RTC_SCENE_ROBUST, RTC_INTERSECT1); CreateGeometry(bsp, selfshadowscene, faces); rtcCommit (selfshadowscene); selfshadowSceneForDModel[model] = selfshadowscene; } } static RTCRay SetupRay(const vec3_t start, const vec3_t dir, vec_t dist) { RTCRay ray; VectorCopy(start, ray.org); VectorCopy(dir, ray.dir); // can be un-normalized ray.tnear = 0.f; ray.tfar = dist; ray.geomID = RTC_INVALID_GEOMETRY_ID; ray.primID = RTC_INVALID_GEOMETRY_ID; ray.instID = RTC_INVALID_GEOMETRY_ID; ray.mask = 0xFFFFFFFF; ray.time = 0.f; return ray; } qboolean TestLight_embree(const vec3_t start, const vec3_t dir, vec_t dist, const modelinfo_t *model) { RTCRay ray = SetupRay(start, dir, dist); rtcOccluded(scene, ray); if (ray.geomID != RTC_INVALID_GEOMETRY_ID) return false; //hit if (model->shadowself) { // check the selfshadow model RTCScene selfshadowscene = selfshadowSceneForDModel[model->model]; ray = SetupRay(start, dir, dist); rtcOccluded(selfshadowscene, ray); if (ray.geomID != RTC_INVALID_GEOMETRY_ID) return false; //hit } // no obstruction return true; } qboolean TestSky_embree(const vec3_t start, const vec3_t dirn, const modelinfo_t *model) { // trace from the sample point towards the sun, and // return true if we hit a sky poly. RTCRay ray = SetupRay(start, dirn, std::numeric_limits::infinity()); rtcIntersect(scene, ray); qboolean hit_sky = (ray.geomID == skygeom.geomID); if (hit_sky && model->shadowself) { // sky is visible, check for an obstruction from the selfshadow RTCScene selfshadowscene = selfshadowSceneForDModel[model->model]; ray = SetupRay(start, dirn, std::numeric_limits::infinity()); rtcIntersect(selfshadowscene, ray); hit_sky = (ray.geomID == RTC_INVALID_GEOMETRY_ID); } return hit_sky; } /* * ============ * DirtTrace * * returns true if the trace from start to stop hits something solid, * or if it started in the void. * ============ */ qboolean DirtTrace_embree(const vec3_t start, const vec3_t dir, vec_t dist, vec_t *hitdist, vec_t *normal, const modelinfo_t *model) { RTCRay ray = SetupRay(start, dir, dist); rtcIntersect(scene, ray); if (ray.geomID != solidgeom.geomID) { // don't re-check the world's self-shadow model because it's already part of 'scene' if (model->model != tracelist[0]->model) { RTCScene selfshadowscene = selfshadowSceneForDModel[model->model]; ray = SetupRay(start, dir, dist); rtcIntersect(selfshadowscene, ray); if (ray.geomID == RTC_INVALID_GEOMETRY_ID) { return false; //no hit } } else { // no self shadow model. no hit return false; } } // check if the ray was coming from behind the hit surface (it started in the void) { VectorNormalize(ray.Ng); vec_t rayHitPlaneAngle = DotProduct(ray.Ng, dir); if (rayHitPlaneAngle > 0) { // pointing in the same direction ray.tfar = 0; } } if (hitdist) *hitdist = ray.tfar; if (normal) { VectorCopy(ray.Ng, normal); } return true; } qboolean FaceTrace_embree(const vec3_t start, const vec3_t dir, vec3_t hitpoint, const bsp2_dface_t **hitface) { RTCRay ray = SetupRay(start, dir, 512); rtcIntersect(scene, ray); if (ray.geomID != solidgeom.geomID) { return false; } VectorMA(start, ray.tfar, dir, hitpoint); *hitface = solidgeom.triToFace[ray.primID]; return true; } /* * ============ * CalcPointsTrace * * returns true if the trace from start to stop hits something solid. * ============ */ qboolean CalcPointsTrace_embree(const vec3_t start, const vec3_t dir, vec_t dist, vec_t *hitdist, vec_t *normal, const modelinfo_t *model) { RTCScene selfshadowscene = selfshadowSceneForDModel[model->model]; RTCRay ray = SetupRay(start, dir, dist); rtcIntersect(selfshadowscene, ray); // if there is no hit, but we were tracning on a submodel, also test against the world. if (ray.geomID == RTC_INVALID_GEOMETRY_ID && model->model != tracelist[0]->model) { ray = SetupRay(start, dir, dist); rtcIntersect(scene, ray); } if (hitdist) *hitdist = ray.tfar; if (normal) { VectorCopy(ray.Ng, normal); VectorNormalize(normal); } return ray.geomID != RTC_INVALID_GEOMETRY_ID; }