From 8958790fedee836b4e8260fe9a10e483154f6fd9 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Tue, 30 Oct 2018 00:25:01 +0100 Subject: [PATCH] - Switch the raytrace function to use different collision routines --- src/level/doomdata.h | 7 +++ src/level/level_udmf.cpp | 66 +++++++++++++++------ src/lightmap/collision.cpp | 82 +++++++++++++++++++++++++- src/lightmap/collision.h | 35 +++++++---- src/lightmap/lightmap.cpp | 43 +++----------- src/lightmap/lightmap.h | 2 +- src/lightmap/surfaces.cpp | 117 ++++++++++++++++++------------------- src/lightmap/trace.cpp | 14 ++++- src/lightmap/trace.h | 18 +++--- 9 files changed, 247 insertions(+), 137 deletions(-) diff --git a/src/level/doomdata.h b/src/level/doomdata.h index 0906cef..2ba0408 100644 --- a/src/level/doomdata.h +++ b/src/level/doomdata.h @@ -3,6 +3,8 @@ #include "framework/tarray.h" #include "lightmap/kexlib/math/mathlib.h" +#include "lightmap/collision.h" +#include #undef MIN #undef MAX @@ -345,6 +347,11 @@ struct FLevel // Dlight helpers + TArray MeshVertices; + TArray MeshElements; + TArray MeshSurfaces; + std::unique_ptr CollisionMesh; + leaf_t *leafs = nullptr; uint8_t *mapPVS = nullptr; diff --git a/src/level/level_udmf.cpp b/src/level/level_udmf.cpp index 2ea5222..1b66c50 100644 --- a/src/level/level_udmf.cpp +++ b/src/level/level_udmf.cpp @@ -28,6 +28,24 @@ typedef unsigned int uint32; typedef signed int int32; #include "framework/xs_Float.h" +static void CopyUDMFString(char *dest, int destlen, const char *udmfvalue) +{ + destlen--; + + char endchar = 0; + if (udmfvalue[0] == '"' || udmfvalue[0] == '\'') + { + endchar = udmfvalue[0]; + udmfvalue++; + } + + for (int i = 0; i < destlen && udmfvalue[i] != 0 && udmfvalue[i] != endchar; i++) + { + *(dest++) = udmfvalue[i]; + } + + *dest = 0; +} class StringBuffer { @@ -251,6 +269,14 @@ void FProcessor::ParseSidedef(IntSideDef *sd) { SC_MustGetStringName("{"); sd->sector = NO_INDEX; + sd->textureoffset = 0; + sd->rowoffset = 0; + sd->toptexture[0] = '-'; + sd->toptexture[1] = 0; + sd->midtexture[0] = '-'; + sd->midtexture[1] = 0; + sd->bottomtexture[0] = '-'; + sd->bottomtexture[1] = 0; while (!SC_CheckString("}")) { const char *value; @@ -262,6 +288,27 @@ void FProcessor::ParseSidedef(IntSideDef *sd) continue; // do not store in props } + if (stricmp(key, "texturetop") == 0) + { + CopyUDMFString(sd->toptexture, 8, value); + } + else if (stricmp(key, "texturemiddle") == 0) + { + CopyUDMFString(sd->midtexture, 8, value); + } + else if (stricmp(key, "texturebottom") == 0) + { + CopyUDMFString(sd->bottomtexture, 8, value); + } + else if (stricmp(key, "offsetx_mid") == 0) + { + sd->textureoffset = CheckInt(key); + } + else if (stricmp(key, "offsety_mid") == 0) + { + sd->rowoffset = CheckInt(key); + } + // now store the key in its unprocessed form UDMFKey k = {key, value}; sd->props.Push(k); @@ -274,25 +321,6 @@ void FProcessor::ParseSidedef(IntSideDef *sd) // //=========================================================================== -static void CopyUDMFString(char *dest, int destlen, const char *udmfvalue) -{ - destlen--; - - char endchar = 0; - if (udmfvalue[0] == '"' || udmfvalue[0] == '\'') - { - endchar = udmfvalue[0]; - udmfvalue++; - } - - for (int i = 0; i < destlen && udmfvalue[i] != 0 && udmfvalue[i] != endchar; i++) - { - *(dest++) = udmfvalue[i]; - } - - *dest = 0; -} - void FProcessor::ParseSector(IntSector *sec) { memset(&sec->data, 0, sizeof(sec->data)); diff --git a/src/lightmap/collision.cpp b/src/lightmap/collision.cpp index a5ddb45..033525f 100644 --- a/src/lightmap/collision.cpp +++ b/src/lightmap/collision.cpp @@ -26,8 +26,8 @@ #undef min #undef max -TriangleMeshShape::TriangleMeshShape(const kexVec3 *vertices, int num_vertices, const unsigned int *elements, int num_elements) - : vertices(vertices), num_vertices(num_vertices), elements(elements), num_elements(num_elements), root(-1) +TriangleMeshShape::TriangleMeshShape(const kexVec3 *vertices, int num_vertices, const unsigned int *elements, int num_elements, const int *surfaces) + : vertices(vertices), num_vertices(num_vertices), elements(elements), num_elements(num_elements), surfaces(surfaces) { int num_triangles = num_elements / 3; if (num_triangles <= 0) @@ -71,6 +71,15 @@ bool TriangleMeshShape::find_any_hit(TriangleMeshShape *shape, const kexVec3 &ra return find_any_hit(shape, ray_start, ray_end, shape->root); } +TraceHit TriangleMeshShape::find_first_hit(TriangleMeshShape *shape, const kexVec3 &ray_start, const kexVec3 &ray_end) +{ + TraceHit hit; + find_first_hit(shape, ray_start, ray_end, shape->root, &hit); + if (hit.surface != -1) + hit.surface = shape->surfaces[hit.surface / 3]; + return hit; +} + float TriangleMeshShape::sweep(TriangleMeshShape *shape1, SphereShape *shape2, int a, const kexVec3 &target) { if (sweep_overlap_bv_sphere(shape1, shape2, a, target)) @@ -178,6 +187,27 @@ bool TriangleMeshShape::find_any_hit(TriangleMeshShape *shape, const kexVec3 &ra return false; } +void TriangleMeshShape::find_first_hit(TriangleMeshShape *shape, const kexVec3 &ray_start, const kexVec3 &ray_end, int a, TraceHit *hit) +{ + if (overlap_bv_ray(shape, ray_start, ray_end, a)) + { + if (shape->is_leaf(a)) + { + float t = intersect_triangle_ray(shape, ray_start, ray_end, a); + if (t < hit->fraction) + { + hit->fraction = t; + hit->surface = shape->nodes[a].element_index; + } + } + else + { + find_first_hit(shape, ray_start, ray_end, shape->nodes[a].left, hit); + find_first_hit(shape, ray_start, ray_end, shape->nodes[a].right, hit); + } + } +} + bool TriangleMeshShape::overlap_bv_ray(TriangleMeshShape *shape, const kexVec3 &ray_start, const kexVec3 &ray_end, int a) { return IntersectionTest::ray_aabb(ray_start, ray_end, shape->nodes[a].aabb) == IntersectionTest::overlap; @@ -194,6 +224,49 @@ float TriangleMeshShape::intersect_triangle_ray(TriangleMeshShape *shape, const shape->vertices[shape->elements[start_element + 2]] }; +#if 1 + + kexVec3 e1 = p[1] - p[0]; + kexVec3 e2 = p[2] - p[0]; + + kexVec3 triangleNormal = kexVec3::Cross(e1, e2); + float dist = kexVec3::Dot(p[0], triangleNormal); + float distA = kexVec3::Dot(ray_start, triangleNormal) - dist; + float distB = kexVec3::Dot(ray_end, triangleNormal) - dist; + + if (distA * distB >= 0.0f) + return 1.0f; + + // Backface check + //if (distA < 0.0f) + // return 1.0f; + + float projLength = distA - distB; + float distance = (distA) / (projLength); + if (distance > 1.0f) + return 1.0f; + + float edgeTolerance = triangleNormal.LengthSq() * -0.0001f; + kexVec3 point = ray_start * (1.0f - distance) + ray_end * distance; + + kexVec3 v0p = p[0] - point; + kexVec3 v1p = p[1] - point; + kexVec3 cp0 = kexVec3::Cross(v0p, v1p); + if (kexVec3::Dot(cp0, triangleNormal) < edgeTolerance) + return 1.0f; + + kexVec3 v2p = p[2] - point; + kexVec3 cp1 = kexVec3::Cross(v1p, v2p); + if (kexVec3::Dot(cp1, triangleNormal) < edgeTolerance) + return 1.0f; + + kexVec3 cp2 = kexVec3::Cross(v2p, v0p); + if (kexVec3::Dot(cp2, triangleNormal) < edgeTolerance) + return 1.0f; + + return distance; + +#else // Moeller–Trumbore ray-triangle intersection algorithm: kexVec3 D = ray_end - ray_start; @@ -206,6 +279,10 @@ float TriangleMeshShape::intersect_triangle_ray(TriangleMeshShape *shape, const kexVec3 P = kexVec3::Cross(D, e2); float det = kexVec3::Dot(e1, P); + // Backface check + //if (det < 0.0f) + // return 1.0f; + // If determinant is near zero, ray lies in plane of triangle if (det > -FLT_EPSILON && det < FLT_EPSILON) return 1.0f; @@ -238,6 +315,7 @@ float TriangleMeshShape::intersect_triangle_ray(TriangleMeshShape *shape, const // No hit, no win return 1.0f; +#endif } bool TriangleMeshShape::sweep_overlap_bv_sphere(TriangleMeshShape *shape1, SphereShape *shape2, int a, const kexVec3 &target) diff --git a/src/lightmap/collision.h b/src/lightmap/collision.h index acdcbb4..5927633 100644 --- a/src/lightmap/collision.h +++ b/src/lightmap/collision.h @@ -35,10 +35,16 @@ public: float radius = 0.0f; }; +struct TraceHit +{ + float fraction = 1.0f; + int surface = -1; +}; + class TriangleMeshShape { public: - TriangleMeshShape(const kexVec3 *vertices, int num_vertices, const unsigned int *elements, int num_elements); + TriangleMeshShape(const kexVec3 *vertices, int num_vertices, const unsigned int *elements, int num_elements, const int *surfaces); int get_min_depth(); int get_max_depth(); @@ -51,25 +57,28 @@ public: static bool find_any_hit(TriangleMeshShape *shape1, SphereShape *shape2); static bool find_any_hit(TriangleMeshShape *shape, const kexVec3 &ray_start, const kexVec3 &ray_end); + static TraceHit find_first_hit(TriangleMeshShape *shape, const kexVec3 &ray_start, const kexVec3 &ray_end); + struct Node { - Node() : left(-1), right(-1), element_index(-1) { } - Node(const kexVec3 &aabb_min, const kexVec3 &aabb_max, int element_index) : aabb(aabb_min, aabb_max), left(-1), right(-1), element_index(element_index) { } - Node(const kexVec3 &aabb_min, const kexVec3 &aabb_max, int left, int right) : aabb(aabb_min, aabb_max), left(left), right(right), element_index(-1) { } + Node() = default; + Node(const kexVec3 &aabb_min, const kexVec3 &aabb_max, int element_index) : aabb(aabb_min, aabb_max), element_index(element_index) { } + Node(const kexVec3 &aabb_min, const kexVec3 &aabb_max, int left, int right) : aabb(aabb_min, aabb_max), left(left), right(right) { } kexBBox aabb; - int left; - int right; - int element_index; + int left = -1; + int right = -1; + int element_index = -1; }; - const kexVec3 *vertices; - const int num_vertices; - const unsigned int *elements; - int num_elements; + const kexVec3 *vertices = nullptr; + const int num_vertices = 0; + const unsigned int *elements = nullptr; + int num_elements = 0; + const int *surfaces = nullptr; std::vector nodes; - int root; + int root = -1; private: static float sweep(TriangleMeshShape *shape1, SphereShape *shape2, int a, const kexVec3 &target); @@ -78,6 +87,8 @@ private: static bool find_any_hit(TriangleMeshShape *shape1, SphereShape *shape2, int a); static bool find_any_hit(TriangleMeshShape *shape1, const kexVec3 &ray_start, const kexVec3 &ray_end, int a); + static void find_first_hit(TriangleMeshShape *shape1, const kexVec3 &ray_start, const kexVec3 &ray_end, int a, TraceHit *hit); + inline static bool overlap_bv_ray(TriangleMeshShape *shape, const kexVec3 &ray_start, const kexVec3 &ray_end, int a); inline static float intersect_triangle_ray(TriangleMeshShape *shape, const kexVec3 &ray_start, const kexVec3 &ray_end, int a); diff --git a/src/lightmap/lightmap.cpp b/src/lightmap/lightmap.cpp index 2c0b670..5ed2ef3 100644 --- a/src/lightmap/lightmap.cpp +++ b/src/lightmap/lightmap.cpp @@ -42,8 +42,6 @@ #include #include -//#define EXPORT_TEXELS_OBJ - kexWorker lightmapWorker; const kexVec3 kexLightmapBuilder::gridSize(64, 64, 128); @@ -223,22 +221,22 @@ kexBBox kexLightmapBuilder::GetBoundsFromSurface(const surface_t *surface) // light if the surface that was traced is a sky // -bool kexLightmapBuilder::EmitFromCeiling(kexTrace &trace, const surface_t *surface, const kexVec3 &origin, - const kexVec3 &normal, float *dist) +bool kexLightmapBuilder::EmitFromCeiling(kexTrace &trace, const surface_t *surface, const kexVec3 &origin, const kexVec3 &normal, kexVec3 &color) { - *dist = normal.Dot(map->GetSunDirection()); + float dist = normal.Dot(map->GetSunDirection()); - if(*dist <= 0) + if(dist <= 0) { // plane is not even facing the sunlight return false; } - trace.Trace(origin, origin + (map->GetSunDirection() * 32768)); + trace.Trace(origin, origin + (map->GetSunDirection() * 32768.0f)); - if(trace.fraction == 1 || trace.hitSurface == NULL) + if(trace.fraction == 1.0f) { // nothing was hit + //color.x += 1.0f; return false; } @@ -248,6 +246,8 @@ bool kexLightmapBuilder::EmitFromCeiling(kexTrace &trace, const surface_t *surfa return false; } + color += map->GetSunColor() * dist; + return true; } @@ -336,15 +336,8 @@ kexVec3 kexLightmapBuilder::LightTexelSample(kexTrace &trace, const kexVec3 &ori if(surface->type != ST_CEILING && map->bSSectsVisibleToSky[surface->subSector - map->GLSubsectors]) { // see if it's exposed to sunlight - if(EmitFromCeiling(trace, surface, origin, plane.Normal(), &dist)) - { - dist = (dist * 4); - kexMath::Clamp(dist, 0, 1); - - color += map->GetSunColor() * dist; - + if(EmitFromCeiling(trace, surface, origin, plane.Normal(), color)) tracedTexels++; - } } // trace against surface lights @@ -504,13 +497,6 @@ void kexLightmapBuilder::TraceSurface(surface_t *surface) normal = surface->plane.Normal(); - // debugging stuff - used to help visualize where texels are positioned in the level -#ifdef EXPORT_TEXELS_OBJ - static int cnt = 0; - FILE *f = fopen(Va("texels_%02d.obj", cnt++), "w"); - int indices = 0; -#endif - // start walking through each texel for(i = 0; i < sampleHeight; i++) { @@ -522,13 +508,6 @@ void kexLightmapBuilder::TraceSurface(surface_t *surface) (surface->lightmapSteps[0] * (float)j) + (surface->lightmapSteps[1] * (float)i); - // debugging stuff -#ifdef EXPORT_TEXELS_OBJ - ExportTexelsToObjFile(f, pos, indices); - indices += 8; -#endif - - // accumulate color samples kexVec3 c = LightTexelSample(trace, pos, surface); // if nothing at all was traced and color is completely black @@ -543,10 +522,6 @@ void kexLightmapBuilder::TraceSurface(surface_t *surface) } } -#ifdef EXPORT_TEXELS_OBJ - fclose(f); -#endif - // SVE redraws the scene for lightmaps, so for optimizations, // tell the engine to ignore this surface if completely black if(bShouldLookupTexture == false) diff --git a/src/lightmap/lightmap.h b/src/lightmap/lightmap.h index 3c9c598..39c9046 100644 --- a/src/lightmap/lightmap.h +++ b/src/lightmap/lightmap.h @@ -64,7 +64,7 @@ private: kexBBox GetBoundsFromSurface(const surface_t *surface); kexVec3 LightTexelSample(kexTrace &trace, const kexVec3 &origin, surface_t *surface); kexVec3 LightCellSample(const int gridid, kexTrace &trace, const kexVec3 &origin, const MapSubsectorEx *sub); - bool EmitFromCeiling(kexTrace &trace, const surface_t *surface, const kexVec3 &origin, const kexVec3 &normal, float *dist); + bool EmitFromCeiling(kexTrace &trace, const surface_t *surface, const kexVec3 &origin, const kexVec3 &normal, kexVec3 &color); void ExportTexelsToObjFile(FILE *f, const kexVec3 &org, int indices); void WriteBlock(FILE *f, const int i, const kexVec3 &org, int indices, kexBBox &box); diff --git a/src/lightmap/surfaces.cpp b/src/lightmap/surfaces.cpp index 3708b43..a55aaf8 100644 --- a/src/lightmap/surfaces.cpp +++ b/src/lightmap/surfaces.cpp @@ -34,14 +34,8 @@ #include "mapdata.h" #include "surfaces.h" -//#define EXPORT_OBJ - kexArray surfaces; -// -// Surface_AllocateFromSeg -// - static void Surface_AllocateFromSeg(FLevel &doomMap, MapSegGLEx *seg) { IntSideDef *side; @@ -277,9 +271,21 @@ static void Surface_AllocateFromLeaf(FLevel &doomMap) printf("\nLeaf surfaces: %i\n", surfaces.Length() - doomMap.NumGLSubsectors); } -// -// Surface_AllocateFromMap -// +static bool IsDegenerate(const kexVec3 &v0, const kexVec3 &v1, const kexVec3 &v2) +{ + // A degenerate triangle has a zero cross product for two of its sides. + float ax = v1.x - v0.x; + float ay = v1.y - v0.y; + float az = v1.z - v0.z; + float bx = v2.x - v0.x; + float by = v2.y - v0.y; + float bz = v2.z - v0.z; + float crossx = ay * bz - az * by; + float crossy = az * bx - ax * bz; + float crossz = ax * by - ay * bx; + float crosslengthsqr = crossx * crossx + crossy * crossy + crossz * crossz; + return crosslengthsqr <= 1.e-6f; +} void Surface_AllocateFromMap(FLevel &doomMap) { @@ -301,61 +307,52 @@ void Surface_AllocateFromMap(FLevel &doomMap) printf("\nSeg surfaces: %i\n", surfaces.Length()); -#ifdef EXPORT_OBJ - FILE *f = fopen("level.obj", "w"); - int curLen = surfaces.Length(); - for(unsigned int i = 0; i < surfaces.Length(); i++) - { - for(int j = 0; j < surfaces[i]->numVerts; j++) - { - fprintf(f, "v %f %f %f\n", - -surfaces[i]->verts[j].y / 256.0f, - surfaces[i]->verts[j].z / 256.0f, - -surfaces[i]->verts[j].x / 256.0f); - } - } - - int tri; - - for(unsigned int i = 0; i < surfaces.Length(); i++) - { - fprintf(f, "o surf%i_seg%i\n", i, i); - fprintf(f, "f %i %i %i\n", 0+(i*4)+1, 1+(i*4)+1, 2+(i*4)+1); - fprintf(f, "f %i %i %i\n", 1+(i*4)+1, 3+(i*4)+1, 2+(i*4)+1); - - tri = 3+(i*4)+1; - } - - tri++; -#endif - Surface_AllocateFromLeaf(doomMap); printf("Surfaces total: %i\n\n", surfaces.Length()); -#ifdef EXPORT_OBJ - for(unsigned int i = curLen; i < surfaces.Length(); i++) - { - for(int j = 0; j < surfaces[i]->numVerts; j++) - { - fprintf(f, "v %f %f %f\n", - -surfaces[i]->verts[j].y / 256.0f, - surfaces[i]->verts[j].z / 256.0f, - -surfaces[i]->verts[j].x / 256.0f); - } - } + printf("Building collision mesh..\n\n"); - for(unsigned int i = curLen; i < surfaces.Length(); i++) - { - fprintf(f, "o surf%i_ssect%i\n", i, i - curLen); - fprintf(f, "f "); - for(int j = 0; j < surfaces[i]->numVerts; j++) - { - fprintf(f, "%i ", tri++); - } - fprintf(f, "\n"); - } + for (unsigned int i = 0; i < surfaces.Length(); i++) + { + const auto &s = surfaces[i]; + int numVerts = s->numVerts; + unsigned int pos = doomMap.MeshVertices.Size(); - fclose(f); -#endif + for (int j = 0; j < numVerts; j++) + doomMap.MeshVertices.Push(s->verts[j]); + + if (s->type == ST_FLOOR || s->type == ST_CEILING) + { + for (int j = 2; j < numVerts; j++) + { + if (!IsDegenerate(s->verts[0], s->verts[j - 1], s->verts[j])) + { + doomMap.MeshElements.Push(pos); + doomMap.MeshElements.Push(pos + j - 1); + doomMap.MeshElements.Push(pos + j); + doomMap.MeshSurfaces.Push(i); + } + } + } + else if (s->type == ST_MIDDLESEG || s->type == ST_UPPERSEG || s->type == ST_LOWERSEG) + { + if (!IsDegenerate(s->verts[0], s->verts[1], s->verts[2])) + { + doomMap.MeshElements.Push(pos + 0); + doomMap.MeshElements.Push(pos + 1); + doomMap.MeshElements.Push(pos + 2); + doomMap.MeshSurfaces.Push(i); + } + if (!IsDegenerate(s->verts[1], s->verts[2], s->verts[3])) + { + doomMap.MeshElements.Push(pos + 1); + doomMap.MeshElements.Push(pos + 2); + doomMap.MeshElements.Push(pos + 3); + doomMap.MeshSurfaces.Push(i); + } + } + } + + doomMap.CollisionMesh.reset(new TriangleMeshShape(&doomMap.MeshVertices[0], doomMap.MeshVertices.Size(), &doomMap.MeshElements[0], doomMap.MeshElements.Size(), &doomMap.MeshSurfaces[0])); } diff --git a/src/lightmap/trace.cpp b/src/lightmap/trace.cpp index f8b1211..278c2dc 100644 --- a/src/lightmap/trace.cpp +++ b/src/lightmap/trace.cpp @@ -69,6 +69,15 @@ void kexTrace::Trace(const kexVec3 &startVec, const kexVec3 &endVec) { start = startVec; end = endVec; + + TraceHit hit = TriangleMeshShape::find_first_hit(map->CollisionMesh.get(), start, end); + fraction = hit.fraction; + if (fraction < 1.0f) + hitSurface = surfaces[hit.surface]; + else + hitSurface = nullptr; + +/* dir = (end - start).Normalize(); hitNormal.Clear(); hitVector.Clear(); @@ -80,9 +89,11 @@ void kexTrace::Trace(const kexVec3 &startVec, const kexVec3 &endVec) return; } - TraceBSPNode(map->NumGLNodes - 1); + TraceBSPNode(map->NumGLNodes - 1); +*/ } +#if 0 // // kexTrace::TraceSurface // @@ -284,3 +295,4 @@ void kexTrace::TraceBSPNode(int num) TraceBSPNode(node->children[side]); } } +#endif diff --git a/src/lightmap/trace.h b/src/lightmap/trace.h index 4d32627..58875de 100644 --- a/src/lightmap/trace.h +++ b/src/lightmap/trace.h @@ -40,17 +40,19 @@ public: kexVec3 start; kexVec3 end; - kexVec3 dir; - kexVec3 hitNormal; - kexVec3 hitVector; - surface_t *hitSurface; - float fraction; + surface_t *hitSurface; + float fraction; private: + FLevel *map; + +#if 0 + kexVec3 dir; + kexVec3 hitNormal; + kexVec3 hitVector; + void TraceBSPNode(int num); void TraceSubSector(int num); void TraceSurface(surface_t *surface); - - FLevel *map; - +#endif };