UltimateZoneBuilder/Source/Native/VPO/vpo_main.cpp
Magnus Norddahl 8eb522c873 Move vpo native code into BuilderNative as it is easier to manage. The plugins folder doesn't support including native dlls properly anyway.
Fix visplane explorer busy looping when waiting for data and reduce the used core count to 75% of the total available
Made vpo native code thread safe, removing the need for ungodly DLL patching hacks
2020-04-19 15:56:24 +02:00

398 lines
8.5 KiB
C++

//------------------------------------------------------------------------
// Visplane Overflow Library
//------------------------------------------------------------------------
//
// Copyright (C) 1993-1996 Id Software, Inc.
// Copyright (C) 2005 Simon Howard
// Copyright (C) 2012-2014 Andrew Apted
//
// 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.
//
//------------------------------------------------------------------------
#include "Precomp.h"
#include "vpo_local.h"
#include "vpo_api.h"
void vpo::Context::ClearError()
{
strcpy(error_buffer, "(No Error)");
}
void vpo::Context::SetError(const char *msg, ...)
{
va_list argptr;
va_start(argptr, msg);
memset(error_buffer, 0, sizeof(error_buffer));
vsnprintf(error_buffer, sizeof(error_buffer) - 1, msg, argptr);
va_end(argptr);
}
const char *VPO_GetError(VPOContext ctx)
{
vpo::Context* context = (vpo::Context*)ctx;
return context->error_buffer;
}
//------------------------------------------------------------------------
VPOContext VPO_NewContext()
{
return new vpo::Context();
}
void VPO_DeleteContext(VPOContext ctx)
{
vpo::Context* context = (vpo::Context*)ctx;
delete context;
}
int VPO_LoadWAD(VPOContext ctx, const char *wad_filename)
{
vpo::Context* context = (vpo::Context*)ctx;
context->ClearError();
// free any previously loaded wad
VPO_FreeWAD(ctx);
context->R_Init();
if (! context->W_AddFile(wad_filename))
{
context->SetError("Missing or invalid wad file: %s", wad_filename);
return -1;
}
return 0; // OK !
}
int VPO_OpenMap(VPOContext ctx, const char *map_name, bool *is_hexen)
{
vpo::Context* context = (vpo::Context*)ctx;
// check a wad is loaded
if (context->numlumps <= 0)
{
context->SetError("VPO_OpenMap called without any loaded wad");
return -1;
}
context->ClearError();
// close any previously loaded map
VPO_CloseMap(ctx);
const char *err_msg = context->P_SetupLevel(map_name, is_hexen);
if (err_msg)
{
context->SetError("%s", err_msg);
return -1;
}
return 0; // OK !
}
void VPO_FreeWAD(VPOContext ctx)
{
vpo::Context* context = (vpo::Context*)ctx;
VPO_CloseMap(ctx);
context->W_RemoveFile();
}
void VPO_CloseMap(VPOContext ctx)
{
vpo::Context* context = (vpo::Context*)ctx;
context->ClearError();
context->last_x = -77777;
context->last_y = -77777;
context->last_sector = NULL;
context->P_FreeLevelData();
}
const char * VPO_GetMapName(VPOContext ctx, unsigned int index, bool *is_hexen)
{
vpo::Context* context = (vpo::Context*)ctx;
char *buffer = context->mapname_buffer;
for (int lump_i = 0 ; lump_i < context->numlumps ; lump_i++)
{
if (! context->lumpinfo[lump_i].is_map_header)
continue;
if (index == 0)
{
// found it
memcpy(buffer, context->lumpinfo[lump_i].name, 8);
buffer[8] = 0;
if (is_hexen)
*is_hexen = context->lumpinfo[lump_i].is_hexen;
return buffer;
}
index--;
}
// not found
return NULL;
}
int VPO_GetLinedef(VPOContext ctx, unsigned int index, int *x1, int *y1, int *x2, int *y2)
{
vpo::Context* context = (vpo::Context*)ctx;
if (index >= (unsigned int)context->numlines)
return -1;
const vpo::line_t *L = &context->lines[index];
*x1 = L->v1->x >> FRACBITS;
*y1 = L->v1->y >> FRACBITS;
*x2 = L->v2->x >> FRACBITS;
*y2 = L->v2->y >> FRACBITS;
return L->backsector ? 2 : 1;
}
int VPO_GetSeg(VPOContext ctx, unsigned int index, int *linedef, int *side,
int *x1, int *y1, int *x2, int *y2)
{
vpo::Context* context = (vpo::Context*)ctx;
if (index >= (unsigned int)context->numsegs)
return -1;
const vpo::seg_t *seg = &context->segs[index];
const vpo::line_t *L = seg->linedef;
*x1 = seg->v1->x >> FRACBITS;
*y1 = seg->v1->y >> FRACBITS;
*x2 = seg->v2->x >> FRACBITS;
*y2 = seg->v2->y >> FRACBITS;
*linedef = (L - context->lines);
*side = 0;
if (L->sidenum[1] >= 0 && seg->sidedef == &context->sides[L->sidenum[1]])
*side = 1;
return 0;
}
void VPO_GetBBox(VPOContext ctx, int *x1, int *y1, int *x2, int *y2)
{
vpo::Context* context = (vpo::Context*)ctx;
*x1 = (context->Map_bbox[vpo::BOXLEFT] >> FRACBITS);
*y1 = (context->Map_bbox[vpo::BOXBOTTOM] >> FRACBITS);
*x2 = (context->Map_bbox[vpo::BOXRIGHT] >> FRACBITS);
*y2 = (context->Map_bbox[vpo::BOXTOP] >> FRACBITS);
}
void VPO_OpenDoorSectors(VPOContext ctx, int dir)
{
vpo::Context* context = (vpo::Context*)ctx;
for (int i = 0 ; i < context->numsectors ; i++)
{
vpo::sector_t *sec = &context->sectors[i];
if (sec->is_door == 0)
continue;
if (dir > 0) // open them
{
if (sec->is_door > 0)
sec->ceilingheight = sec->alt_height;
else
sec->floorheight = sec->alt_height;
}
else if (dir < 0) // close them
{
if (sec->is_door > 0)
sec->ceilingheight = sec->floorheight;
else
sec->floorheight = sec->ceilingheight;
}
}
}
//------------------------------------------------------------------------
int VPO_TestSpot(VPOContext ctx, int x, int y, int dz, int angle,
int *num_visplanes, int *num_drawsegs,
int *num_openings, int *num_solidsegs)
{
vpo::Context* context = (vpo::Context*)ctx;
// the actual spot we will use
// (this prevents issues with X_SectorForPoint getting the wrong
// value when the casted ray hits a vertex)
vpo::fixed_t rx = (x << FRACBITS) + (FRACUNIT / 2);
vpo::fixed_t ry = (y << FRACBITS) + (FRACUNIT / 2);
// check if spot is outside the map
if (rx < context->Map_bbox[vpo::BOXLEFT] ||
rx > context->Map_bbox[vpo::BOXRIGHT] ||
ry < context->Map_bbox[vpo::BOXBOTTOM] ||
ry > context->Map_bbox[vpo::BOXTOP])
{
return RESULT_IN_VOID;
}
// optimization: we cache the last sector lookup
vpo::sector_t *sec;
if (x == context->last_x && y == context->last_y)
sec = context->last_sector;
else
{
sec = context->X_SectorForPoint(rx, ry);
context->last_x = x;
context->last_y = y;
context->last_sector = sec;
}
if (! sec)
return RESULT_IN_VOID;
vpo::fixed_t rz;
if (dz < 0)
rz = sec->ceilingheight + (dz << FRACBITS);
else
rz = sec->floorheight + (dz << FRACBITS);
if (rz <= sec->floorheight || rz >= sec->ceilingheight)
return RESULT_BAD_Z;
// convert angle to the 32-bit BAM representation
if (angle == 360)
angle = 0;
vpo::fixed_t ang2 = vpo::FixedDiv(angle << FRACBITS, 360 << FRACBITS);
vpo::angle_t r_ang = (vpo::angle_t) (ang2 << 16);
int result = RESULT_OK;
// perform a no-draw render and see how many visplanes were needed
try
{
context->R_RenderView(rx, ry, rz, r_ang);
}
catch (vpo::overflow_exception&)
{
result = RESULT_OVERFLOW;
}
*num_visplanes = MAX(*num_visplanes, context->total_visplanes);
*num_drawsegs = MAX(*num_drawsegs, context->total_drawsegs);
*num_openings = MAX(*num_openings, context->total_openings);
*num_solidsegs = MAX(*num_solidsegs, context->max_solidsegs);
return result;
}
//------------------------------------------------------------------------
#if 0 // VPO_TEST_PROGRAM
// NOTE: this is out of date and will not compile
#define EYE_HEIGHT 41
int main(int argc, char **argv)
{
printf("----------------\n");
printf("VPO TEST PROGRAM\n");
printf("----------------\n");
printf("\n");
if (argc < 5 ||
(strcmp (argv[1], "-h") == 0 ||
strcmp (argv[1], "--help") == 0 ||
strcmp (argv[1], "/?") == 0) )
{
printf("Usage: vpotest file.wad MAP01 x y angle\n");
fflush(stdout);
return 0;
}
const char *filename = argv[1];
const char *map = argv[2];
int x = atoi(argv[3]);
int y = atoi(argv[4]);
int angle = atoi(argv[5]);
printf("Loading file: %s [%s]\n", filename, map);
VPOContext context = VPO_NewContext();
if (VPO_LoadWAD(context, filename) != 0)
{
printf("ERROR: %s\n", VPO_GetError());
fflush(stdout);
return 1;
}
if (VPO_OpenMap(context, map) != 0)
{
printf("ERROR: %s\n", VPO_GetError());
fflush(stdout);
VPO_FreeWAD();
return 1;
}
int vp_num = VPO_TestSpot(context, TEST_VISPLANES, x, y, EYE_HEIGHT, angle);
printf("\n");
printf("Visplanes @ (%d %d) ---> %d\n", x, y, vp_num);
fflush(stdout);
VPO_CloseMap(context);
VPO_FreeWAD(context);
VPO_DeleteContext(context);
return 0;
}
#endif // VPO_TEST_PROGRAM
//--- editor settings ---
// vi:ts=4:sw=4:noexpandtab