- Switch the raytrace function to use different collision routines

This commit is contained in:
Magnus Norddahl 2018-10-30 00:25:01 +01:00
parent 99a4ffa69f
commit 8958790fed
9 changed files with 247 additions and 137 deletions

View file

@ -3,6 +3,8 @@
#include "framework/tarray.h"
#include "lightmap/kexlib/math/mathlib.h"
#include "lightmap/collision.h"
#include <memory>
#undef MIN
#undef MAX
@ -345,6 +347,11 @@ struct FLevel
// Dlight helpers
TArray<kexVec3> MeshVertices;
TArray<unsigned int> MeshElements;
TArray<int> MeshSurfaces;
std::unique_ptr<TriangleMeshShape> CollisionMesh;
leaf_t *leafs = nullptr;
uint8_t *mapPVS = nullptr;

View file

@ -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));

View file

@ -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
// MoellerTrumbore 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)

View file

@ -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<Node> 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);

View file

@ -42,8 +42,6 @@
#include <map>
#include <vector>
//#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)

View file

@ -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);

View file

@ -34,14 +34,8 @@
#include "mapdata.h"
#include "surfaces.h"
//#define EXPORT_OBJ
kexArray<surface_t*> 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]));
}

View file

@ -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

View file

@ -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
};