- fix race condition crash

- rewrote kexWorker to use std::function
- added --threads, --samples, --size command line arguments
- changed the default threading to be what the processor supports (64 threads on my Threadripper, muhahaha!)
This commit is contained in:
Magnus Norddahl 2018-10-30 12:37:22 +01:00
parent 6459b10724
commit 81a70d7c96
6 changed files with 343 additions and 515 deletions

View file

@ -22,6 +22,9 @@
//#include "rejectbuilder.h"
#include <memory>
extern int LMDims;
extern int Samples;
extern void ShowView (FLevel *level);
enum
@ -597,9 +600,9 @@ void FProcessor::BuildNodes()
void FProcessor::BuildLightmaps(const char *configFile)
{
LMBuilder.ambience = 0.0f;
LMBuilder.samples = 8;
LMBuilder.textureWidth = LIGHTMAP_MAX_SIZE;
LMBuilder.textureHeight = LIGHTMAP_MAX_SIZE;
LMBuilder.samples = Samples;
LMBuilder.textureWidth = LMDims;
LMBuilder.textureHeight = LMDims;
Level.ParseConfigFile(configFile);
Level.SetupDlight();

View file

@ -42,30 +42,8 @@
#include <map>
#include <vector>
kexWorker lightmapWorker;
const kexVec3 kexLightmapBuilder::gridSize(64, 64, 128);
//
// LightmapWorkerFunc
//
static void LightmapWorkerFunc(void *data, int id)
{
kexLightmapBuilder *builder = static_cast<kexLightmapBuilder*>(data);
builder->LightSurface(id);
}
//
// LightGridWorkerFunc
//
static void LightGridWorkerFunc(void *data, int id)
{
kexLightmapBuilder *builder = static_cast<kexLightmapBuilder*>(data);
builder->LightGrid(id);
}
//
// kexLightmapBuilder::kexLightmapBuilder
//
@ -536,15 +514,15 @@ void kexLightmapBuilder::TraceSurface(surface_t *surface)
int width = surface->lightmapDims[0];
int height = surface->lightmapDims[1];
std::unique_lock<std::mutex> lock(mutex);
// now that we know the width and height of this block, see if we got
// room for it in the light map texture. if not, then we must allocate
// a new texture
if(!MakeRoomForBlock(width, height, &x, &y, &surface->lightmapNum))
{
// allocate a new texture for this block
lightmapWorker.LockMutex();
NewTexture();
lightmapWorker.UnlockMutex();
if(!MakeRoomForBlock(width, height, &x, &y, &surface->lightmapNum))
{
@ -553,6 +531,8 @@ void kexLightmapBuilder::TraceSurface(surface_t *surface)
}
}
lock.unlock();
// calculate texture coordinates
for(i = 0; i < surface->numVerts; i++)
{
@ -567,9 +547,9 @@ void kexLightmapBuilder::TraceSurface(surface_t *surface)
surface->lightmapOffs[1] = y;
}
lightmapWorker.LockMutex();
std::unique_lock<std::mutex> lock(mutex);
currentTexture = textures[surface->lightmapNum];
lightmapWorker.UnlockMutex();
lock.unlock();
// store results to lightmap texture
for(i = 0; i < sampleHeight; i++)
@ -581,9 +561,9 @@ void kexLightmapBuilder::TraceSurface(surface_t *surface)
surface->lightmapOffs[0]) * 3);
// convert RGB to bytes
rgb[0] = (byte)(colorSamples[i][j][0] * 255);
rgb[1] = (byte)(colorSamples[i][j][1] * 255);
rgb[2] = (byte)(colorSamples[i][j][2] * 255);
rgb[0] = (uint32_t)(colorSamples[i][j][0] * 255 + 0.5f);
rgb[1] = (uint32_t)(colorSamples[i][j][1] * 255 + 0.5f);
rgb[2] = (uint32_t)(colorSamples[i][j][2] * 255 + 0.5f);
currentTexture[offs + j * 3 + 0] = rgb[0];
currentTexture[offs + j * 3 + 1] = rgb[1];
@ -611,12 +591,12 @@ void kexLightmapBuilder::LightSurface(const int surfid)
BuildSurfaceParams(surfaces[surfid]);
TraceSurface(surfaces[surfid]);
lightmapWorker.LockMutex();
std::unique_lock<std::mutex> lock(mutex);
remaining = (float)processed / (float)numsurfs;
processed++;
printf("%i%c surfaces done\r", (int)(remaining * 100.0f), '%');
lightmapWorker.UnlockMutex();
}
//
@ -822,11 +802,11 @@ void kexLightmapBuilder::LightGrid(const int gridid)
kexMath::Clamp(gridMap[gridid].color, 0, 1);
lightmapWorker.LockMutex();
std::unique_lock<std::mutex> lock(mutex);
remaining = (float)processed / (float)numLightGrids;
printf("%i%c cells done\r", (int)(remaining * 100.0f), '%');
lightmapWorker.UnlockMutex();
}
//
@ -841,15 +821,12 @@ void kexLightmapBuilder::CreateLightmaps(FLevel &doomMap)
CreateLightGrid();
printf("------------- Tracing surfaces -------------\n");
lightmapWorker.RunThreads(surfaces.Length(), this, LightmapWorkerFunc);
while(!lightmapWorker.FinishedAllJobs())
{
Delay(1000);
}
kexWorker::RunJob(surfaces.Length(), [=](int id) {
LightSurface(id);
});
printf("Texels traced: %i \n\n", tracedTexels);
lightmapWorker.Destroy();
}
//
@ -895,12 +872,9 @@ void kexLightmapBuilder::CreateLightGrid()
(int)(gridBlock.x * gridBlock.y), hb_static);
// process all grid cells
lightmapWorker.RunThreads(count, this, LightGridWorkerFunc);
while(!lightmapWorker.FinishedAllJobs())
{
Delay(1000);
}
kexWorker::RunJob(count, [=](int id) {
LightGrid(id);
});
printf("\nGrid cells: %i\n\n", count);
}

View file

@ -28,6 +28,7 @@
#pragma once
#include "surfaces.h"
#include <mutex>
#define LIGHTMAP_MAX_SIZE 1024
@ -87,4 +88,6 @@ private:
kexBBox worldGrid;
kexBBox gridBound;
kexVec3 gridBlock;
std::mutex mutex;
};

View file

@ -1,407 +1,36 @@
//-----------------------------------------------------------------------------
// Note: this is a modified version of dlight. It is not the original software.
//-----------------------------------------------------------------------------
//
// Copyright (c) 2013-2014 Samuel Villarreal
// svkaiser@gmail.com
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
// distribution.
//
//-----------------------------------------------------------------------------
//
// DESCRIPTION: Worker Threads
//
//-----------------------------------------------------------------------------
#include "common.h"
#include "worker.h"
#include <vector>
#include <thread>
#include <algorithm>
int kexWorker::maxWorkThreads = MAX_THREADS;
extern int NumThreads;
//
// WorkThread
//
void *WorkThread(void *p)
void kexWorker::RunJob(int count, std::function<void(int)> callback)
{
jobFuncArgs_t *args = (jobFuncArgs_t*)p;
kexWorker *worker = args->worker;
int numThreads = NumThreads;
if (numThreads <= 0)
numThreads = std::thread::hardware_concurrency();
if (numThreads <= 0)
numThreads = 4;
while(1)
numThreads = std::min(numThreads, count);
std::vector<std::thread> threads;
for (int threadIndex = 0; threadIndex < numThreads; threadIndex++)
{
int jobid;
threads.push_back(std::thread([=]() {
worker->LockMutex();
if(worker->FinishedAllJobs())
int start = threadIndex * count / numThreads;
int end = std::min((threadIndex + 1) * count / numThreads, count);
for (int i = start; i < end; i++)
{
worker->UnlockMutex();
break;
callback(i);
}
jobid = worker->DispatchJob();
worker->UnlockMutex();
worker->RunJob(args->data, jobid);
}));
}
return 0;
}
//
// kexWorker::kexWorker
//
kexWorker::kexWorker()
{
this->numWorkLoad = 0;
this->jobsWorked = 0;
this->job = NULL;
memset(this->jobArgs, 0, sizeof(this->jobArgs));
}
//
// kexWorker::~kexWorker
//
kexWorker::~kexWorker()
{
}
//
// kexWorker::LockMutex
//
void kexWorker::LockMutex()
{
mutex.lock();
}
//
// kexWorker::UnlockMutex
//
void kexWorker::UnlockMutex()
{
mutex.unlock();
}
//
// kexWorker::Destroy
//
void kexWorker::Destroy()
{
}
//
// kexWorker::RunThreads
//
void kexWorker::RunThreads(const int count, void *data, jobFunc_t jobFunc)
{
job = jobFunc;
numWorkLoad = count;
jobsWorked = 0;
for(int i = 0; i < kexWorker::maxWorkThreads; ++i)
{
jobArgs[i].worker = this;
jobArgs[i].jobID = i;
jobArgs[i].data = data;
threads[i] = std::thread([=]() { WorkThread(&jobArgs[i]); });
}
for(int i = 0; i < kexWorker::maxWorkThreads; ++i)
{
for (int i = 0; i < numThreads; i++)
threads[i].join();
}
}
/////////////////////////////////////////////////////////////////////////////
// Stuff from old main.cpp
void Error(const char *error, ...)
{
va_list argptr;
va_start(argptr, error);
vprintf(error, argptr);
va_end(argptr);
printf("\n");
exit(1);
}
char *Va(const char *str, ...)
{
va_list v;
static char vastr[1024];
va_start(v, str);
vsprintf(vastr, str, v);
va_end(v);
return vastr;
}
void Delay(int ms)
{
std::this_thread::sleep_for(std::chrono::milliseconds(ms));
}
//
// GetSeconds
//
const int64_t GetSeconds()
{
return time(0);
}
//
// Main
//
#if 0
int main(int argc, char **argv)
{
kexWadFile wadFile;
kexWadFile outWadFile;
FLevel doomMap;
lump_t *lmLump;
kexArray<int> ignoreLumps;
kexLightmapBuilder builder;
kexStr configFile("strife_sve.cfg");
bool bWriteTGA;
int map = 1;
int arg = 1;
printf("DLight (c) 2013-2014 Samuel Villarreal\n\n");
if (argc < 1 || argv[1] == NULL)
{
printf("Usage: dlight [options] [wadfile]\n");
printf("Use -help for list of options\n");
return 0;
}
basePath = argv[0];
basePath.StripFile();
bWriteTGA = false;
while (1)
{
if (!strcmp(argv[arg], "-help"))
{
printf("Options:\n");
printf("-help: displays all known options\n");
printf("-map: process lightmap for MAP##\n");
printf("-samples: set texel sampling size (lowest = higher quaility but\n");
printf(" slow compile time) must be in powers of two\n");
printf("-ambience: set global ambience value for lightmaps (0.0 - 1.0)\n");
printf("-size: lightmap texture dimentions for width and height\n");
printf(" must be in powers of two (1, 2, 4, 8, 16, etc)\n");
printf("-threads: set total number of threads (1 min, 128 max)\n");
printf("-config: specify a config file to parse (default: strife_sve.cfg)\n");
printf("-writetga: dumps lightmaps to targa (.TGA) files\n");
arg++;
return 0;
}
else if (!strcmp(argv[arg], "-map"))
{
if (argv[arg + 1] == NULL)
{
Error("Specify map number for -map\n");
return 1;
}
map = atoi(argv[++arg]);
arg++;
}
else if (!strcmp(argv[arg], "-samples"))
{
if (argv[arg + 1] == NULL)
{
Error("Specify value for -samples\n");
return 1;
}
builder.samples = atoi(argv[++arg]);
if (builder.samples <= 0)
{
builder.samples = 1;
}
if (builder.samples > 128)
{
builder.samples = 128;
}
builder.samples = kexMath::RoundPowerOfTwo(builder.samples);
arg++;
}
else if (!strcmp(argv[arg], "-ambience"))
{
if (argv[arg + 1] == NULL)
{
Error("Specify value for -ambience\n");
return 1;
}
builder.ambience = (float)atof(argv[++arg]);
if (builder.ambience < 0)
{
builder.ambience = 0;
}
if (builder.ambience > 1)
{
builder.ambience = 1;
}
}
else if (!strcmp(argv[arg], "-size"))
{
int lmDims;
if (argv[arg + 1] == NULL)
{
Error("Specify value for -size\n");
return 1;
}
lmDims = atoi(argv[++arg]);
if (lmDims <= 0)
{
lmDims = 1;
}
if (lmDims > LIGHTMAP_MAX_SIZE)
{
lmDims = LIGHTMAP_MAX_SIZE;
}
lmDims = kexMath::RoundPowerOfTwo(lmDims);
builder.textureWidth = lmDims;
builder.textureHeight = lmDims;
arg++;
}
else if (!strcmp(argv[arg], "-threads"))
{
kexWorker::maxWorkThreads = atoi(argv[++arg]);
kexMath::Clamp(kexWorker::maxWorkThreads, 1, MAX_THREADS);
arg++;
}
else if (!strcmp(argv[arg], "-config"))
{
configFile = argv[++arg];
arg++;
}
else if (!strcmp(argv[arg], "-writetga"))
{
bWriteTGA = true;
arg++;
}
else
{
break;
}
}
if (argv[arg] == NULL)
{
printf("Usage: dlight [options] [wadfile]\n");
return 0;
}
if (!wadFile.Open(argv[arg]))
{
return 1;
}
// concat the base path to light def file if there is none
if (configFile.IndexOf("\\") == -1 && configFile.IndexOf("/") == -1)
{
configFile = basePath + configFile;
}
int starttime = (int)GetSeconds();
printf("---------------- Parsing config file ----------------\n\n");
doomMap.ParseConfigFile(configFile.c_str());
printf("------------- Building level structures -------------\n\n");
wadFile.SetCurrentMap(map);
doomMap.BuildMapFromWad(wadFile);
printf("----------- Allocating surfaces from level ----------\n\n");
Surface_AllocateFromMap(doomMap);
printf("---------------- Allocating lights ----------------\n\n");
doomMap.CreateLights();
printf("---------------- Creating lightmaps ---------------\n\n");
builder.CreateLightmaps(doomMap);
doomMap.CleanupThingLights();
if (bWriteTGA)
{
builder.WriteTexturesToTGA();
}
printf("------------------ Rebuilding wad ----------------\n\n");
wadFile.CreateBackup();
lmLump = wadFile.GetLumpFromName(Va("LM_MAP%02d", wadFile.currentmap));
if (lmLump)
{
int lumpnum = lmLump - wadFile.lumps;
ignoreLumps.Push(lumpnum + ML_LM_LABEL);
ignoreLumps.Push(lumpnum + ML_LM_CELLS);
ignoreLumps.Push(lumpnum + ML_LM_SUN);
ignoreLumps.Push(lumpnum + ML_LM_SURFS);
ignoreLumps.Push(lumpnum + ML_LM_TXCRD);
ignoreLumps.Push(lumpnum + ML_LM_LMAPS);
}
outWadFile.InitForWrite();
outWadFile.CopyLumpsFromWadFile(wadFile, ignoreLumps);
outWadFile.AddLump(Va("LM_MAP%02d", wadFile.currentmap), 0, NULL);
builder.AddLightGridLump(outWadFile);
builder.AddLightmapLumps(outWadFile);
printf("------------- Writing %s -------------\n\n", wadFile.wadName.c_str());
outWadFile.Write(wadFile.wadName);
outWadFile.Close();
wadFile.Close();
printf("----------------- Shutting down -----------------\n\n");
Mem_Purge(hb_static);
int proctime = (int)GetSeconds() - starttime;
printf("\nBuild time: %d:%02d:%02d\n",
proctime / 3600, (proctime / 60) % 60, proctime % 60);
return 0;
}
#endif

View file

@ -1,75 +1,10 @@
//-----------------------------------------------------------------------------
// Note: this is a modified version of dlight. It is not the original software.
//-----------------------------------------------------------------------------
//
// Copyright (c) 2013-2014 Samuel Villarreal
// svkaiser@gmail.com
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
// distribution.
//
#pragma once
#include <thread>
#include <mutex>
// There's a race condition in the code that causes it to sometimes fail if using multiple threads
//#define MAX_THREADS 128
#define MAX_THREADS 1
class kexWorker;
typedef void(*jobFunc_t)(void*, int);
struct jobFuncArgs_t
{
kexWorker *worker;
void *data;
int jobID;
};
#include <functional>
class kexWorker
{
public:
kexWorker();
~kexWorker();
void RunThreads(const int count, void *data, jobFunc_t jobFunc);
void LockMutex();
void UnlockMutex();
void Destroy();
bool FinishedAllJobs() { return jobsWorked == numWorkLoad; }
int DispatchJob() { int j = jobsWorked; jobsWorked++; return j; }
void RunJob(void *data, const int jobID) { job(data, jobID); }
jobFuncArgs_t *Args(const int id) { return &jobArgs[id]; }
const int JobsWorked() const { return jobsWorked; }
static int maxWorkThreads;
private:
std::thread threads[MAX_THREADS];
jobFuncArgs_t jobArgs[MAX_THREADS];
std::mutex mutex;
jobFunc_t job;
int jobsWorked;
int numWorkLoad;
static void RunJob(int count, std::function<void(int i)> callback);
};

View file

@ -53,6 +53,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <thread>
#include "framework/zdray.h"
#include "wad/wad.h"
@ -113,6 +114,9 @@ bool GLOnly = true;// false;
bool V5GLNodes = false;
bool HaveSSE1, HaveSSE2;
int SSELevel;
int NumThreads = 0;
int LMDims = LIGHTMAP_MAX_SIZE;
int Samples = 8;
// PRIVATE DATA DEFINITIONS ------------------------------------------------
@ -148,6 +152,9 @@ static option long_opts[] =
{"no-sse", no_argument, 0, 1002},
{"no-sse2", no_argument, 0, 1003},
{"comments", no_argument, 0, 'c'},
{"threads", required_argument, 0, 'j'},
{"samples", required_argument, 0, 'Q'},
{"size", required_argument, 0, 'S'},
{0,0,0,0}
};
@ -426,6 +433,21 @@ static void ParseArgs(int argc, char **argv)
case 1003: // Disable only SSE2 ClassifyLine routine
HaveSSE2 = false;
break;
case 'j':
NumThreads = atoi(optarg);
break;
case 'Q':
Samples = atoi(optarg);
if (Samples <= 0) Samples = 1;
if (Samples > 128) Samples = 128;
Samples = kexMath::RoundPowerOfTwo(Samples);
break;
case 'S':
LMDims = atoi(optarg);
if (LMDims <= 0) LMDims = 1;
if (LMDims > LIGHTMAP_MAX_SIZE) LMDims = LIGHTMAP_MAX_SIZE;
LMDims = kexMath::RoundPowerOfTwo(LMDims);
break;
case 1000:
ShowUsage();
exit(0);
@ -466,6 +488,11 @@ static void ShowUsage()
" -s, --split-cost=NNN Cost for splitting segs (default %d)\n"
" -d, --diagonal-cost=NNN Cost for avoiding diagonal splitters (default %d)\n"
" -P, --no-polyobjs Do not check for polyobject subsector splits\n"
" -j, --threads=NNN Number of threads used for raytracing (default %d)\n"
" -Q, --samples=NNN Set texel sampling size (lowest = higher quaility but\n"
" slow compile time) must be in powers of two\n"
" -S, --size=NNN lightmap texture dimensions for width and height\n"
" must be in powers of two (1, 2, 4, 8, 16, etc)\n"
#ifdef _WIN32
" -v, --view View the nodes\n"
#endif
@ -481,6 +508,7 @@ static void ShowUsage()
, MaxSegs /* Partition size */
, SplitCost
, AAPreference
, (int)std::thread::hardware_concurrency()
);
}
@ -678,10 +706,6 @@ static void CheckSSE()
}
#endif
//==========================================================================
//
// Warn
//
//==========================================================================
void Warn(const char *format, ...)
@ -697,3 +721,263 @@ void Warn(const char *format, ...)
vprintf(format, marker);
va_end(marker);
}
void Error(const char *error, ...)
{
va_list argptr;
va_start(argptr, error);
vprintf(error, argptr);
va_end(argptr);
printf("\n");
exit(1);
}
char *Va(const char *str, ...)
{
va_list v;
static char vastr[1024];
va_start(v, str);
vsprintf(vastr, str, v);
va_end(v);
return vastr;
}
void Delay(int ms)
{
std::this_thread::sleep_for(std::chrono::milliseconds(ms));
}
const int64_t GetSeconds()
{
return time(0);
}
#if 0 // Old dlight main function
int main(int argc, char **argv)
{
kexWadFile wadFile;
kexWadFile outWadFile;
FLevel doomMap;
lump_t *lmLump;
kexArray<int> ignoreLumps;
kexLightmapBuilder builder;
kexStr configFile("strife_sve.cfg");
bool bWriteTGA;
int map = 1;
int arg = 1;
printf("DLight (c) 2013-2014 Samuel Villarreal\n\n");
if (argc < 1 || argv[1] == NULL)
{
printf("Usage: dlight [options] [wadfile]\n");
printf("Use -help for list of options\n");
return 0;
}
basePath = argv[0];
basePath.StripFile();
bWriteTGA = false;
while (1)
{
if (!strcmp(argv[arg], "-help"))
{
printf("Options:\n");
printf("-help: displays all known options\n");
printf("-map: process lightmap for MAP##\n");
printf("-samples: set texel sampling size (lowest = higher quaility but\n");
printf(" slow compile time) must be in powers of two\n");
printf("-ambience: set global ambience value for lightmaps (0.0 - 1.0)\n");
printf("-size: lightmap texture dimentions for width and height\n");
printf(" must be in powers of two (1, 2, 4, 8, 16, etc)\n");
printf("-threads: set total number of threads (1 min, 128 max)\n");
printf("-config: specify a config file to parse (default: strife_sve.cfg)\n");
printf("-writetga: dumps lightmaps to targa (.TGA) files\n");
arg++;
return 0;
}
else if (!strcmp(argv[arg], "-map"))
{
if (argv[arg + 1] == NULL)
{
Error("Specify map number for -map\n");
return 1;
}
map = atoi(argv[++arg]);
arg++;
}
else if (!strcmp(argv[arg], "-samples"))
{
if (argv[arg + 1] == NULL)
{
Error("Specify value for -samples\n");
return 1;
}
builder.samples = atoi(argv[++arg]);
if (builder.samples <= 0)
{
builder.samples = 1;
}
if (builder.samples > 128)
{
builder.samples = 128;
}
builder.samples = kexMath::RoundPowerOfTwo(builder.samples);
arg++;
}
else if (!strcmp(argv[arg], "-ambience"))
{
if (argv[arg + 1] == NULL)
{
Error("Specify value for -ambience\n");
return 1;
}
builder.ambience = (float)atof(argv[++arg]);
if (builder.ambience < 0)
{
builder.ambience = 0;
}
if (builder.ambience > 1)
{
builder.ambience = 1;
}
}
else if (!strcmp(argv[arg], "-size"))
{
int lmDims;
if (argv[arg + 1] == NULL)
{
Error("Specify value for -size\n");
return 1;
}
lmDims = atoi(argv[++arg]);
if (lmDims <= 0)
{
lmDims = 1;
}
if (lmDims > LIGHTMAP_MAX_SIZE)
{
lmDims = LIGHTMAP_MAX_SIZE;
}
lmDims = kexMath::RoundPowerOfTwo(lmDims);
builder.textureWidth = lmDims;
builder.textureHeight = lmDims;
arg++;
}
else if (!strcmp(argv[arg], "-threads"))
{
kexWorker::maxWorkThreads = atoi(argv[++arg]);
kexMath::Clamp(kexWorker::maxWorkThreads, 1, MAX_THREADS);
arg++;
}
else if (!strcmp(argv[arg], "-config"))
{
configFile = argv[++arg];
arg++;
}
else if (!strcmp(argv[arg], "-writetga"))
{
bWriteTGA = true;
arg++;
}
else
{
break;
}
}
if (argv[arg] == NULL)
{
printf("Usage: dlight [options] [wadfile]\n");
return 0;
}
if (!wadFile.Open(argv[arg]))
{
return 1;
}
// concat the base path to light def file if there is none
if (configFile.IndexOf("\\") == -1 && configFile.IndexOf("/") == -1)
{
configFile = basePath + configFile;
}
int starttime = (int)GetSeconds();
printf("---------------- Parsing config file ----------------\n\n");
doomMap.ParseConfigFile(configFile.c_str());
printf("------------- Building level structures -------------\n\n");
wadFile.SetCurrentMap(map);
doomMap.BuildMapFromWad(wadFile);
printf("----------- Allocating surfaces from level ----------\n\n");
Surface_AllocateFromMap(doomMap);
printf("---------------- Allocating lights ----------------\n\n");
doomMap.CreateLights();
printf("---------------- Creating lightmaps ---------------\n\n");
builder.CreateLightmaps(doomMap);
doomMap.CleanupThingLights();
if (bWriteTGA)
{
builder.WriteTexturesToTGA();
}
printf("------------------ Rebuilding wad ----------------\n\n");
wadFile.CreateBackup();
lmLump = wadFile.GetLumpFromName(Va("LM_MAP%02d", wadFile.currentmap));
if (lmLump)
{
int lumpnum = lmLump - wadFile.lumps;
ignoreLumps.Push(lumpnum + ML_LM_LABEL);
ignoreLumps.Push(lumpnum + ML_LM_CELLS);
ignoreLumps.Push(lumpnum + ML_LM_SUN);
ignoreLumps.Push(lumpnum + ML_LM_SURFS);
ignoreLumps.Push(lumpnum + ML_LM_TXCRD);
ignoreLumps.Push(lumpnum + ML_LM_LMAPS);
}
outWadFile.InitForWrite();
outWadFile.CopyLumpsFromWadFile(wadFile, ignoreLumps);
outWadFile.AddLump(Va("LM_MAP%02d", wadFile.currentmap), 0, NULL);
builder.AddLightGridLump(outWadFile);
builder.AddLightmapLumps(outWadFile);
printf("------------- Writing %s -------------\n\n", wadFile.wadName.c_str());
outWadFile.Write(wadFile.wadName);
outWadFile.Close();
wadFile.Close();
printf("----------------- Shutting down -----------------\n\n");
Mem_Purge(hb_static);
int proctime = (int)GetSeconds() - starttime;
printf("\nBuild time: %d:%02d:%02d\n",
proctime / 3600, (proctime / 60) % 60, proctime % 60);
return 0;
}
#endif