room101/Apps/DEBSP.CPP

714 lines
20 KiB
C++

#include "../main.hpp"
#include "../string.hpp"
#include "../light.hpp"
#include "../triangle.hpp"
#include "../surface.hpp"
#include "../bsp.hpp"
#include "../file.hpp"
#include "../polygon.hpp"
#define MAX_SURFACES 10000
#define MAX_POINTS 5000
#define MAX_LIGHTS 1000
#define MAX_FACES 10000
#define MAX_BSP 10000
struct
{
int lighting;
int shadows;
} switches;
int nlights;
Light lights[MAX_LIGHTS];
int nsurfaces;
int nportals;
Surface surfaces[MAX_SURFACES];
Picture lightmaps[MAX_SURFACES];
BSP bsps[MAX_BSP];
int nbsps;
int nleafs;
typedef struct
{
Plane plane;
int nfaces;
int points_infront;
int points_behind;
int point_balance;
int faces_split;
int nportals;
float balance;
} PlaneInfo;
PlaneInfo planeinfo[MAX_FACES];
/*******************************************************************************
** FaceCreate
*******************************************************************************/
void
FaceCreate(Face* face, Polygon src, int n, int colour, int surfaceid)
{
Polygon tt1;
Polygon tt2;
ThrowIf(n >= POLYGON_MAX, "Polygon has too many vertices");
memset(face, 0, sizeof(Face));
if (n < 3) return;
n = PolygonMergePoints(tt2, src, n);
if (n < 3) return;
n = PolygonCollinear(tt1, tt2, n);
if (n < 3) return;
face->vertices = (Vector*) MemoryAlloc(n, sizeof(Vector));
PolygonCopy(face->vertices, tt1, n);
if (surfaceid >= 0)
{
PlaneCopy(face->plane, surfaces[surfaceid].plane);
}
else
{
TrianglePlane(tt1, face->plane);
}
face->surfaceid = surfaceid;
face->colour = colour;
}
/*******************************************************************************
** PlaneHullCreate
*******************************************************************************/
void
PlaneHullCreate(Plane plane, int i, Polygon poly, int n, Plane faceplane)
{
Vector normal, edge;
VectorSubtract(edge, poly[i], poly[(i + 1) % n]);
VectorCrossProduct(normal, faceplane, edge);
PlaneInit(plane, normal, poly[i]);
}
/*******************************************************************************
** FaceCreateHullPlanes
*******************************************************************************/
void
FaceCreateHullPlanes(Face* face)
{
assert(face);
assert(NumberOf(face->hull) == 0);
assert(NumberOf(face->vertices) > 2);
face->hull = (Plane*) MemoryAlloc(NumberOf(face->vertices), sizeof(Plane));
for (int i = 0; i < NumberOf(face->vertices); i++)
{
PlaneHullCreate(face->hull[i], i, face->vertices, NumberOf(face->vertices), face->plane);
}
}
/*******************************************************************************
** FaceSlice
*******************************************************************************/
void
FaceSlice(Face* dst, Face* src, Plane plane)
{
Polygon tmp;
assert(dst && src && plane && NumberOf(src->vertices) < 20);
FaceCreate(dst, tmp,
PolygonClipToPlane(tmp, src->vertices, NumberOf(src->vertices), plane),
src->colour,
src->surfaceid);
}
/*******************************************************************************
** FaceArrayPush
*******************************************************************************/
void
FaceArrayPush(Face** facearray, Face* face)
{
assert(facearray && face);
*facearray = (Face*) MemoryRealloc(*facearray, NumberOf(*facearray) + 1, sizeof(Face));
(*facearray)[NumberOf(*facearray) - 1] = *face;
}
/*******************************************************************************
** FaceArrayPull
*******************************************************************************/
void
FaceArrayPull(Face** facearray, Face* face)
{
assert(facearray && *facearray && face);
*face = (*facearray)[NumberOf(*facearray) - 1];
*facearray = (Face*) MemoryRealloc(*facearray, NumberOf(*facearray) - 1, sizeof(Face));
}
/*******************************************************************************
** FaceArrayReadMDL
*******************************************************************************/
Face*
FaceArrayReadMDL(FILE* file_raw, FILE* file_lights, FILE* file_data)
{
String s, name;
int colour, groupid;
Triangle triangle;
Face face;
Face* array = NULL;
fscanf(file_raw, "%s", s);
int nfaces = 0;
while (TriangleScan(triangle, file_raw))
{
nfaces++;
fscanf(file_data, "%s %d %d", name, &colour, &groupid);
if (colour == 0) nportals++;
if (colour != 0)
{
FaceCreate(&face, triangle, 3, colour, -1);
if (face.vertices) FaceArrayPush(&array, &face);
}
}
while (
fscanf(file_lights, "%s %f %f %f %f %f %f",
lights[nlights].name,
&lights[nlights].pos[0],
&lights[nlights].pos[1],
&lights[nlights].pos[2],
&lights[nlights].rgb[0],
&lights[nlights].rgb[1],
&lights[nlights].rgb[2]) == 7)
{
nlights++;
ThrowIf(nlights >= MAX_LIGHTS, "Too many lights");
}
fprintf(stderr, "Number of faces: %d\n", nfaces);
fprintf(stderr, "Number of portals: %d\n", nportals);
fprintf(stderr, "Number of lights: %d\n", nlights);
return array;
}
/*******************************************************************************
** FaceArrayWrite
*******************************************************************************/
void
FaceArrayWrite(Face* array, FILE* file)
{
assert(file);
IntWrite(NumberOf(array), file);
for (int i = 0; i < NumberOf(array); i++)
{
FaceWrite(&array[i], file);
}
}
/*******************************************************************************
** FaceArrayDelete
*******************************************************************************/
void
FaceArrayDelete(Face* array)
{
Face face;
while (array)
{
FaceArrayPull(&array, &face);
FaceDelete(&face);
}
}
/*******************************************************************************
** FaceArraySlice
*******************************************************************************/
Face*
FaceArraySlice(Face* src, Plane plane)
{
Face face;
Face* facearray = NULL;
for (int i = 0; i < NumberOf(src); i++)
{
FaceSlice(&face, &src[i], plane);
if (face.vertices)
{
FaceArrayPush(&facearray, &face);
}
}
return facearray;
}
/*******************************************************************************
** PlaneInfoCollect
*******************************************************************************/
void
PlaneInfoCollect(Face* faces)
{
assert(faces && NumberOf(faces) < MAX_FACES);
for (int infoid = 0; infoid < NumberOf(faces); infoid++)
{
PlaneInfo* info = &planeinfo[infoid];
memset(info, 0, sizeof(PlaneInfo));
memcpy(info->plane, faces[infoid].plane, sizeof(Plane));
for (int faceid = 0; faceid < NumberOf(faces); faceid++)
{
Face* face = &faces[faceid];
if (PlanesAreCollinear(face->plane, info->plane))
{
info->nfaces++;
if (face->colour == 0) info->nportals++;
}
else
{
int behind = 0;
int infront = 0;
/*
** Count how many points are on each side of the plane.
*/
for (int i = 0; i < NumberOf(face->vertices); i++)
{
info->balance += PlaneDistanceTo(info->plane, face->vertices[i]);
if (PlaneDistanceTo(info->plane, face->vertices[i]) < 0.0f)
{
behind++;
}
else
{
infront++;
}
}
/*
** Update totals.
*/
info->points_behind += behind;
info->points_infront += infront;
if (behind && infront) info->faces_split++;
}
}
/*
** Calculate the point balance for this plane.
*/
info->point_balance = abs(info->points_behind - info->points_infront);
info->balance = (float) fabs(info->balance);
}
}
/*******************************************************************************
** SelectPlane
*******************************************************************************/
void
SelectPlane(Plane plane, Face* faces)
{
int i, j;
assert(faces && NumberOf(faces));
PlaneInfoCollect(faces);
for (j = i = 0; i < NumberOf(faces); i++)
{
//if (planeinfo[i].balance < planeinfo[j].balance ) j = i; else
//if (planeinfo[i].balance == planeinfo[j].balance )
//if (planeinfo[i].nportals > planeinfo[j].nportals) j = i; else
//if (planeinfo[i].nportals == planeinfo[j].nportals)
if (planeinfo[i].faces_split < planeinfo[j].faces_split ) j = i; else
if (planeinfo[i].faces_split == planeinfo[j].faces_split )
//if (planeinfo[i].points_infront > planeinfo[j].points_infront) j = i; else
//if (planeinfo[i].points_infront == planeinfo[j].points_infront)
//if (planeinfo[i].nfaces > planeinfo[j].nfaces ) j = i; else
//if (planeinfo[i].nfaces == planeinfo[j].nfaces )
//if (planeinfo[i].point_balance < planeinfo[j].point_balance) j = i; else
//if (planeinfo[i].point_balance == planeinfo[j].point_balance)
{}
}
memcpy(plane, planeinfo[j].plane, sizeof(Plane));
}
/*******************************************************************************
** BSPCompile
*******************************************************************************/
BSP*
BSPCompile(Face** facearray)
{
Face* both = NULL;
Face* front = NULL;
Face* back = NULL;
Face face;
Plane plane;
BSP* bsp = &bsps[nbsps++];;
assert(facearray);
ThrowIf(nbsps >= MAX_BSP, "Too many BSP's");
if (*facearray)
{
SelectPlane(bsp->plane, *facearray);
while (*facearray)
{
FaceArrayPull(facearray, &face);
if (PlanesAreCollinear(face.plane, bsp->plane))
{
FaceArrayPush(&bsp->faces, &face);
}
else
{
FaceArrayPush(&both, &face);
}
}
memcpy(plane, bsp->plane, sizeof(Plane));
front = FaceArraySlice(both, plane);
PlaneNegate(plane);
back = FaceArraySlice(both, plane);
FaceArrayDelete(*facearray);
FaceArrayDelete(both);
bsp->sides[0] = BSPCompile(&front);
bsp->sides[1] = BSPCompile(&back);
}
else
{
nleafs++;
}
return bsp;
}
/*******************************************************************************
** FaceLightMapCreate
*******************************************************************************/
void
FaceLightMapCreate(Picture* dst, Surface* surface, BSP* bsproot)
{
Vector rgb, sample, vector, L;
Matrix matrix;
assert(dst && surface && bsproot);
if (surface->colour < 3)
{
PictureCreate(dst, 1, 1, 0, 0);
return;
}
/*
** Create lightmap channel. The lightmap contains sample points every 16th pixel
** plus one extra point along the right and bottom edges.
*/
PictureCreate(dst, surface->width / 16 + 1, surface->height / 16 + 1, NULL, 0);
if (!switches.lighting || nlights == 0)
{
PictureBlitRect(dst, dst->rect, PixelCreate(31, 31, 31));
return;
}
MatrixTranspose(matrix, surface->matrix);
/*
** Iterate over lightmap surface.
*/
for (int x = 0; x < dst->width; x++)
{
for (int y = 0; y < dst->height; y++)
{
/*
** Create and transform the sample point into world space.
*/
vector[0] = surface->lo[0] + x * 16.0f;
vector[1] = surface->lo[1] + y * 16.0f;
vector[2] = surface->lo[2] + 16.0f;
VectorRotate(sample, vector, matrix);
VectorCreate(rgb, 0.0f, 0.0f, 0.0f);
/*
** Iterate through each light.
*/
for (int z = 0; z < nlights; z++)
{
if (PlaneDistanceTo(surface->plane, lights[z].pos) > 0.0f)
{
Face* face = BSPFaceIntersect(bsproot, lights[z].pos, sample);
if (!face)
{
VectorSubtract(L, lights[z].pos, sample);
float brightness = 1.0f / VectorMagnitude(L);
VectorScale(L, L, brightness);
brightness *= VectorDotProduct(L, surface->plane);
rgb[0] += lights[z].rgb[0] * brightness;
rgb[1] += lights[z].rgb[1] * brightness;
rgb[2] += lights[z].rgb[2] * brightness;
}
}
}
int r = Clamp((int)(rgb[0] * 31.0f + 0.5f), 7, 31);
int g = Clamp((int)(rgb[1] * 31.0f + 0.5f), 7, 31);
int b = Clamp((int)(rgb[2] * 31.0f + 0.5f), 7, 31);
dst->pixels[y][x] = PixelCreate(r, g, b);
}
}
}
/*******************************************************************************
** BSPCreateHullPlanes
*******************************************************************************/
void
BSPCreateHullPlanes(BSP* bsp)
{
if (bsp)
{
for (int i = 0; i < NumberOf(bsp->faces); i++)
{
FaceCreateHullPlanes(&bsp->faces[i]);
}
BSPCreateHullPlanes(bsp->sides[0]);
BSPCreateHullPlanes(bsp->sides[1]);
}
}
/*******************************************************************************
** FaceSurfacesCreate
*******************************************************************************/
void
FaceSurfacesCreate(Face* faces)
{
static Vector temp[MAX_POINTS];
Surface surface1, surface2, surface3;
int i, j, newfaces;
assert(faces);
/*
** Reset all the face surfaces.
*/
for (i = 0; i < NumberOf(faces); i++)
{
faces[i].surfaceid = -1;
}
nsurfaces = 0;
for (i = 0; i < NumberOf(faces); i++)
{
Face* face1 = &faces[i];
float angle = 0.0;
float sz = 0.0;
if (face1->colour == 2)
{
angle = 22.0;
sz = -8000.0;
}
if (face1->surfaceid == -1)
{
face1->surfaceid = nsurfaces++;
ThrowIf(nsurfaces >= MAX_SURFACES, "Too many Surfaces");
int npoints = 0;
memcpy(temp[npoints], face1->vertices, sizeof(Vector) * NumberOf(face1->vertices));
npoints += NumberOf(face1->vertices);
assert(npoints < MAX_POINTS);
SurfaceCreate(&surface1,
temp,
npoints,
face1->colour,
angle, sz);
do
{
newfaces = 0;
for (j = 0; j < NumberOf(faces); j++)
{
Face* face2 = &faces[j];
if (
face2->surfaceid == -1 &&
face2->colour == face1->colour &&
PlanesAreEqual(face2->plane, face1->plane)
)
{
memcpy(temp[npoints], face2->vertices, sizeof(Vector) * NumberOf(face2->vertices));
npoints += NumberOf(face2->vertices);
assert(npoints < MAX_POINTS);
SurfaceCreate(&surface2,
face2->vertices,
NumberOf(face2->vertices),
face1->colour,
angle, sz);
SurfaceCreate(&surface3,
temp,
npoints,
face1->colour,
angle, sz);
int size1 = surface1.width * surface1.height + surface2.width * surface2.height;
int size2 = surface3.width * surface3.height;
if (face1->colour < 3 || size1 > size2)
{
surface1 = surface3;
face2->surfaceid = face1->surfaceid;
newfaces++;
}
else
{
npoints -= NumberOf(face2->vertices);
}
}
}
}
while (newfaces > 0);
surfaces[face1->surfaceid] = surface1;
}
}
for (i = 0; i < NumberOf(faces); i++)
{
memcpy(faces[i].plane, surfaces[faces[i].surfaceid].plane, sizeof(Plane));
}
fprintf(stderr, "Number of surfaces: %d\n", nsurfaces);
}
/*******************************************************************************
** BSPNodeWrite
*******************************************************************************/
void
BSPNodeWrite(BSP* bsp, FILE* file)
{
if (bsp)
{
fwrite(bsp->plane, sizeof(Plane), 1, file);
FaceArrayWrite(bsp->faces, file);
BSPNodeWrite(bsp->sides[0], file);
BSPNodeWrite(bsp->sides[1], file);
}
}
/*******************************************************************************
** BSPWrite
*******************************************************************************/
void
BSPWrite(BSP* bsp, FILE* file)
{
BSPNodeWrite(bsp, file);
MemoryWrite(surfaces, nsurfaces, sizeof(Surface), file);
IntWrite(nsurfaces, file);
for (int i = 0; i < nsurfaces; i++)
{
PictureWrite(&lightmaps[i], file);
}
MemoryWrite(lights, nlights, sizeof(Light), file);
}
char* title =
"\nDEBSP Compiler - (c) 1999-2000 by Derek J. Evans\n\n"
"Usage:\n\n"
" debsp INPUT <input filename>\n\n"
"Options:\n\n"
" -v Verbose\n"
" -s Shadows\n"
" -l Calculate lightmaps\n\n";
void
Main(void)
{
String filename;
strcpy(filename, FindArg("INPUT", title, NULL));
FILE* file_raw = FileOpen(FileNameExtChange(filename, "raw"), "r");
FILE* file_lights = FileOpen(FileNameExtChange(filename, "lit"), "r");
FILE* file_colours = FileOpen(FileNameExtChange(filename, "dat"), "r");
FILE* file_bsp = FileOpen(FileNameExtChange(filename, "bsp"), "wb");
switches.lighting = FindSwitch('l');
switches.shadows = FindSwitch('s');
Verbose("Reading world file");
Face* faces = FaceArrayReadMDL(file_raw, file_lights, file_colours);
Verbose("Creating surfaces");
FaceSurfacesCreate(faces);
Verbose("Compiling BSP tree");
BSP* bsp = BSPCompile(&faces);
Verbose("Creating hull planes");
BSPCreateHullPlanes(bsp);
Verbose("Calculating lightmaps");
for (int i = 0; i < nsurfaces; i++)
{
FaceLightMapCreate(&lightmaps[i], &surfaces[i], bsp);
}
Verbose("Writing BSP file");
BSPWrite(bsp, file_bsp);
fprintf(stderr, "Number of BSPs: %d\n", nbsps);
fprintf(stderr, "Number of nodes: %d\n", nbsps - nleafs);
fprintf(stderr, "Number of leafs: %d\n", nleafs);
Verbose("Done");
}