From 405bb743a04fa99ef9a9d30727bd2df2c044ab83 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Fri, 26 Oct 2018 09:15:14 +0200 Subject: [PATCH] Add dlight codebase to zdray --- CMakeLists.txt | 40 +- src/lightmap/common.h | 166 +++ src/lightmap/kexlib/array.h | 312 ++++++ src/lightmap/kexlib/binfile.cpp | 399 +++++++ src/lightmap/kexlib/binfile.h | 79 ++ src/lightmap/kexlib/kstring.cpp | 804 ++++++++++++++ src/lightmap/kexlib/kstring.h | 131 +++ src/lightmap/kexlib/math/angle.cpp | 405 ++++++++ src/lightmap/kexlib/math/bounds.cpp | 520 ++++++++++ src/lightmap/kexlib/math/mathlib.cpp | 99 ++ src/lightmap/kexlib/math/mathlib.h | 661 ++++++++++++ src/lightmap/kexlib/math/matrix.cpp | 717 +++++++++++++ src/lightmap/kexlib/math/plane.cpp | 251 +++++ src/lightmap/kexlib/math/pluecker.cpp | 107 ++ src/lightmap/kexlib/math/quaternion.cpp | 446 ++++++++ src/lightmap/kexlib/math/random.cpp | 99 ++ src/lightmap/kexlib/math/vector.cpp | 1268 +++++++++++++++++++++++ src/lightmap/kexlib/memheap.cpp | 403 +++++++ src/lightmap/kexlib/memheap.h | 114 ++ src/lightmap/kexlib/parser.cpp | 1134 ++++++++++++++++++++ src/lightmap/kexlib/parser.h | 175 ++++ src/lightmap/lightmap.cpp | 1213 ++++++++++++++++++++++ src/lightmap/lightmap.h | 93 ++ src/lightmap/lightsurface.cpp | 464 +++++++++ src/lightmap/lightsurface.h | 92 ++ src/lightmap/mapdata.cpp | 957 +++++++++++++++++ src/lightmap/mapdata.h | 276 +++++ src/lightmap/surfaces.cpp | 364 +++++++ src/lightmap/surfaces.h | 73 ++ src/lightmap/trace.cpp | 285 +++++ src/lightmap/trace.h | 59 ++ src/lightmap/wad.cpp | 326 ++++++ src/lightmap/wad.h | 164 +++ src/lightmap/worker.cpp | 418 ++++++++ src/lightmap/worker.h | 76 ++ 35 files changed, 13189 insertions(+), 1 deletion(-) create mode 100644 src/lightmap/common.h create mode 100644 src/lightmap/kexlib/array.h create mode 100644 src/lightmap/kexlib/binfile.cpp create mode 100644 src/lightmap/kexlib/binfile.h create mode 100644 src/lightmap/kexlib/kstring.cpp create mode 100644 src/lightmap/kexlib/kstring.h create mode 100644 src/lightmap/kexlib/math/angle.cpp create mode 100644 src/lightmap/kexlib/math/bounds.cpp create mode 100644 src/lightmap/kexlib/math/mathlib.cpp create mode 100644 src/lightmap/kexlib/math/mathlib.h create mode 100644 src/lightmap/kexlib/math/matrix.cpp create mode 100644 src/lightmap/kexlib/math/plane.cpp create mode 100644 src/lightmap/kexlib/math/pluecker.cpp create mode 100644 src/lightmap/kexlib/math/quaternion.cpp create mode 100644 src/lightmap/kexlib/math/random.cpp create mode 100644 src/lightmap/kexlib/math/vector.cpp create mode 100644 src/lightmap/kexlib/memheap.cpp create mode 100644 src/lightmap/kexlib/memheap.h create mode 100644 src/lightmap/kexlib/parser.cpp create mode 100644 src/lightmap/kexlib/parser.h create mode 100644 src/lightmap/lightmap.cpp create mode 100644 src/lightmap/lightmap.h create mode 100644 src/lightmap/lightsurface.cpp create mode 100644 src/lightmap/lightsurface.h create mode 100644 src/lightmap/mapdata.cpp create mode 100644 src/lightmap/mapdata.h create mode 100644 src/lightmap/surfaces.cpp create mode 100644 src/lightmap/surfaces.h create mode 100644 src/lightmap/trace.cpp create mode 100644 src/lightmap/trace.h create mode 100644 src/lightmap/wad.cpp create mode 100644 src/lightmap/wad.h create mode 100644 src/lightmap/worker.cpp create mode 100644 src/lightmap/worker.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 7351bbd..151662a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -144,6 +144,26 @@ set( SOURCES src/nodebuilder/nodebuild_gl.cpp src/nodebuilder/nodebuild_utility.cpp src/nodebuilder/nodebuild_classify_nosse2.cpp + src/lightmap/lightmap.cpp + src/lightmap/lightsurface.cpp + src/lightmap/mapdata.cpp + src/lightmap/surfaces.cpp + src/lightmap/trace.cpp + src/lightmap/wad.cpp + src/lightmap/worker.cpp + src/lightmap/kexlib/binfile.cpp + src/lightmap/kexlib/kstring.cpp + src/lightmap/kexlib/memheap.cpp + src/lightmap/kexlib/parser.cpp + src/lightmap/kexlib/math/angle.cpp + src/lightmap/kexlib/math/bounds.cpp + src/lightmap/kexlib/math/mathlib.cpp + src/lightmap/kexlib/math/matrix.cpp + src/lightmap/kexlib/math/plane.cpp + src/lightmap/kexlib/math/pluecker.cpp + src/lightmap/kexlib/math/quaternion.cpp + src/lightmap/kexlib/math/random.cpp + src/lightmap/kexlib/math/vector.cpp ) if( WIN32 ) set( SOURCES "${SOURCES}" src/viewer/view.cpp ) @@ -164,7 +184,22 @@ set( HEADERS src/framework/tarray.h src/framework/templates.h src/framework/zdray.h - src/framework/xs_Float.h ) + src/framework/xs_Float.h + src/lightmap/common.h + src/lightmap/lightmap.h + src/lightmap/lightsurface.h + src/lightmap/mapdata.h + src/lightmap/surfaces.h + src/lightmap/trace.h + src/lightmap/wad.h + src/lightmap/worker.h + src/lightmap/kexlib/array.h + src/lightmap/kexlib/binfile.h + src/lightmap/kexlib/kstring.h + src/lightmap/kexlib/memheap.h + src/lightmap/kexlib/parser.h + src/lightmap/kexlib/math/mathlib.h +) if( SSE_MATTERS ) if( FULL_SSE2 ) @@ -233,3 +268,6 @@ source_group("Sources\\Platform" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR source_group("Sources\\Platform\\Windows" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/src/platform/windows/.+") source_group("Sources\\Viewer" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/src/viewer/.+") source_group("Sources\\Wad" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/src/wad/.+") +source_group("Sources\\Lightmap" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/src/lightmap/.+") +source_group("Sources\\Lightmap\\kexlib" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/src/lightmap/kexlib/.+") +source_group("Sources\\Lightmap\\kexlib\\math" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/src/lightmap/kexlib/math/.+") diff --git a/src/lightmap/common.h b/src/lightmap/common.h new file mode 100644 index 0000000..c1fcf6e --- /dev/null +++ b/src/lightmap/common.h @@ -0,0 +1,166 @@ +//----------------------------------------------------------------------------- +// 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. +// + +#ifndef __COMMON_H__ +#define __COMMON_H__ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(disable: 4267) // warning C4267: 'argument': conversion from 'size_t' to 'int', possible loss of data +#pragma warning(disable: 4244) // warning C4244: '=': conversion from '__int64' to 'int', possible loss of data +#endif + +// narrow down the windows preprocessor bullshit down to just one macro define +#if defined(__WIN32__) || defined(__WIN32) || defined(_WIN32_) || defined(_WIN32) || defined(WIN32) +#define KEX_WIN32 +#else +#if defined(__APPLE__) +// lets us know what version of Mac OS X we're compiling on +#include "AvailabilityMacros.h" +#include "TargetConditionals.h" +#if TARGET_OS_IPHONE +// if compiling for iPhone +#define KEX_IPHONE +#else +// if not compiling for iPhone +#define KEX_MACOSX +#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050 +# error KexLIB for Mac OS X only supports deploying on 10.5 and above. +#endif // MAC_OS_X_VERSION_MIN_REQUIRED < 1050 +#if MAC_OS_X_VERSION_MAX_ALLOWED < 1060 +# error KexLIB for Mac OS X must be built with a 10.6 SDK or above. +#endif // MAC_OS_X_VERSION_MAX_ALLOWED < 1060 +#endif // TARGET_OS_IPHONE +#endif // defined(__APPLE__) +#endif // WIN32 + +#ifdef KEX_WIN32 +#include +#else +#include +#endif + +#define MAX_FILEPATH 256 +#define MAX_HASH 2048 + +typedef unsigned char byte; +typedef unsigned short word; +typedef unsigned long ulong; +typedef unsigned int uint; +typedef unsigned int dtexture; +typedef unsigned int rcolor; +typedef char filepath_t[MAX_FILEPATH]; + +typedef union +{ + int i; + float f; +} fint_t; + +#define ASCII_SLASH 47 +#define ASCII_BACKSLASH 92 + +#ifdef KEX_WIN32 +#define DIR_SEPARATOR '\\' +#define PATH_SEPARATOR ';' +#else +#define DIR_SEPARATOR '/' +#define PATH_SEPARATOR ':' +#endif + +#include +#define D_MININT INT_MIN +#define D_MAXINT INT_MAX + +#ifndef MAX +#define MAX(a,b) ((a)>(b)?(a):(b)) +#endif + +#ifndef MIN +#define MIN(a,b) ((a)<(b)?(a):(b)) +#endif + +#ifndef BETWEEN +#define BETWEEN(l,u,x) ((l)>(x)?(l):(x)>(u)?(u):(x)) +#endif + +#ifndef BIT +#define BIT(num) (1<<(num)) +#endif + +#if defined(KEX_WIN32) && !defined(__GNUC__) +#define KDECL __cdecl +#else +#define KDECL +#endif + +#ifdef ALIGNED +#undef ALIGNED +#endif + +#if defined(_MSC_VER) +#define ALIGNED(x) __declspec(align(x)) +#define PACKED +#elif defined(__GNUC__) +#define ALIGNED(x) __attribute__ ((aligned(x))) +#define PACKED __attribute__((packed)) +#else +#define ALIGNED(x) +#define PACKED +#endif + +// function inlining is available on most platforms, however, +// the GNU C __inline__ is too common and conflicts with a +// definition in other dependencies, so it needs to be factored +// out into a custom macro definition + +#if defined(__GNUC__) || defined(__APPLE__) +#define d_inline __inline__ +#elif defined(_MSC_VER) || defined(KEX_WIN32) +#define d_inline __forceinline +#else +#define d_inline +#endif + +#include "kexlib/memHeap.h" +#include "kexlib/kstring.h" +#include "kexlib/math/mathlib.h" + +void Error(const char *error, ...); +char *Va(const char *str, ...); +void Delay(int ms); +const int64_t GetSeconds(void); +const kexStr &FilePath(void); + +#endif diff --git a/src/lightmap/kexlib/array.h b/src/lightmap/kexlib/array.h new file mode 100644 index 0000000..286bdca --- /dev/null +++ b/src/lightmap/kexlib/array.h @@ -0,0 +1,312 @@ +//----------------------------------------------------------------------------- +// 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. +// + +#ifndef __KEXARRAY_H__ +#define __KEXARRAY_H__ + +#include + +template +class kexArray +{ +public: + kexArray(void); + ~kexArray(void); + + typedef int compare_t(const type*, const type*); + + void Push(type o); + void Pop(void); + void Empty(void); + void Init(void); + void Resize(unsigned int size); + type IndexOf(unsigned int index) const; + bool Contains(const type check) const; + void Splice(const unsigned int start, unsigned int len); + void Sort(compare_t *function); + void Sort(compare_t *function, unsigned int count); + + const unsigned int Length(void) const { return length; } + type GetData(const int index) { return data[index]; } + + type &operator[](unsigned int index); + kexArray &operator=(const kexArray &arr); + +protected: + type *data; + unsigned int length; + unsigned int aidx; +}; + +// +// kexArray::kexArray +// +template +kexArray::kexArray(void) +{ + Init(); +} + +// +// kexArray::~kexArray +// +template +kexArray::~kexArray(void) +{ + Empty(); +} + +// +// kexArray::Init +// +template +void kexArray::Init(void) +{ + data = NULL; + length = 0; + aidx = 0; +} + +// +// kexArray::Resize +// +template +void kexArray::Resize(unsigned int size) +{ + type *tmp; + + if(size == length) + { + return; + } + + if(size <= 0 && length != 0) + { + delete[] data; + data = NULL; + length = 0; + return; + } + + if(length == 0) + { + data = new type[size]; + length = size; + return; + } + + tmp = data; + data = new type[size]; + + for(unsigned int i = 0; i < length; i++) + { + data[i] = tmp[i]; + } + + length = size; + delete[] tmp; +} + +// +// kexArray::Push +// +template +void kexArray::Push(type o) +{ + Resize(length+1); + data[aidx++] = o; +} + +// +// kexArray::Pop +// +template +void kexArray::Pop(void) +{ + if(length == 0) + { + return; + } + + Resize(length-1); + aidx--; +} + +// +// kexArray::Empty +// +template +void kexArray::Empty(void) +{ + if(data && length > 0) + { + delete[] data; + data = NULL; + length = 0; + aidx = 0; + } +} + +// +// kexArray::IndexOf +// +template +type kexArray::IndexOf(unsigned int index) const +{ + if(index >= length) + { + index = length-1; + } + + return data[index]; +} + +// +// kexArray::Contains +// +template +bool kexArray::Contains(const type check) const +{ + for(unsigned int i = 0; i < length; ++i) + { + if(data[i] == check) + { + return true; + } + } + + return false; +} + +// +// kexArray::Splice +// +template +void kexArray::Splice(const unsigned int start, unsigned int len) +{ + if(length == 0 || len == 0) + { + return; + } + + if(len >= length) + { + len = length; + } + + type *tmp = new type[len]; + + for(unsigned int i = 0; i < len; i++) + { + tmp[i] = data[start+i]; + } + + delete[] data; + data = tmp; + length = length - len; + aidx = length-1; +} + +// +// kexArray::Sort +// +// Note that data will be shuffled around, so this could invalidate any +// pointers that relies on the array/data +// +template +void kexArray::Sort(compare_t *function) +{ + if(data == NULL) + { + return; + } + + typedef int compareCast(const void*, const void*); + compareCast *cmpFunc = (compareCast*)function; + + qsort((void*)data, length, sizeof(type), cmpFunc); +} + +// +// kexArray::Sort +// +// Note that data will be shuffled around, so this could invalidate any +// pointers that relies on the array/data +// +template +void kexArray::Sort(compare_t *function, unsigned int count) +{ + if(data == NULL) + { + return; + } + + typedef int compareCast(const void*, const void*); + compareCast *cmpFunc = (compareCast*)function; + + qsort((void*)data, count, sizeof(type), cmpFunc); +} + +// +// kexArray::operator[] +// +template +type &kexArray::operator[](unsigned int index) +{ + assert(index < length); + return data[index]; +} + +// +// kexArray::operator= +// +template +kexArray &kexArray::operator=(const kexArray &arr) +{ + if(data) + { + delete[] data; + } + + data = NULL; + length = arr.length; + aidx = arr.aidx; + + if(arr.length > 0) + { + data = new type[arr.length]; + + for(unsigned int i = 0; i < arr.length; i++) + { + data[i] = arr.data[i]; + } + } + + return *this; +} + +#endif diff --git a/src/lightmap/kexlib/binfile.cpp b/src/lightmap/kexlib/binfile.cpp new file mode 100644 index 0000000..a56cb44 --- /dev/null +++ b/src/lightmap/kexlib/binfile.cpp @@ -0,0 +1,399 @@ +//----------------------------------------------------------------------------- +// 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: Binary file operations +// +//----------------------------------------------------------------------------- + +#include "lightmap/common.h" +#include "lightmap/kexlib/binFile.h" + +// +// kexBinFile::kexBinFile +// + +kexBinFile::kexBinFile(void) +{ + this->handle = NULL; + this->buffer = NULL; + this->bufferOffset = 0; + this->bOpened = false; +} + +// +// kexBinFile::~kexBinFile +// + +kexBinFile::~kexBinFile(void) +{ + Close(); +} + +// +// kexBinFile::Open +// + +bool kexBinFile::Open(const char *file, kexHeapBlock &heapBlock) +{ + if((handle = fopen(file, "rb"))) + { + size_t length; + + fseek(handle, 0, SEEK_END); + length = ftell(handle); + fseek(handle, 0, SEEK_SET); + + buffer = (byte*)Mem_Calloc(length+1, heapBlock); + + if(fread(buffer, 1, length, handle) == length) + { + if(length > 0) + { + bOpened = true; + bufferOffset = 0; + return true; + } + } + + Mem_Free(buffer); + buffer = NULL; + fclose(handle); + } + + return false; +} + +// +// kexBinFile::Create +// + +bool kexBinFile::Create(const char *file) +{ + if((handle = fopen(file, "wb"))) + { + bOpened = true; + bufferOffset = 0; + return true; + } + + return false; +} + +// +// kexBinFile::Close +// + +void kexBinFile::Close(void) +{ + if(bOpened == false) + { + return; + } + if(handle) + { + fclose(handle); + handle = NULL; + if(buffer) + { + Mem_Free(buffer); + } + } + + bOpened = false; +} + +// +// kexBinFile::Exists +// + +bool kexBinFile::Exists(const char *file) +{ + FILE *fstream; + + fstream = fopen(file, "r"); + + if(fstream != NULL) + { + fclose(fstream); + return true; + } +#ifdef KEX_WIN32 + else + { + // If we can't open because the file is a directory, the + // "file" exists at least! + if(errno == 21) + { + return true; + } + } +#endif + + return false; +} + +// +// kexBinFile::Duplicate +// + +void kexBinFile::Duplicate(const char *newFileName) +{ + FILE *f; + + if(bOpened == false) + { + return; + } + + f = fopen(newFileName, "wb"); + + if(f == NULL) + { + return; + } + + fwrite(buffer, Length(), 1, f); + fclose(f); +} + +// +// kexBinFile::Length +// + +int kexBinFile::Length(void) +{ + long savedpos; + long length; + + if(bOpened == false) + { + return 0; + } + + // save the current position in the file + savedpos = ftell(handle); + + // jump to the end and find the length + fseek(handle, 0, SEEK_END); + length = ftell(handle); + + // go back to the old location + fseek(handle, savedpos, SEEK_SET); + + return length; +} + +// +// kexBinFile::Read8 +// + +byte kexBinFile::Read8(void) +{ + byte result; + result = buffer[bufferOffset++]; + return result; +} + +// +// kexBinFile::Read16 +// + +short kexBinFile::Read16(void) +{ + int result; + result = Read8(); + result |= Read8() << 8; + return result; +} + +// +// kexBinFile::Read32 +// + +int kexBinFile::Read32(void) +{ + int result; + result = Read8(); + result |= Read8() << 8; + result |= Read8() << 16; + result |= Read8() << 24; + return result; +} + +// +// kexBinFile::ReadFloat +// + +float kexBinFile::ReadFloat(void) +{ + fint_t fi; + fi.i = Read32(); + return fi.f; +} + +// +// kexBinFile::ReadVector +// + +kexVec3 kexBinFile::ReadVector(void) +{ + kexVec3 vec; + + vec.x = ReadFloat(); + vec.y = ReadFloat(); + vec.z = ReadFloat(); + + return vec; +} + +// +// kexBinFile::ReadString +// + +kexStr kexBinFile::ReadString(void) +{ + kexStr str; + char c = 0; + + while(1) + { + if(!(c = Read8())) + { + break; + } + + str += c; + } + + return str; +} + +// +// kexBinFile::Write8 +// + +void kexBinFile::Write8(const byte val) +{ + if(bOpened) + { + fwrite(&val, 1, 1, handle); + } + else + { + buffer[bufferOffset] = val; + } + bufferOffset++; +} + +// +// kexBinFile::Write16 +// + +void kexBinFile::Write16(const short val) +{ + Write8(val & 0xff); + Write8((val >> 8) & 0xff); +} + +// +// kexBinFile::Write32 +// + +void kexBinFile::Write32(const int val) +{ + Write8(val & 0xff); + Write8((val >> 8) & 0xff); + Write8((val >> 16) & 0xff); + Write8((val >> 24) & 0xff); +} + +// +// kexBinFile::WriteFloat +// + +void kexBinFile::WriteFloat(const float val) +{ + fint_t fi; + fi.f = val; + Write32(fi.i); +} + +// +// kexBinFile::WriteVector +// + +void kexBinFile::WriteVector(const kexVec3 &val) +{ + WriteFloat(val.x); + WriteFloat(val.y); + WriteFloat(val.z); +} + +// +// kexBinFile::WriteString +// + +void kexBinFile::WriteString(const kexStr &val) +{ + const char *c = val.c_str(); + + for(int i = 0; i < val.Length(); i++) + { + Write8(c[i]); + } + + Write8(0); +} + +// +// kexBinFile::GetOffsetValue +// + +int kexBinFile::GetOffsetValue(int id) +{ + return *(int*)(buffer + (id << 2)); +} + +// +// kexBinFile::GetOffset +// + +byte *kexBinFile::GetOffset(int id, byte *subdata, int *count) +{ + byte *data = (subdata == NULL) ? buffer : subdata; + + bufferOffset = GetOffsetValue(id); + byte *dataOffs = &data[bufferOffset]; + + if(count) + { + *count = *(int*)(dataOffs); + } + + return dataOffs; +} diff --git a/src/lightmap/kexlib/binfile.h b/src/lightmap/kexlib/binfile.h new file mode 100644 index 0000000..6858b91 --- /dev/null +++ b/src/lightmap/kexlib/binfile.h @@ -0,0 +1,79 @@ +//----------------------------------------------------------------------------- +// 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. +// + +#ifndef __BINFILE_H__ +#define __BINFILE_H__ + +#include "lightmap/kexlib/math/mathlib.h" + +class kexBinFile +{ +public: + kexBinFile(void); + ~kexBinFile(void); + + bool Open(const char *file, kexHeapBlock &heapBlock = hb_static); + bool Create(const char *file); + void Close(void); + bool Exists(const char *file); + int Length(void); + void Duplicate(const char *newFileName); + + byte Read8(void); + short Read16(void); + int Read32(void); + float ReadFloat(void); + kexVec3 ReadVector(void); + kexStr ReadString(void); + + void Write8(const byte val); + void Write16(const short val); + void Write32(const int val); + void WriteFloat(const float val); + void WriteVector(const kexVec3 &val); + void WriteString(const kexStr &val); + + int GetOffsetValue(int id); + byte *GetOffset(int id, + byte *subdata = NULL, + int *count = NULL); + + FILE *Handle(void) const { return handle; } + byte *Buffer(void) const { return buffer; } + void SetBuffer(byte *ptr) { buffer = ptr; } + byte *BufferAt(void) const { return &buffer[bufferOffset]; } + bool Opened(void) const { return bOpened; } + void SetOffset(const int offset) { bufferOffset = offset; } + +private: + FILE *handle; + byte *buffer; + unsigned int bufferOffset; + bool bOpened; +}; + +#endif diff --git a/src/lightmap/kexlib/kstring.cpp b/src/lightmap/kexlib/kstring.cpp new file mode 100644 index 0000000..b3cb08e --- /dev/null +++ b/src/lightmap/kexlib/kstring.cpp @@ -0,0 +1,804 @@ +//----------------------------------------------------------------------------- +// 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: String Class +// +//----------------------------------------------------------------------------- + +#include +#include "kstring.h" + +// +// kexStr::Init +// + +void kexStr::Init(void) +{ + length = 0; + bufferLength = STRING_DEFAULT_SIZE; + charPtr = defaultBuffer; + charPtr[0] = '\0'; +} + +// +// kexStr::CheckSize +// + +void kexStr::CheckSize(int size, bool bKeepString) +{ + if(size <= bufferLength) + { + return; + } + + Resize(size, bKeepString); +} + +// +// kexStr::CopyNew +// + +void kexStr::CopyNew(const char *string, int len) +{ + CheckSize(len+1, false); + strcpy(charPtr, string); + length = len; +} + +// +// kexStr::kexStr +// + +kexStr::kexStr(void) +{ + Init(); +} + +// +// kexStr::kexStr +// + +kexStr::kexStr(const char *string) +{ + Init(); + + if(string == NULL) + { + return; + } + + CopyNew(string, strlen(string)); +} + +// +// kexStr::kexStr +// + +kexStr::kexStr(const char *string, const int length) +{ + Init(); + + if(string == NULL) + { + return; + } + + CopyNew(string, length); +} + +// +// kexStr::kexStr +// + +kexStr::kexStr(const kexStr &string) +{ + Init(); + + if(string.charPtr == NULL) + { + return; + } + + CopyNew(string.charPtr, string.Length()); +} + +// +// kexStr::~kexStr +// + +kexStr::~kexStr(void) +{ + if(charPtr != defaultBuffer) + { + delete[] charPtr; + charPtr = defaultBuffer; + } + + charPtr[0] = '\0'; + length = 0; +} + +// +// kexStr::Concat +// + +kexStr &kexStr::Concat(const char *string) +{ + return Concat(string, strlen(string)); +} + +// +// kexStr::Concat +// + +kexStr &kexStr::Concat(const char c) +{ + CheckSize((length + 1)+1, true); + charPtr[length++] = c; + charPtr[length] = '\0'; + return *this; +} + +// +// kexStr::Concat +// + +kexStr &kexStr::Concat(const char *string, int len) +{ + CheckSize((length + len)+1, true); + + for(int i = 0; i < len; i++) + { + charPtr[length+i] = string[i]; + } + + length += len; + charPtr[length] = '\0'; + + return *this; +} + +// +// kexStr::Copy +// + +kexStr &kexStr::Copy(const kexStr &src, int len) +{ + int i = 0; + const char *p = src; + CheckSize((length + len)+1, true); + + while((len--) >= 0) + { + charPtr[i] = p[i]; + i++; + } + + return *this; +} + +// +// kexStr::Copy +// + +kexStr &kexStr::Copy(const kexStr &src) +{ + return Copy(src, src.Length()); +} + +// +// kexStr::operator= +// + +kexStr &kexStr::operator=(const kexStr &str) +{ + int len = str.Length(); + + CheckSize(len+1, false); + strncpy(charPtr, str.charPtr, len); + length = len; + charPtr[length] = '\0'; + + return *this; +} + +// +// kexStr::operator= +// + +kexStr &kexStr::operator=(const char *str) +{ + int len = strlen(str); + + CheckSize(len+1, false); + strncpy(charPtr, str, len); + length = len; + charPtr[length] = '\0'; + + return *this; +} + +// +// kexStr::operator= +// + +kexStr &kexStr::operator=(const bool b) +{ + const char *str = b ? "true" : "false"; + int len = strlen(str); + + CheckSize(len+1, false); + strncpy(charPtr, str, len); + length = len; + charPtr[length] = '\0'; + + return *this; +} + +// +// kexStr::operator+ +// + +kexStr kexStr::operator+(const kexStr &str) +{ + kexStr out(*this); + + return out.Concat(str.c_str()); +} + +// +// kexStr::operator+ +// + +kexStr kexStr::operator+(const char *str) +{ + kexStr out(*this); + + return out.Concat(str); +} + +// +// kexStr::operator+ +// + +kexStr kexStr::operator+(const bool b) +{ + kexStr out(*this); + + return out.Concat(b ? "true" : "false"); +} + +// +// kexStr::operator+ +// + +kexStr kexStr::operator+(const int i) +{ + kexStr out(*this); + + char tmp[64]; + sprintf(tmp, "%i", i); + + return out.Concat(tmp); +} + +// +// kexStr::operator+ +// + +kexStr kexStr::operator+(const float f) +{ + kexStr out(*this); + + char tmp[64]; + sprintf(tmp, "%f", f); + + return out.Concat(tmp); +} + +// +// kexStr::operator+= +// + +kexStr &kexStr::operator+=(const kexStr &str) +{ + return Concat(str.c_str()); +} + +// +// kexStr::operator+= +// + +kexStr &kexStr::operator+=(const char *str) +{ + return Concat(str); +} + +// +// kexStr::operator+= +// + +kexStr &kexStr::operator+=(const char c) +{ + return Concat(c); +} + +// +// kexStr::operator+= +// + +kexStr &kexStr::operator+=(const bool b) +{ + return Concat(b ? "true" : "false"); +} + +// +// kexStr::operator[] +// + +const char kexStr::operator[](int index) const +{ + assert(index >= 0 && index < length); + return charPtr[index]; +} + +// +// kexStr::Resize +// + +void kexStr::Resize(int size, bool bKeepString) +{ + + if(size <= 0) + { + return; + } + + int newsize = size + ((32 - (size & 31)) & 31); + char *newbuffer = new char[newsize]; + + if(bKeepString) + { + strncpy(newbuffer, charPtr, length); + } + + if(charPtr != defaultBuffer) + { + delete[] charPtr; + } + + charPtr = newbuffer; + bufferLength = newsize; +} + +// +// kexStr::IndexOf +// + +int kexStr::IndexOf(const char *pattern) const +{ + int patlen = strlen(pattern); + int i = 0; + int j = 0; + + while(i + j < Length()) + { + if(charPtr[i + j] == pattern[j]) + { + if(++j == patlen) + { + return i; + } + } + else + { + i++; + j = 0; + } + } + + return -1; +} + +// +// kexStr::IndexOf +// + +int kexStr::IndexOf(const char *string, const char *pattern) +{ + int patlen = strlen(pattern); + int i = 0; + int j = 0; + + while(i + j < (int)strlen(string)) + { + if(string[i + j] == pattern[j]) + { + if(++j == patlen) + { + return i; + } + } + else + { + i++; + j = 0; + } + } + + return -1; +} + +// +// kexStr::IndexOf +// + +int kexStr::IndexOf(const kexStr &pattern) const +{ + return IndexOf(pattern.c_str()); +} + +// +// kexStr::NormalizeSlashes +// + +kexStr &kexStr::NormalizeSlashes(void) +{ + for(int i = 0; i < length; i++) + { + if((charPtr[i] == '/' || charPtr[i] == '\\') && charPtr[i] != DIR_SEPARATOR) + { + charPtr[i] = DIR_SEPARATOR; + } + } + + return *this; +} + +// +// kexStr::StripPath +// + +kexStr &kexStr::StripPath(void) +{ + int pos = 0; + int i = 0; + + pos = length; + + for(i = length - 1; charPtr[i] != '\\' && charPtr[i] != '/'; i--, pos--) + { + if(pos <= 0) + { + return *this; + } + } + length = length - pos; + for(i = 0; i < length; i++) + { + charPtr[i] = charPtr[pos+i]; + } + + CheckSize(length, true); + charPtr[length] = '\0'; + return *this; +} + +// +// kexStr::StripExtension +// + +kexStr &kexStr::StripExtension(void) +{ + int pos = IndexOf("."); + + if(pos == -1) + { + return *this; + } + + length = pos; + CheckSize(length, true); + charPtr[length] = '\0'; + + return *this; +} + +// +// kexStr::StripFile +// + +kexStr &kexStr::StripFile(void) +{ + int pos = 0; + int i = 0; + + if(IndexOf(".") == -1) + { + return *this; + } + + pos = length; + + for(i = length - 1; charPtr[i] != '\\' && charPtr[i] != '/'; i--, pos--) + { + if(pos <= 0) + { + return *this; + } + } + + length = pos; + CheckSize(length, true); + charPtr[length] = '\0'; + + return *this; +} + +// +// kexStr::Hash +// + +int kexStr::Hash(void) +{ + unsigned int hash = 0; + char *str = (char*)charPtr; + char c; + + while((c = *str++)) + { + hash = c + (hash << 6) + (hash << 16) - hash; + } + + return hash & (MAX_HASH-1); +} + +// +// kexStr::Hash +// + +int kexStr::Hash(const char *s) +{ + unsigned int hash = 0; + char *str = (char*)s; + char c; + + while((c = *str++)) + { + hash = c + (hash << 6) + (hash << 16) - hash; + } + + return hash & (MAX_HASH-1); +} + +// +// kexStr::Format +// + +char *kexStr::Format(const char *str, ...) +{ + va_list v; + static char vastr[1024]; + + va_start(v, str); + vsprintf(vastr, str,v); + va_end(v); + + return vastr; +} + +// +// kexStr::Substr +// + +kexStr kexStr::Substr(int start, int len) const +{ + kexStr str; + int l = Length(); + + if(l <= 0 || start >= l) + { + return str; + } + + if(start + len >= l) + { + len = l - start; + } + + return str.Concat((const char*)&charPtr[start], len); +} + +// +// kexStr::Split +// + +void kexStr::Split(kexStrList &list, const char seperator) +{ + int splitLen = 0; + int startOffs = 0; + for(int i = 0; i < length; i++) + { + if(charPtr[i] == seperator) + { + if(splitLen == 0) + { + continue; + } + + list.Push(kexStr(&charPtr[startOffs], splitLen)); + startOffs += (splitLen+1); + splitLen = 0; + continue; + } + + splitLen++; + } + + if(splitLen != 0 && startOffs != 0) + { + list.Push(kexStr(&charPtr[startOffs], splitLen)); + } +} + +// +// kexStr::Atoi +// + +int kexStr::Atoi(void) +{ + return atoi(charPtr); +} + +// +// kexStr::Atof +// + +float kexStr::Atof(void) +{ + return (float)atof(charPtr); +} + +// +// kexStr::WriteToFile +// + +void kexStr::WriteToFile(const char *file) +{ + if(length <= 0) + { + return; + } + + FILE *f = fopen(file, "w"); + fprintf(f, "%s", charPtr); + fclose(f); +} + +// +// kexStr::ToUpper +// + +kexStr &kexStr::ToUpper(void) +{ + char c; + for(int i = 0; i < length; i++) + { + c = charPtr[i]; + if(c >= 'a' && c <= 'z') + { + c -= 'a'-'A'; + } + charPtr[i] = c; + } + + return *this; +} + +// +// kexStr::ToLower +// + +kexStr &kexStr::ToLower(void) +{ + char c; + for(int i = 0; i < length; i++) + { + c = charPtr[i]; + if(c >= 'A' && c <= 'Z') + { + c += 32; + } + charPtr[i] = c; + } + + return *this; +} + +// +// kexStr::CompareCase +// + +bool kexStr::CompareCase(const char *s1, const char *s2) +{ + while(*s1 && *s2) + { + if(*s1 != *s2) + { + return (*s2 - *s1) != 0; + } + s1++; + s2++; + } + if(*s1 != *s2) + { + return (*s2 - *s1) != 0; + } + + return false; +} + +// +// kexStr::CompareCase +// + +bool kexStr::CompareCase(const kexStr &a, const kexStr &b) +{ + return CompareCase(a.c_str(), b.c_str()); +} + +// +// kexStr::Compare +// + +bool kexStr::Compare(const char *s1, const char *s2) +{ + const byte *us1 = (const byte*)s1; + const byte *us2 = (const byte*)s2; + + while(tolower(*us1) == tolower(*us2)) + { + if(*us1++ == '\0') + { + return false; + } + + us2++; + } + + return (tolower(*us1) - tolower(*us2)) != 0; +} + +// +// kexStr::Compare +// + +bool kexStr::Compare(const kexStr &a, const kexStr &b) +{ + return Compare(a.c_str(), b.c_str()); +} diff --git a/src/lightmap/kexlib/kstring.h b/src/lightmap/kexlib/kstring.h new file mode 100644 index 0000000..e9944fd --- /dev/null +++ b/src/lightmap/kexlib/kstring.h @@ -0,0 +1,131 @@ +//----------------------------------------------------------------------------- +// 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. +// + +#ifndef __KSTRING_H__ +#define __KSTRING_H__ + +#include +#include "lightmap/common.h" +#include "array.h" + +class kexStr; + +typedef kexArray kexStrList; + +class kexStr +{ +public: + kexStr(void); + kexStr(const char *string); + kexStr(const char *string, const int length); + kexStr(const kexStr &string); + ~kexStr(void); + + void CheckSize(int size, bool bKeepString); + int IndexOf(const char *pattern) const; + int IndexOf(const kexStr &pattern) const; + kexStr &Concat(const char *string); + kexStr &Concat(const char *string, int len); + kexStr &Concat(const char c); + kexStr &NormalizeSlashes(void); + kexStr &StripPath(void); + kexStr &StripExtension(void); + kexStr &StripFile(void); + kexStr &Copy(const kexStr &src, int len); + kexStr &Copy(const kexStr &src); + kexStr &ToUpper(void); + kexStr &ToLower(void); + int Hash(void); + kexStr Substr(int start, int len) const; + void Split(kexStrList &list, const char seperator); + int Atoi(void); + float Atof(void); + void WriteToFile(const char *file); + + int Length(void) const { return length; } + const char *c_str(void) const { return charPtr; } + + kexStr &operator=(const kexStr &str); + kexStr &operator=(const char *str); + kexStr &operator=(const bool b); + kexStr operator+(const kexStr &str); + kexStr operator+(const char *str); + kexStr operator+(const bool b); + kexStr operator+(const int i); + kexStr operator+(const float f); + kexStr &operator+=(const kexStr &str); + kexStr &operator+=(const char *str); + kexStr &operator+=(const char c); + kexStr &operator+=(const bool b); + const char operator[](int index) const; + + friend bool operator==(const kexStr &a, const kexStr &b); + friend bool operator==(const char *a, const kexStr &b); + friend bool operator==(const kexStr &a, const char *b); + + operator const char *(void) const { return c_str(); } + operator const char *(void) { return c_str(); } + + static bool CompareCase(const char *s1, const char *s2); + static bool CompareCase(const kexStr &a, const kexStr &b); + static bool Compare(const char *s1, const char *s2); + static bool Compare(const kexStr &a, const kexStr &b); + static int IndexOf(const char *string, const char *pattern); + static int Hash(const char *s); + static char *Format(const char *str, ...); + +private: + void Resize(int size, bool bKeepString); + void CopyNew(const char *string, int len); + +protected: + void Init(void); + + static const int STRING_DEFAULT_SIZE = 32; + + char *charPtr; + char defaultBuffer[STRING_DEFAULT_SIZE]; + int length; + int bufferLength; +}; + +d_inline bool operator==(const kexStr &a, const kexStr &b) +{ + return (!strcmp(a.charPtr, b.charPtr)); +} + +d_inline bool operator==(const char *a, const kexStr &b) +{ + return (!strcmp(a, b.charPtr)); +} + +d_inline bool operator==(const kexStr &a, const char *b) +{ + return (!strcmp(a.charPtr, b)); +} + +#endif diff --git a/src/lightmap/kexlib/math/angle.cpp b/src/lightmap/kexlib/math/angle.cpp new file mode 100644 index 0000000..188e4d2 --- /dev/null +++ b/src/lightmap/kexlib/math/angle.cpp @@ -0,0 +1,405 @@ +//----------------------------------------------------------------------------- +// 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: Angle operations +// +//----------------------------------------------------------------------------- + +#include +#include "mathlib.h" + +#define FULLCIRCLE (M_PI * 2) + +// +// kexAngle::kexAngle +// + +kexAngle::kexAngle(void) +{ + this->yaw = 0; + this->pitch = 0; + this->roll = 0; +} + +// +// kexAngle::kexAngle +// + +kexAngle::kexAngle(const float yaw, const float pitch, const float roll) +{ + this->yaw = yaw; + this->pitch = pitch; + this->roll = roll; +} + +// +// kexAngle::kexAngle +// + +kexAngle::kexAngle(const kexVec3 &vector) +{ + this->yaw = vector.x; + this->pitch = vector.y; + this->roll = vector.z; + + Clamp180(); +} + +// +// kexAngle::kexAngle +// + +kexAngle::kexAngle(const kexAngle &an) +{ + this->yaw = an.yaw; + this->pitch = an.pitch; + this->roll = an.roll; +} + +// +// kexAngle::Clamp180 +// + +kexAngle &kexAngle::Clamp180(void) +{ +#define CLAMP180(x) \ + if(x < -M_PI) for(; x < -M_PI; x = x + FULLCIRCLE); \ + if(x > M_PI) for(; x > M_PI; x = x - FULLCIRCLE) + CLAMP180(yaw); + CLAMP180(pitch); + CLAMP180(roll); +#undef CLAMP180 + + return *this; +} + +// +// kexAngle::Clamp180Invert +// + +kexAngle &kexAngle::Clamp180Invert(void) +{ +#define CLAMP180(x) \ + for(; x < -M_PI; x = x + FULLCIRCLE); \ + for(; x > M_PI; x = x - FULLCIRCLE) + CLAMP180(yaw); + CLAMP180(pitch); + CLAMP180(roll); +#undef CLAMP180 + + yaw = -yaw; + pitch = -pitch; + roll = -roll; + + return *this; +} + +// +// kexAngle::Clamp180InvertSum +// + +kexAngle &kexAngle::Clamp180InvertSum(const kexAngle &angle) +{ + kexAngle an = angle; + + an.Clamp180Invert(); + + an.yaw += this->yaw; + an.pitch += this->pitch; + an.roll += this->roll; + + an.Clamp180Invert(); + + this->yaw = an.yaw; + this->pitch = an.pitch; + this->roll = an.roll; + + return *this; +} + +// +// kexAngle::Round +// + +kexAngle &kexAngle::Round(void) +{ +#define ROUND(x) \ + x = DEG2RAD((360.0f / 65536.0f) * \ + ((int)(RAD2DEG(x) * (65536.0f / 360.0f)) & 65535)) + yaw = ROUND(yaw); + pitch = ROUND(pitch); + roll = ROUND(roll); +#undef ROUND + + return Clamp180(); +} + +// +// kexAngle::Diff +// + +kexAngle kexAngle::Diff(kexAngle &angle) +{ + float an; + kexAngle out; + + Clamp180(); + angle.Clamp180(); + +#define DIFF(x) \ + if(x <= angle.x) { \ + an = angle.x + FULLCIRCLE; \ + if(x - angle.x > an - x) { \ + out.x = x - an; \ + } \ + else { \ + out.x = x - angle.x; \ + } \ + } \ + else { \ + an = angle.x - FULLCIRCLE; \ + if(angle.x - x <= x - an) { \ + out.x = x - angle.x; \ + } \ + else { \ + out.x = x - an; \ + } \ + } + DIFF(yaw); + DIFF(pitch); + DIFF(roll); +#undef DIFF + + return out; +} + +// +// kexAngle::ToAxis +// + +void kexAngle::ToAxis(kexVec3 *forward, kexVec3 *up, kexVec3 *right) +{ + float sy = kexMath::Sin(yaw); + float cy = kexMath::Cos(yaw); + float sp = kexMath::Sin(pitch); + float cp = kexMath::Cos(pitch); + float sr = kexMath::Sin(roll); + float cr = kexMath::Cos(roll); + + if(forward) + { + forward->x = sy * cp; + forward->y = sp; + forward->z = cy * cp; + } + if(right) + { + right->x = sr * sp * sy + cr * cy; + right->y = sr * cp; + right->z = sr * sp * cy + cr * -sy; + } + if(up) + { + up->x = cr * sp * sy + -sr * cy; + up->y = cr * cp; + up->z = cr * sp * cy + -sr * -sy; + } +} + +// +// kexAngle::ToForwardAxis +// + +kexVec3 kexAngle::ToForwardAxis(void) +{ + kexVec3 vec; + + ToAxis(&vec, NULL, NULL); + return vec; +} + +// +// kexAngle::ToUpAxis +// + +kexVec3 kexAngle::ToUpAxis(void) +{ + kexVec3 vec; + + ToAxis(NULL, &vec, NULL); + return vec; +} + +// +// kexAngle::ToRightAxis +// + +kexVec3 kexAngle::ToRightAxis(void) +{ + kexVec3 vec; + + ToAxis(NULL, NULL, &vec); + return vec; +} + +// +// kexAngle::ToVec3 +// + +const kexVec3 &kexAngle::ToVec3(void) const +{ + return *reinterpret_cast(&yaw); +} + +// +// kexAngle::ToVec3 +// + +kexVec3 &kexAngle::ToVec3(void) +{ + return *reinterpret_cast(&yaw); +} + +// +// kexAngle::ToQuat +// + +kexQuat kexAngle::ToQuat(void) +{ + return + (kexQuat(pitch, kexVec3::vecRight) * + (kexQuat(yaw, kexVec3::vecUp) * + kexQuat(roll, kexVec3::vecForward))); +} + +// +// kexAngle::operator+ +// + +kexAngle kexAngle::operator+(const kexAngle &angle) +{ + return kexAngle(yaw + angle.yaw, pitch + angle.pitch, roll + angle.roll); +} + +// +// kexAngle::operator- +// + +kexAngle kexAngle::operator-(const kexAngle &angle) +{ + return kexAngle(yaw - angle.yaw, pitch - angle.pitch, roll - angle.roll); +} + +// +// kexAngle::operator- +// + +kexAngle kexAngle::operator-(void) +{ + return kexAngle(-yaw, -pitch, -roll); +} + +// +// kexAngle::operator+= +// + +kexAngle &kexAngle::operator+=(const kexAngle &angle) +{ + yaw += angle.yaw; + pitch += angle.pitch; + roll += angle.roll; + return *this; +} + +// +// kexAngle::operator-= +// + +kexAngle &kexAngle::operator-=(const kexAngle &angle) +{ + yaw -= angle.yaw; + pitch -= angle.pitch; + roll -= angle.roll; + return *this; +} + +// +// kexAngle::operator= +// + +kexAngle &kexAngle::operator=(const kexAngle &angle) +{ + yaw = angle.yaw; + pitch = angle.pitch; + roll = angle.roll; + return *this; +} + +// +// kexAngle::operator= +// + +kexAngle &kexAngle::operator=(const kexVec3 &vector) +{ + yaw = vector.x; + pitch = vector.y; + roll = vector.z; + return *this; +} + +// +// kexAngle::operator= +// + +kexAngle &kexAngle::operator=(const float *vecs) +{ + yaw = vecs[0]; + pitch = vecs[1]; + roll = vecs[2]; + return *this; +} + +// +// kexAngle::operator[] +// + +float kexAngle::operator[](int index) const +{ + assert(index >= 0 && index < 3); + return (&yaw)[index]; +} + +// +// kexAngle::operator[] +// + +float &kexAngle::operator[](int index) +{ + assert(index >= 0 && index < 3); + return (&yaw)[index]; +} diff --git a/src/lightmap/kexlib/math/bounds.cpp b/src/lightmap/kexlib/math/bounds.cpp new file mode 100644 index 0000000..f322571 --- /dev/null +++ b/src/lightmap/kexlib/math/bounds.cpp @@ -0,0 +1,520 @@ +//----------------------------------------------------------------------------- +// 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: Bounding box operations +// +//----------------------------------------------------------------------------- + +#include +#include "mathlib.h" + +// +// kexBBox::kexBBox +// + +kexBBox::kexBBox(void) +{ + Clear(); +} + +// +// kexBBox::kexBBox +// + +kexBBox::kexBBox(const kexVec3 &vMin, const kexVec3 &vMax) +{ + this->min = vMin; + this->max = vMax; +} + +// +// kexBBox::Clear +// + +void kexBBox::Clear(void) +{ + min.Set(M_INFINITY, M_INFINITY, M_INFINITY); + max.Set(-M_INFINITY, -M_INFINITY, -M_INFINITY); +} + +// +// kexBBox::AddPoint +// + +void kexBBox::AddPoint(const kexVec3 &vec) +{ + float lowx = min.x; + float lowy = min.y; + float lowz = min.z; + float hix = max.x; + float hiy = max.y; + float hiz = max.z; + + if(vec.x < lowx) { lowx = vec.x; } + if(vec.y < lowy) { lowy = vec.y; } + if(vec.z < lowz) { lowz = vec.z; } + if(vec.x > hix) { hix = vec.x; } + if(vec.y > hiy) { hiy = vec.y; } + if(vec.z > hiz) { hiz = vec.z; } + + min.Set(lowx, lowy, lowz); + max.Set(hix, hiy, hiz); +} + +// +// kexBBox::Center +// + +kexVec3 kexBBox::Center(void) const +{ + return kexVec3( + (max.x + min.x) * 0.5f, + (max.y + min.y) * 0.5f, + (max.z + min.z) * 0.5f); +} + +// +// kexBBox::Radius +// + +float kexBBox::Radius(void) const +{ + int i; + float r = 0; + float r1; + float r2; + + for(i = 0; i < 3; i++) + { + r1 = kexMath::Fabs(min[i]); + r2 = kexMath::Fabs(max[i]); + + if(r1 > r2) + { + r += r1 * r1; + } + else + { + r += r2 * r2; + } + } + + return kexMath::Sqrt(r); +} + +// +// kexBBox::PointInside +// + +bool kexBBox::PointInside(const kexVec3 &vec) const +{ + return !(vec[0] < min[0] || vec[1] < min[1] || vec[2] < min[2] || + vec[0] > max[0] || vec[1] > max[1] || vec[2] > max[2]); +} + +// +// kexBBox::IntersectingBox +// + +bool kexBBox::IntersectingBox(const kexBBox &box) const +{ + return !(box.max[0] < min[0] || box.max[1] < min[1] || box.max[2] < min[2] || + box.min[0] > max[0] || box.min[1] > max[1] || box.min[2] > max[2]); +} + +// +// kexBBox::IntersectingBox2D +// + +bool kexBBox::IntersectingBox2D(const kexBBox &box) const +{ + return !(box.max[0] < min[0] || box.max[2] < min[2] || + box.min[0] > max[0] || box.min[2] > max[2]); +} + +// +// kexBBox::DistanceToPlane +// + +float kexBBox::DistanceToPlane(kexPlane &plane) +{ + kexVec3 c; + float distStart; + float distEnd; + float dist = 0; + + c = Center(); + + distStart = plane.Distance(c); + distEnd = kexMath::Fabs((max.x - c.x) * plane.a) + + kexMath::Fabs((max.y - c.y) * plane.b) + + kexMath::Fabs((max.z - c.z) * plane.c); + + dist = distStart - distEnd; + + if(dist > 0) + { + // in front + return dist; + } + + dist = distStart + distEnd; + + if(dist < 0) + { + // behind + return dist; + } + + return 0; +} + +// +// kexBBox::operator+ +// + +kexBBox kexBBox::operator+(const float radius) const +{ + kexVec3 vmin = min; + kexVec3 vmax = max; + + vmin.x -= radius; + vmin.y -= radius; + vmin.z -= radius; + + vmax.x += radius; + vmax.y += radius; + vmax.z += radius; + + return kexBBox(vmin, vmax); +} + +// +// kexBBox::operator+= +// + +kexBBox &kexBBox::operator+=(const float radius) +{ + min.x -= radius; + min.y -= radius; + min.z -= radius; + max.x += radius; + max.y += radius; + max.z += radius; + return *this; +} + +// +// kexBBox::operator+ +// + +kexBBox kexBBox::operator+(const kexVec3 &vec) const +{ + kexVec3 vmin = min; + kexVec3 vmax = max; + + vmin.x += vec.x; + vmin.y += vec.y; + vmin.z += vec.z; + + vmax.x += vec.x; + vmax.y += vec.y; + vmax.z += vec.z; + + return kexBBox(vmin, vmax); +} + +// +// kexBBox::operator- +// + +kexBBox kexBBox::operator-(const float radius) const +{ + kexVec3 vmin = min; + kexVec3 vmax = max; + + vmin.x += radius; + vmin.y += radius; + vmin.z += radius; + + vmax.x -= radius; + vmax.y -= radius; + vmax.z -= radius; + + return kexBBox(vmin, vmax); +} + +// +// kexBBox::operator- +// + +kexBBox kexBBox::operator-(const kexVec3 &vec) const +{ + kexVec3 vmin = min; + kexVec3 vmax = max; + + vmin.x -= vec.x; + vmin.y -= vec.y; + vmin.z -= vec.z; + + vmax.x -= vec.x; + vmax.y -= vec.y; + vmax.z -= vec.z; + + return kexBBox(vmin, vmax); +} + +// +// kexBBox::operator-= +// + +kexBBox &kexBBox::operator-=(const float radius) +{ + min.x += radius; + min.y += radius; + min.z += radius; + max.x -= radius; + max.y -= radius; + max.z -= radius; + return *this; +} + +// +// kexBBox::operator* +// + +kexBBox kexBBox::operator*(const kexMatrix &matrix) const +{ + kexVec3 c = Center(); + kexVec3 ct = c * matrix; + kexBBox box(ct, ct); + + kexMatrix mtx(matrix); + + for(int i = 0; i < 3; i++) + { + mtx.vectors[i].x = kexMath::Fabs(mtx.vectors[i].x); + mtx.vectors[i].y = kexMath::Fabs(mtx.vectors[i].y); + mtx.vectors[i].z = kexMath::Fabs(mtx.vectors[i].z); + } + + kexVec3 ht = (max - c) * mtx; + box.min -= ht; + box.max += ht; + + return box; +} + +// +// kexBBox::operator*= +// + +kexBBox &kexBBox::operator*=(const kexMatrix &matrix) +{ + kexVec3 c = Center(); + kexVec3 ct = c * matrix; + + kexMatrix mtx(matrix); + + for(int i = 0; i < 3; i++) + { + mtx.vectors[i].x = kexMath::Fabs(mtx.vectors[i].x); + mtx.vectors[i].y = kexMath::Fabs(mtx.vectors[i].y); + mtx.vectors[i].z = kexMath::Fabs(mtx.vectors[i].z); + } + + kexVec3 ht = (max - c) * mtx; + + min = (ct - ht); + max = (ct + ht); + + return *this; +} + +// +// kexBBox::operator* +// + +kexBBox kexBBox::operator*(const kexVec3 &vec) const +{ + kexBBox box = *this; + + if(vec.x < 0) { box.min.x += (vec.x-1); } + else { box.max.x += (vec.x+1); } + if(vec.y < 0) { box.min.y += (vec.y-1); } + else { box.max.y += (vec.y+1); } + if(vec.z < 0) { box.min.z += (vec.z-1); } + else { box.max.z += (vec.z+1); } + + return box; +} + +// +// kexBBox::operator*= +// + +kexBBox &kexBBox::operator*=(const kexVec3 &vec) +{ + if(vec.x < 0) { min.x += (vec.x-1); } + else { max.x += (vec.x+1); } + if(vec.y < 0) { min.y += (vec.y-1); } + else { max.y += (vec.y+1); } + if(vec.z < 0) { min.z += (vec.z-1); } + else { max.z += (vec.z+1); } + + return *this; +} + +// +// kexBBox::operator= +// + +kexBBox &kexBBox::operator=(const kexBBox &bbox) +{ + min = bbox.min; + max = bbox.max; + + return *this; +} + +// +// kexBBox::operator[] +// + +kexVec3 kexBBox::operator[](int index) const +{ + assert(index >= 0 && index < 2); + return index == 0 ? min : max; +} + +// +// kexBBox::operator[] +// + +kexVec3 &kexBBox::operator[](int index) +{ + assert(index >= 0 && index < 2); + return index == 0 ? min : max; +} + +// +// kexBBox:LineIntersect +// + +bool kexBBox::LineIntersect(const kexVec3 &start, const kexVec3 &end) +{ + float ld[3]; + kexVec3 center = Center(); + kexVec3 extents = max - center; + kexVec3 lineDir = (end - start) * 0.5f; + kexVec3 lineCenter = lineDir + start; + kexVec3 dir = lineCenter - center; + + ld[0] = kexMath::Fabs(lineDir.x); + if(kexMath::Fabs(dir.x) > extents.x + ld[0]) { return false; } + ld[1] = kexMath::Fabs(lineDir.y); + if(kexMath::Fabs(dir.y) > extents.y + ld[1]) { return false; } + ld[2] = kexMath::Fabs(lineDir.z); + if(kexMath::Fabs(dir.z) > extents.z + ld[2]) { return false; } + + kexVec3 cross = lineDir.Cross(dir); + + if(kexMath::Fabs(cross.x) > extents.y * ld[2] + extents.z * ld[1]) { return false; } + if(kexMath::Fabs(cross.y) > extents.x * ld[2] + extents.z * ld[0]) { return false; } + if(kexMath::Fabs(cross.z) > extents.x * ld[1] + extents.y * ld[0]) { return false; } + + return true; +} + +// +// kexBBox::ToPoints +// +// Assumes points is an array of 24 +// + +void kexBBox::ToPoints(float *points) const +{ + points[0 * 3 + 0] = max[0]; + points[0 * 3 + 1] = min[1]; + points[0 * 3 + 2] = min[2]; + points[1 * 3 + 0] = max[0]; + points[1 * 3 + 1] = min[1]; + points[1 * 3 + 2] = max[2]; + points[2 * 3 + 0] = min[0]; + points[2 * 3 + 1] = min[1]; + points[2 * 3 + 2] = max[2]; + points[3 * 3 + 0] = min[0]; + points[3 * 3 + 1] = min[1]; + points[3 * 3 + 2] = min[2]; + points[4 * 3 + 0] = max[0]; + points[4 * 3 + 1] = max[1]; + points[4 * 3 + 2] = min[2]; + points[5 * 3 + 0] = max[0]; + points[5 * 3 + 1] = max[1]; + points[5 * 3 + 2] = max[2]; + points[6 * 3 + 0] = min[0]; + points[6 * 3 + 1] = max[1]; + points[6 * 3 + 2] = max[2]; + points[7 * 3 + 0] = min[0]; + points[7 * 3 + 1] = max[1]; + points[7 * 3 + 2] = min[2]; +} + +// +// kexBBox::ToVectors +// +// Assumes vectors is an array of 8 +// + +void kexBBox::ToVectors(kexVec3 *vectors) const +{ + vectors[0][0] = max[0]; + vectors[0][1] = min[1]; + vectors[0][2] = min[2]; + vectors[1][0] = max[0]; + vectors[1][1] = min[1]; + vectors[1][2] = max[2]; + vectors[2][0] = min[0]; + vectors[2][1] = min[1]; + vectors[2][2] = max[2]; + vectors[3][0] = min[0]; + vectors[3][1] = min[1]; + vectors[3][2] = min[2]; + vectors[4][0] = max[0]; + vectors[4][1] = max[1]; + vectors[4][2] = min[2]; + vectors[5][0] = max[0]; + vectors[5][1] = max[1]; + vectors[5][2] = max[2]; + vectors[6][0] = min[0]; + vectors[6][1] = max[1]; + vectors[6][2] = max[2]; + vectors[7][0] = min[0]; + vectors[7][1] = max[1]; + vectors[7][2] = min[2]; +} diff --git a/src/lightmap/kexlib/math/mathlib.cpp b/src/lightmap/kexlib/math/mathlib.cpp new file mode 100644 index 0000000..cd48acd --- /dev/null +++ b/src/lightmap/kexlib/math/mathlib.cpp @@ -0,0 +1,99 @@ +//----------------------------------------------------------------------------- +// 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: Math functions +// +//----------------------------------------------------------------------------- + +#include "lightmap/common.h" +#include "mathlib.h" + +// +// kexMath::RoundPowerOfTwo +// + +int kexMath::RoundPowerOfTwo(int x) +{ + int mask = 1; + + while(mask < 0x40000000) + { + if(x == mask || (x & (mask-1)) == x) + { + return mask; + } + + mask <<= 1; + } + + return x; +} + +// +// kexMath::CubicCurve +// + +void kexMath::CubicCurve(const kexVec3 &start, const kexVec3 &end, const float time, + const kexVec3 &point, kexVec3 *vec) +{ + int i; + float xyz[3]; + + for(i = 0; i < 3; i++) + { + xyz[i] = kexMath::Pow(1-time, 2) * start[i] + + (2 * (1-time)) * time * point[i] + kexMath::Pow(time, 2) * end[i]; + } + + vec->x = xyz[0]; + vec->y = xyz[1]; + vec->z = xyz[2]; +} + +// +// kexMath::QuadraticCurve +// + +void kexMath::QuadraticCurve(const kexVec3 &start, const kexVec3 &end, const float time, + const kexVec3 &pt1, const kexVec3 &pt2, kexVec3 *vec) +{ + int i; + float xyz[3]; + + for(i = 0; i < 3; i++) + { + xyz[i] = kexMath::Pow(1-time, 3) * start[i] + (3 * kexMath::Pow(1-time, 2)) * + time * pt1[i] + (3 * (1-time)) * kexMath::Pow(time, 2) * pt2[i] + + kexMath::Pow(time, 3) * end[i]; + } + + vec->x = xyz[0]; + vec->y = xyz[1]; + vec->z = xyz[2]; +} + diff --git a/src/lightmap/kexlib/math/mathlib.h b/src/lightmap/kexlib/math/mathlib.h new file mode 100644 index 0000000..77ad4aa --- /dev/null +++ b/src/lightmap/kexlib/math/mathlib.h @@ -0,0 +1,661 @@ +//----------------------------------------------------------------------------- +// 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. +// + +#ifndef __MATHLIB_H__ +#define __MATHLIB_H__ + +#include +#include "lightmap/common.h" + +#ifdef M_PI +#undef M_PI +#endif + +#define M_PI 3.1415926535897932384626433832795f +#define M_RAD (M_PI / 180.0f) +#define M_DEG (180.0f / M_PI) +#define M_INFINITY 1e30f + +#define DEG2RAD(x) ((x) * M_RAD) +#define RAD2DEG(x) ((x) * M_DEG) + +#define FLOATSIGNBIT(f) (reinterpret_cast(f) >> 31) + +class kexVec2; +class kexVec3; +class kexVec4; +class kexMatrix; +class kexAngle; +class kexStr; + +class kexMath +{ +public: + static float Sin(float x); + static float Cos(float x); + static float Tan(float x); + static float ATan2(float x, float y); + static float ACos(float x); + static float Sqrt(float x); + static float Pow(float x, float y); + static float Log(float x); + static float Floor(float x); + static float Ceil(float x); + static float Deg2Rad(float x); + static float Rad2Deg(float x); + + static int Abs(int x); + static float Fabs(float x); + static int RoundPowerOfTwo(int x); + static float InvSqrt(float x); + static void Clamp(float &f, const float min, const float max); + static void Clamp(int &i, const int min, const int max); + static void Clamp(byte &b, const byte min, const byte max); + static void Clamp(kexVec3 &f, const float min, const float max); + + static void CubicCurve(const kexVec3 &start, const kexVec3 &end, const float time, + const kexVec3 &point, kexVec3 *vec); + static void QuadraticCurve(const kexVec3 &start, const kexVec3 &end, const float time, + const kexVec3 &pt1, const kexVec3 &pt2, kexVec3 *vec); +}; + +class kexRand +{ +public: + static void SetSeed(const int randSeed); + static int SysRand(void); + static int Int(void); + static int Max(const int max); + static float Float(void); + static float CFloat(void); + +private: + static int seed; +}; + +class kexQuat +{ +public: + kexQuat(void); + + explicit kexQuat(const float angle, const float x, const float y, const float z); + explicit kexQuat(const float angle, kexVec3 &vector); + explicit kexQuat(const float angle, const kexVec3 &vector); + + void Set(const float x, const float y, const float z, const float w); + void Clear(void); + float Dot(const kexQuat &quat) const; + float UnitSq(void) const; + float Unit(void) const; + kexQuat &Normalize(void); + kexQuat Slerp(const kexQuat &quat, float movement) const; + kexQuat RotateFrom(const kexVec3 &location, const kexVec3 &target, float maxAngle); + kexQuat Inverse(void) const; + + kexQuat operator+(const kexQuat &quat); + kexQuat &operator+=(const kexQuat &quat); + kexQuat operator-(const kexQuat &quat); + kexQuat &operator-=(const kexQuat &quat); + kexQuat operator*(const kexQuat &quat); + kexQuat operator*(const float val) const; + kexQuat &operator*=(const kexQuat &quat); + kexQuat &operator*=(const float val); + kexQuat &operator=(const kexQuat &quat); + kexQuat &operator=(const kexVec4 &vec); + kexQuat &operator=(const float *vecs); + kexVec3 operator|(const kexVec3 &vector); + + const kexVec3 &ToVec3(void) const; + kexVec3 &ToVec3(void); + + float x; + float y; + float z; + float w; +}; + +class kexVec2 +{ +public: + kexVec2(void); + explicit kexVec2(const float x, const float y); + + void Set(const float x, const float y); + void Clear(void); + float Dot(const kexVec2 &vec) const; + static float Dot(const kexVec2 &vec1, const kexVec2 &vec2); + float CrossScalar(const kexVec2 &vec) const; + kexVec2 Cross(const kexVec2 &vec) const; + kexVec2 &Cross(const kexVec2 &vec1, const kexVec2 &vec2); + float Dot(const kexVec3 &vec) const; + static float Dot(const kexVec3 &vec1, const kexVec3 &vec2); + kexVec2 Cross(const kexVec3 &vec) const; + kexVec2 &Cross(const kexVec3 &vec1, const kexVec3 &vec2); + float UnitSq(void) const; + float Unit(void) const; + float DistanceSq(const kexVec2 &vec) const; + float Distance(const kexVec2 &vec) const; + kexVec2 &Normalize(void); + kexVec2 Lerp(const kexVec2 &next, float movement) const; + kexVec2 &Lerp(const kexVec2 &next, const float movement); + kexVec2 &Lerp(const kexVec2 &start, const kexVec2 &next, float movement); + kexStr ToString(void) const; + float ToYaw(void) const; + float *ToFloatPtr(void); + kexVec3 ToVec3(void); + + kexVec2 operator+(const kexVec2 &vec); + kexVec2 operator+(const kexVec2 &vec) const; + kexVec2 operator+(kexVec2 &vec); + kexVec2 operator-(void) const; + kexVec2 operator-(const kexVec2 &vec) const; + kexVec2 operator*(const kexVec2 &vec); + kexVec2 operator*(const float val); + kexVec2 operator*(const float val) const; + kexVec2 operator/(const kexVec2 &vec); + kexVec2 operator/(const float val); + kexVec2 &operator=(kexVec3 &vec); + kexVec2 &operator=(const kexVec2 &vec); + kexVec2 &operator=(const kexVec3 &vec); + kexVec2 &operator=(const float *vecs); + kexVec2 &operator+=(const kexVec2 &vec); + kexVec2 &operator-=(const kexVec2 &vec); + kexVec2 &operator*=(const kexVec2 &vec); + kexVec2 &operator*=(const float val); + kexVec2 &operator/=(const kexVec2 &vec); + kexVec2 &operator/=(const float val); + kexVec2 operator*(const kexMatrix &mtx); + kexVec2 operator*(const kexMatrix &mtx) const; + kexVec2 &operator*=(const kexMatrix &mtx); + float operator[](int index) const; + float &operator[](int index); + bool operator==(const kexVec2 &vec); + + operator float *(void) { return reinterpret_cast(&x); } + + static kexVec2 vecZero; + static const kexVec2 vecRight; + static const kexVec2 vecUp; + + float x; + float y; +}; + +class kexVec3 +{ +public: + kexVec3(void); + explicit kexVec3(const float x, const float y, const float z); + + void Set(const float x, const float y, const float z); + void Clear(void); + float Dot(const kexVec3 &vec) const; + static float Dot(const kexVec3 &vec1, const kexVec3 &vec2); + kexVec3 Cross(const kexVec3 &vec) const; + kexVec3 &Cross(const kexVec3 &vec1, const kexVec3 &vec2); + float UnitSq(void) const; + float Unit(void) const; + float DistanceSq(const kexVec3 &vec) const; + float Distance(const kexVec3 &vec) const; + kexVec3 &Normalize(void); + kexAngle PointAt(kexVec3 &location) const; + kexVec3 Lerp(const kexVec3 &next, float movement) const; + kexVec3 &Lerp(const kexVec3 &start, const kexVec3 &next, float movement); + kexQuat ToQuat(void); + float ToYaw(void) const; + float ToPitch(void) const; + kexStr ToString(void) const; + float *ToFloatPtr(void); + kexVec2 ToVec2(void); + kexVec2 ToVec2(void) const; + kexVec3 ScreenProject(kexMatrix &proj, kexMatrix &model, + const int width, const int height, + const int wx, const int wy); + + kexVec3 operator+(const kexVec3 &vec); + kexVec3 operator+(const kexVec3 &vec) const; + kexVec3 operator+(kexVec3 &vec); + kexVec3 operator-(void) const; + kexVec3 operator-(const kexVec3 &vec) const; + kexVec3 operator*(const kexVec3 &vec); + kexVec3 operator*(const float val); + kexVec3 operator*(const float val) const; + kexVec3 operator/(const kexVec3 &vec); + kexVec3 operator/(const float val); + kexVec3 operator*(const kexQuat &quat); + kexVec3 operator*(const kexMatrix &mtx); + kexVec3 operator*(const kexMatrix &mtx) const; + kexVec3 &operator=(const kexVec3 &vec); + kexVec3 &operator=(const float *vecs); + kexVec3 &operator+=(const kexVec3 &vec); + kexVec3 &operator-=(const kexVec3 &vec); + kexVec3 &operator*=(const kexVec3 &vec); + kexVec3 &operator*=(const float val); + kexVec3 &operator/=(const kexVec3 &vec); + kexVec3 &operator/=(const float val); + kexVec3 &operator*=(const kexQuat &quat); + kexVec3 &operator*=(const kexMatrix &mtx); + float operator[](int index) const; + float &operator[](int index); + + operator float *(void) { return reinterpret_cast(&x); } + + static const kexVec3 vecForward; + static const kexVec3 vecUp; + static const kexVec3 vecRight; + + float x; + float y; + float z; +}; + +class kexVec4 +{ +public: + kexVec4(void); + explicit kexVec4(const float x, const float y, const float z, const float w); + + void Set(const float x, const float y, const float z, const float w); + void Clear(void); + float *ToFloatPtr(void); + + const kexVec3 &ToVec3(void) const; + kexVec3 &ToVec3(void); + kexVec4 operator|(const kexMatrix &mtx); + kexVec4 &operator|=(const kexMatrix &mtx); + float operator[](int index) const; + float &operator[](int index); + + float x; + float y; + float z; + float w; +}; + +class kexMatrix +{ +public: + kexMatrix(void); + kexMatrix(const kexMatrix &mtx); + kexMatrix(const float x, const float y, const float z); + kexMatrix(const kexQuat &quat); + kexMatrix(const float angle, const int axis); + + kexMatrix &Identity(void); + kexMatrix &Identity(const float x, const float y, const float z); + kexMatrix &SetTranslation(const float x, const float y, const float z); + kexMatrix &SetTranslation(const kexVec3 &vector); + kexMatrix &AddTranslation(const float x, const float y, const float z); + kexMatrix &AddTranslation(const kexVec3 &vector); + kexMatrix &Scale(const float x, const float y, const float z); + kexMatrix &Scale(const kexVec3 &vector); + static kexMatrix Scale(const kexMatrix &mtx, const float x, const float y, const float z); + kexMatrix &Transpose(void); + static kexMatrix Transpose(const kexMatrix &mtx); + static kexMatrix Invert(kexMatrix &mtx); + kexQuat ToQuat(void); + float *ToFloatPtr(void); + void SetViewProjection(float aspect, float fov, float zNear, float zFar); + void SetOrtho(float left, float right, + float bottom, float top, + float zNear, float zFar); + + kexMatrix operator*(const kexVec3 &vector); + kexMatrix &operator*=(const kexVec3 &vector); + kexMatrix operator*(const kexMatrix &matrix); + kexMatrix &operator*=(const kexMatrix &matrix); + friend kexMatrix operator*(const kexMatrix &m1, const kexMatrix &m2); + kexMatrix &operator=(const kexMatrix &matrix); + kexMatrix &operator=(const float *m); + kexMatrix operator|(const kexMatrix &matrix); + + kexVec4 vectors[4]; +}; + +class kexPluecker +{ +public: + kexPluecker(void); + kexPluecker(const kexVec3 &start, const kexVec3 &end, bool bRay = false); + + void Clear(void); + void SetLine(const kexVec3 &start, const kexVec3 &end); + void SetRay(const kexVec3 &start, const kexVec3 &dir); + float InnerProduct(const kexPluecker &pluecker) const; + + float p[6]; +}; + +class kexPlane +{ +public: + kexPlane(void); + kexPlane(const float a, const float b, const float c, const float d); + kexPlane(const kexVec3 &pt1, const kexVec3 &pt2, const kexVec3 &pt3); + kexPlane(const kexVec3 &normal, const kexVec3 &point); + kexPlane(const kexPlane &plane); + + typedef enum + { + AXIS_YZ = 0, + AXIS_XZ, + AXIS_XY + } planeAxis_t; + + const kexVec3 &Normal(void) const; + kexVec3 &Normal(void); + kexPlane &SetNormal(const kexVec3 &normal); + kexPlane &SetNormal(const kexVec3 &pt1, const kexVec3 &pt2, const kexVec3 &pt3); + float Distance(const kexVec3 &point); + kexPlane &SetDistance(const kexVec3 &point); + bool IsFacing(const float yaw); + float ToYaw(void); + float ToPitch(void); + kexQuat ToQuat(void); + const kexVec4 &ToVec4(void) const; + kexVec4 &ToVec4(void); + const planeAxis_t BestAxis(void) const; + kexVec3 GetInclination(void); + + kexPlane &operator|(const kexQuat &quat); + kexPlane &operator|=(const kexQuat &quat); + kexPlane &operator|(const kexMatrix &mtx); + kexPlane &operator|=(const kexMatrix &mtx); + + float a; + float b; + float c; + float d; +}; + +class kexAngle +{ +public: + kexAngle(void); + kexAngle(const float yaw, const float pitch, const float roll); + kexAngle(const kexVec3 &vector); + kexAngle(const kexAngle &an); + + kexAngle &Round(void); + kexAngle &Clamp180(void); + kexAngle &Clamp180Invert(void); + kexAngle &Clamp180InvertSum(const kexAngle &angle); + kexAngle Diff(kexAngle &angle); + void ToAxis(kexVec3 *forward, kexVec3 *up, kexVec3 *right); + kexVec3 ToForwardAxis(void); + kexVec3 ToUpAxis(void); + kexVec3 ToRightAxis(void); + const kexVec3 &ToVec3(void) const; + kexVec3 &ToVec3(void); + kexQuat ToQuat(void); + + kexAngle operator+(const kexAngle &angle); + kexAngle operator-(const kexAngle &angle); + kexAngle &operator+=(const kexAngle &angle); + kexAngle &operator-=(const kexAngle &angle); + kexAngle &operator=(const kexAngle &angle); + kexAngle &operator=(const kexVec3 &vector); + kexAngle &operator=(const float *vecs); + kexAngle operator-(void); + float operator[](int index) const; + float &operator[](int index); + + float yaw; + float pitch; + float roll; +}; + +class kexBBox +{ +public: + kexBBox(void); + explicit kexBBox(const kexVec3 &vMin, const kexVec3 &vMax); + + void Clear(void); + kexVec3 Center(void) const; + float Radius(void) const; + void AddPoint(const kexVec3 &vec); + bool PointInside(const kexVec3 &vec) const; + bool IntersectingBox(const kexBBox &box) const; + bool IntersectingBox2D(const kexBBox &box) const; + float DistanceToPlane(kexPlane &plane); + bool LineIntersect(const kexVec3 &start, const kexVec3 &end); + void ToPoints(float *points) const; + void ToVectors(kexVec3 *vectors) const; + + kexBBox operator+(const float radius) const; + kexBBox &operator+=(const float radius); + kexBBox operator+(const kexVec3 &vec) const; + kexBBox operator-(const float radius) const; + kexBBox operator-(const kexVec3 &vec) const; + kexBBox &operator-=(const float radius); + kexBBox operator*(const kexMatrix &matrix) const; + kexBBox &operator*=(const kexMatrix &matrix); + kexBBox operator*(const kexVec3 &vec) const; + kexBBox &operator*=(const kexVec3 &vec); + kexBBox &operator=(const kexBBox &bbox); + kexVec3 operator[](int index) const; + kexVec3 &operator[](int index); + + kexVec3 min; + kexVec3 max; +}; + +// +// kexMath::Sin +// + +d_inline float kexMath::Sin(float x) +{ + return sinf(x); +} + +// +// kexMath::Cos +// + +d_inline float kexMath::Cos(float x) +{ + return cosf(x); +} + +// +// kexMath::Tan +// + +d_inline float kexMath::Tan(float x) +{ + return tanf(x); +} + +// +// kexMath::ATan2 +// + +d_inline float kexMath::ATan2(float x, float y) +{ + return atan2f(x, y); +} + +// +// kexMath::ACos +// + +d_inline float kexMath::ACos(float x) +{ + return acosf(x); +} + +// +// kexMath::Sqrt +// + +d_inline float kexMath::Sqrt(float x) +{ + return x * InvSqrt(x); +} + +// +// kexMath::Pow +// + +d_inline float kexMath::Pow(float x, float y) +{ + return powf(x, y); +} + +// +// kexMath::Log +// + +d_inline float kexMath::Log(float x) +{ + return logf(x); +} + +// +// kexMath::Floor +// + +d_inline float kexMath::Floor(float x) +{ + return floorf(x); +} + +// +// kexMath::Ceil +// + +d_inline float kexMath::Ceil(float x) +{ + return ceilf(x); +} + +// +// kexMath::Deg2Rad +// + +d_inline float kexMath::Deg2Rad(float x) +{ + return DEG2RAD(x); +} + +// +// kexMath::Rad2Deg +// + +d_inline float kexMath::Rad2Deg(float x) +{ + return RAD2DEG(x); +} + +// +// kexMath::Abs +// + +d_inline int kexMath::Abs(int x) +{ + int y = x >> 31; + return ((x ^ y) - y); +} + +// +// kexMath::Fabs +// + +d_inline float kexMath::Fabs(float x) +{ + int tmp = *reinterpret_cast(&x); + tmp &= 0x7FFFFFFF; + return *reinterpret_cast(&tmp); +} + +// +// kexMath::InvSqrt +// + +d_inline float kexMath::InvSqrt(float x) +{ + unsigned int i; + float r; + float y; + + y = x * 0.5f; + i = *reinterpret_cast(&x); + i = 0x5f3759df - (i >> 1); + r = *reinterpret_cast(&i); + r = r * (1.5f - r * r * y); + + return r; +} + +// +// kexMath::Clamp +// + +d_inline void kexMath::Clamp(float &f, const float min, const float max) +{ + if(f < min) { f = min; } + if(f > max) { f = max; } +} + +// +// kexMath::Clamp +// + +d_inline void kexMath::Clamp(byte &b, const byte min, const byte max) +{ + if(b < min) { b = min; } + if(b > max) { b = max; } +} + +// +// kexMath::Clamp +// + +d_inline void kexMath::Clamp(int &i, const int min, const int max) +{ + if(i < min) { i = min; } + if(i > max) { i = max; } +} + +// +// kexMath::Clamp +// + +d_inline void kexMath::Clamp(kexVec3 &v, const float min, const float max) +{ + if(v.x < min) { v.x = min; } + if(v.x > max) { v.x = max; } + if(v.y < min) { v.y = min; } + if(v.y > max) { v.y = max; } + if(v.z < min) { v.z = min; } + if(v.z > max) { v.z = max; } +} + +#endif + diff --git a/src/lightmap/kexlib/math/matrix.cpp b/src/lightmap/kexlib/math/matrix.cpp new file mode 100644 index 0000000..7870325 --- /dev/null +++ b/src/lightmap/kexlib/math/matrix.cpp @@ -0,0 +1,717 @@ +//----------------------------------------------------------------------------- +// 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: Matrix (left handed) operations +// +// Reference: +// _________________ +// | 0 4 8 12 | +// | 1 5 9 13 | +// | 2 6 10 14 | +// | 3 7 11 15 | +// _________________ +// +// translation +// _________________ +// | 0 4 8 x | +// | 1 5 9 y | +// | 2 6 10 z | +// | 3 7 11 15 | +// _________________ +// +// rotation x +// _________________ +// |(1) 4 8 x | +// | 1 xc -xs y | +// | 2 xs xs z | +// | 3 7 11 (1) | +// _________________ +// +// rotation y +// _________________ +// | yc 4 ys 12 | +// | 1 (1) 9 13 | +// |-ys 6 yc 14 | +// | 3 7 11 (1) | +// _________________ +// +// rotation z +// _________________ +// | zc -zs 8 12 | +// | zs zc 9 13 | +// | 2 6 (1) 14 | +// | 3 7 11 (1) | +// _________________ +// +//----------------------------------------------------------------------------- + +#include +#include "mathlib.h" + +// +// kexMatrix::kexMatrix +// + +kexMatrix::kexMatrix(void) +{ + Identity(); +} + +// +// kexMatrix::kexMatrix +// + +kexMatrix::kexMatrix(const kexMatrix &mtx) +{ + for(int i = 0; i < 4; i++) + { + vectors[i].x = mtx.vectors[i].x; + vectors[i].y = mtx.vectors[i].y; + vectors[i].z = mtx.vectors[i].z; + vectors[i].w = mtx.vectors[i].w; + } +} + +// +// kexMatrix::kexMatrix +// + +kexMatrix::kexMatrix(const float x, const float y, const float z) +{ + Identity(x, y, z); +} + +// +// kexMatrix::kexMatrix +// + +kexMatrix::kexMatrix(const kexQuat &quat) +{ + float xx = quat.x * quat.x; + float yx = quat.y * quat.x; + float zx = quat.z * quat.x; + float wx = quat.w * quat.x; + float yy = quat.y * quat.y; + float zy = quat.z * quat.y; + float wy = quat.w * quat.y; + float zz = quat.z * quat.z; + float wz = quat.w * quat.z; + float ww = quat.w * quat.w; + + vectors[0].Set( + ((ww + xx) - yy) - zz, + (wz + wz) + (yx + yx), + (zx + zx) - (wy + wy), + 0); + vectors[1].Set( + (yx + yx) - (wz + wz), + (yy + (ww - xx)) - zz, + (wx + wx) + (zy + zy), + 0); + vectors[2].Set( + (wy + wy + zx + zx), + (zy + zy) - (wx + wx), + ((ww - xx) - yy) + zz, + 0); + vectors[3].Set(0, 0, 0, 1); +} + +// +// kexMatrix::kexMatrix +// + +kexMatrix::kexMatrix(const float angle, const int axis) +{ + float s; + float c; + + s = kexMath::Sin(angle); + c = kexMath::Cos(angle); + + Identity(); + + switch(axis) + { + case 0: + this->vectors[0].x = c; + this->vectors[0].z = -s; + this->vectors[3].x = s; + this->vectors[3].z = c; + break; + case 1: + this->vectors[1].y = c; + this->vectors[1].z = s; + this->vectors[2].y = -s; + this->vectors[2].z = c; + break; + case 2: + this->vectors[0].x = c; + this->vectors[0].y = s; + this->vectors[1].x = -s; + this->vectors[1].y = c; + break; + } +} + +// +// kexMatrix::Identity +// + +kexMatrix &kexMatrix::Identity(void) +{ + vectors[0].Set(1, 0, 0, 0); + vectors[1].Set(0, 1, 0, 0); + vectors[2].Set(0, 0, 1, 0); + vectors[3].Set(0, 0, 0, 1); + + return *this; +} + +// +// kexMatrix::Identity +// + +kexMatrix &kexMatrix::Identity(const float x, const float y, const float z) +{ + vectors[0].Set(x, 0, 0, 0); + vectors[1].Set(0, y, 0, 0); + vectors[2].Set(0, 0, z, 0); + vectors[3].Set(0, 0, 0, 1); + + return *this; +} + +// +// kexMatrix::SetTranslation +// + +kexMatrix &kexMatrix::SetTranslation(const float x, const float y, const float z) +{ + vectors[3].ToVec3().Set(x, y, z); + return *this; +} + +// +// kexMatrix::SetTranslation +// + +kexMatrix &kexMatrix::SetTranslation(const kexVec3 &vector) +{ + vectors[3].ToVec3() = vector; + return *this; +} + +// +// kexMatrix::AddTranslation +// + +kexMatrix &kexMatrix::AddTranslation(const float x, const float y, const float z) +{ + vectors[3].x += x; + vectors[3].y += y; + vectors[3].z += z; + return *this; +} + +// +// kexMatrix::AddTranslation +// + +kexMatrix &kexMatrix::AddTranslation(const kexVec3 &vector) +{ + vectors[3].ToVec3() += vector; + return *this; +} + +// +// kexMatrix::Scale +// + +kexMatrix &kexMatrix::Scale(const float x, const float y, const float z) +{ + vectors[0].ToVec3() *= x; + vectors[1].ToVec3() *= y; + vectors[2].ToVec3() *= z; + + return *this; +} + +// +// kexMatrix::Scale +// + +kexMatrix &kexMatrix::Scale(const kexVec3 &vector) +{ + vectors[0].ToVec3() *= vector.x; + vectors[1].ToVec3() *= vector.y; + vectors[2].ToVec3() *= vector.z; + + return *this; +} + +// +// kexMatrix::Scale +// + +kexMatrix kexMatrix::Scale(const kexMatrix &mtx, const float x, const float y, const float z) +{ + kexMatrix out; + + out.vectors[0].ToVec3() = mtx.vectors[0].ToVec3() * x; + out.vectors[1].ToVec3() = mtx.vectors[1].ToVec3() * y; + out.vectors[2].ToVec3() = mtx.vectors[2].ToVec3() * z; + + return out; +} + +// +// kexMatrix::Transpose +// + +kexMatrix &kexMatrix::Transpose(void) +{ + kexVec3 v1 = vectors[1].ToVec3(); + kexVec3 v2 = vectors[2].ToVec3(); + + vectors[1].ToVec3() = v2; + vectors[2].ToVec3() = v1; + return *this; +} + +// +// kexMatrix::Transpose +// + +kexMatrix kexMatrix::Transpose(const kexMatrix &mtx) +{ + kexMatrix out; + + out.vectors[0].ToVec3() = mtx.vectors[0].ToVec3(); + out.vectors[1].ToVec3() = mtx.vectors[2].ToVec3(); + out.vectors[2].ToVec3() = mtx.vectors[1].ToVec3(); + out.vectors[3].ToVec3() = mtx.vectors[3].ToVec3(); + + return out; +} + +// +// kexMatrix::Invert +// + +kexMatrix kexMatrix::Invert(kexMatrix &mtx) +{ + float d; + float *m; + + m = mtx.ToFloatPtr(); + + d = m[ 0] * m[10] * m[ 5] - + m[ 0] * m[ 9] * m[ 6] - + m[ 1] * m[ 4] * m[10] + + m[ 2] * m[ 4] * m[ 9] + + m[ 1] * m[ 6] * m[ 8] - + m[ 2] * m[ 5] * m[ 8]; + + if(d != 0.0f) + { + kexMatrix inv; + + d = (1.0f / d); + + inv.vectors[0].x = ( m[10] * m[ 5] - m[ 9] * m[ 6]) * d; + inv.vectors[0].y = -((m[ 1] * m[10] - m[ 2] * m[ 9]) * d); + inv.vectors[0].z = ( m[ 1] * m[ 6] - m[ 2] * m[ 5]) * d; + inv.vectors[0].w = 0; + inv.vectors[1].x = ( m[ 6] * m[ 8] - m[ 4] * m[10]) * d; + inv.vectors[1].y = ( m[ 0] * m[10] - m[ 2] * m[ 8]) * d; + inv.vectors[1].z = -((m[ 0] * m[ 6] - m[ 2] * m[ 4]) * d); + inv.vectors[1].w = 0; + inv.vectors[2].x = -((m[ 5] * m[ 8] - m[ 4] * m[ 9]) * d); + inv.vectors[2].y = ( m[ 1] * m[ 8] - m[ 0] * m[ 9]) * d; + inv.vectors[2].z = -((m[ 1] * m[ 4] - m[ 0] * m[ 5]) * d); + inv.vectors[2].w = 0; + inv.vectors[3].x = ( + ( m[13] * m[10] - m[14] * m[ 9]) * m[ 4] + + m[14] * m[ 5] * m[ 8] + - m[13] * m[ 6] * m[ 8] + - m[12] * m[10] * m[ 5] + + m[12] * m[ 9] * m[ 6]) * d; + inv.vectors[3].y = ( + m[ 0] * m[14] * m[ 9] + - m[ 0] * m[13] * m[10] + - m[14] * m[ 1] * m[ 8] + + m[13] * m[ 2] * m[ 8] + + m[12] * m[ 1] * m[10] + - m[12] * m[ 2] * m[ 9]) * d; + inv.vectors[3].z = -( + ( m[ 0] * m[14] * m[ 5] + - m[ 0] * m[13] * m[ 6] + - m[14] * m[ 1] * m[ 4] + + m[13] * m[ 2] * m[ 4] + + m[12] * m[ 1] * m[ 6] + - m[12] * m[ 2] * m[ 5]) * d); + inv.vectors[3].w = 1.0f; + + return inv; + } + + return mtx; +} + +// +// kexMatrix::SetViewProjection +// + +void kexMatrix::SetViewProjection(float aspect, float fov, float zNear, float zFar) +{ + float top = zNear * kexMath::Tan(fov * M_PI / 360.0f); + float bottom = -top; + float left = bottom * aspect; + float right = top * aspect; + + vectors[0].x = (2 * zNear) / (right - left); + vectors[1].y = (2 * zNear) / (top - bottom); + vectors[3].z = -(2 * zFar * zNear) / (zFar - zNear); + + vectors[2].x = (right + left) / (right - left); + vectors[2].y = (top + bottom) / (top - bottom); + vectors[2].z = -(zFar + zNear) / (zFar - zNear); + + vectors[0].y = 0; + vectors[0].z = 0; + vectors[0].w = 0; + vectors[1].x = 0; + vectors[1].w = 0; + vectors[1].z = 0; + vectors[2].w = -1; + vectors[3].x = 0; + vectors[3].y = 0; + vectors[3].w = 0; +} + +// +// kexMatrix::SetOrtho +// + +void kexMatrix::SetOrtho(float left, float right, + float bottom, float top, + float zNear, float zFar) +{ + vectors[0].x = 2 / (right - left); + vectors[1].y = 2 / (top - bottom); + vectors[2].z = -2 / (zFar - zNear); + + vectors[3].x = -(right + left) / (right - left); + vectors[3].y = -(top + bottom) / (top - bottom); + vectors[3].z = -(zFar + zNear) / (zFar - zNear); + vectors[3].w = 1; + + vectors[0].y = 0; + vectors[0].z = 0; + vectors[0].w = 0; + vectors[1].x = 0; + vectors[1].z = 0; + vectors[1].w = 0; + vectors[2].x = 0; + vectors[2].y = 0; + vectors[2].w = 0; +} + +// +// kexMatrix::ToQuat +// + +kexQuat kexMatrix::ToQuat(void) +{ + float t; + float d; + float mx; + float my; + float mz; + float m21; + float m20; + float m10; + kexQuat q; + + mx = vectors[0][0]; + my = vectors[1][1]; + mz = vectors[2][2]; + + m21 = (vectors[2][1] - vectors[1][2]); + m20 = (vectors[2][0] - vectors[0][2]); + m10 = (vectors[1][0] - vectors[0][1]); + + t = 1.0f + mx + my + mz; + + if(t > 0) + { + d = 0.5f / kexMath::Sqrt(t); + q.x = m21 * d; + q.y = m20 * d; + q.z = m10 * d; + q.w = 0.25f / d; + } + else if(mx > my && mx > mz) + { + d = kexMath::Sqrt(1.0f + mx - my - mz) * 2; + q.x = 0.5f / d; + q.y = m10 / d; + q.z = m20 / d; + q.w = m21 / d; + } + else if(my > mz) + { + d = kexMath::Sqrt(1.0f + my - mx - mz) * 2; + q.x = m10 / d; + q.y = 0.5f / d; + q.z = m21 / d; + q.w = m20 / d; + } + else + { + d = kexMath::Sqrt(1.0f + mz - mx - my) * 2; + q.x = m20 / d; + q.y = m21 / d; + q.z = 0.5f / d; + q.w = m10 / d; + } + + q.Normalize(); + return q; +} + +// +// kexMatrix::operator* +// + +kexMatrix kexMatrix::operator*(const kexVec3 &vector) +{ + kexMatrix out(*this); + + out.vectors[3].ToVec3() += + vectors[0].ToVec3() * vector.x + + vectors[1].ToVec3() * vector.y + + vectors[2].ToVec3() * vector.z; + return out; +} + +// +// kexMatrix::operator*= +// + +kexMatrix &kexMatrix::operator*=(const kexVec3 &vector) +{ + vectors[3].ToVec3() += + vectors[0].ToVec3() * vector.x + + vectors[1].ToVec3() * vector.y + + vectors[2].ToVec3() * vector.z; + return *this; +} + +// +// kexMatrix::ToFloatPtr +// + +float *kexMatrix::ToFloatPtr(void) +{ + return reinterpret_cast(vectors); +} + +// +// kexMatrix::operator* +// + +kexMatrix kexMatrix::operator*(const kexMatrix &matrix) +{ + kexMatrix out; + + for(int i = 0; i < 4; i++) + { + out.vectors[i].x = + vectors[i].x * matrix.vectors[0].x + + vectors[i].y * matrix.vectors[1].x + + vectors[i].z * matrix.vectors[2].x + + vectors[i].w * matrix.vectors[3].x; + out.vectors[i].y = + vectors[i].x * matrix.vectors[0].y + + vectors[i].y * matrix.vectors[1].y + + vectors[i].z * matrix.vectors[2].y + + vectors[i].w * matrix.vectors[3].y; + out.vectors[i].z = + vectors[i].x * matrix.vectors[0].z + + vectors[i].y * matrix.vectors[1].z + + vectors[i].z * matrix.vectors[2].z + + vectors[i].w * matrix.vectors[3].z; + out.vectors[i].w = + vectors[i].x * matrix.vectors[0].w + + vectors[i].y * matrix.vectors[1].w + + vectors[i].z * matrix.vectors[2].w + + vectors[i].w * matrix.vectors[3].w; + } + + return out; +} + +// +// kexMatrix::operator*= +// + +kexMatrix &kexMatrix::operator*=(const kexMatrix &matrix) +{ + for(int i = 0; i < 4; i++) + { + vectors[i].x = + vectors[i].x * matrix.vectors[0].x + + vectors[i].y * matrix.vectors[1].x + + vectors[i].z * matrix.vectors[2].x + + vectors[i].w * matrix.vectors[3].x; + vectors[i].y = + vectors[i].x * matrix.vectors[0].y + + vectors[i].y * matrix.vectors[1].y + + vectors[i].z * matrix.vectors[2].y + + vectors[i].w * matrix.vectors[3].y; + vectors[i].z = + vectors[i].x * matrix.vectors[0].z + + vectors[i].y * matrix.vectors[1].z + + vectors[i].z * matrix.vectors[2].z + + vectors[i].w * matrix.vectors[3].z; + vectors[i].w = + vectors[i].x * matrix.vectors[0].w + + vectors[i].y * matrix.vectors[1].w + + vectors[i].z * matrix.vectors[2].w + + vectors[i].w * matrix.vectors[3].w; + } + + return *this; +} + +// +// kexMatrix::operator* +// + +kexMatrix operator*(const kexMatrix &m1, const kexMatrix &m2) +{ + kexMatrix out; + + for(int i = 0; i < 4; i++) + { + out.vectors[i].x = + m1.vectors[i].x * m2.vectors[0].x + + m1.vectors[i].y * m2.vectors[1].x + + m1.vectors[i].z * m2.vectors[2].x + + m1.vectors[i].w * m2.vectors[3].x; + out.vectors[i].y = + m1.vectors[i].x * m2.vectors[0].y + + m1.vectors[i].y * m2.vectors[1].y + + m1.vectors[i].z * m2.vectors[2].y + + m1.vectors[i].w * m2.vectors[3].y; + out.vectors[i].z = + m1.vectors[i].x * m2.vectors[0].z + + m1.vectors[i].y * m2.vectors[1].z + + m1.vectors[i].z * m2.vectors[2].z + + m1.vectors[i].w * m2.vectors[3].z; + out.vectors[i].w = + m1.vectors[i].x * m2.vectors[0].w + + m1.vectors[i].y * m2.vectors[1].w + + m1.vectors[i].z * m2.vectors[2].w + + m1.vectors[i].w * m2.vectors[3].w; + } + + return out; +} + +// +// kexMatrix::operator| +// + +kexMatrix kexMatrix::operator|(const kexMatrix &matrix) +{ + kexMatrix out; + + for(int i = 0; i < 3; i++) + { + out.vectors[i].x = + vectors[i].x * matrix.vectors[0].x + + vectors[i].y * matrix.vectors[1].x + + vectors[i].z * matrix.vectors[2].x; + out.vectors[i].y = + vectors[i].x * matrix.vectors[0].y + + vectors[i].y * matrix.vectors[1].y + + vectors[i].z * matrix.vectors[2].y; + out.vectors[i].z = + vectors[i].x * matrix.vectors[0].z + + vectors[i].y * matrix.vectors[1].z + + vectors[i].z * matrix.vectors[2].z; + } + + out.vectors[3].x = + vectors[3].x * matrix.vectors[0].x + + vectors[3].y * matrix.vectors[1].x + + vectors[3].z * matrix.vectors[2].x + matrix.vectors[3].x; + out.vectors[3].y = + vectors[3].x * matrix.vectors[0].y + + vectors[3].y * matrix.vectors[1].y + + vectors[3].z * matrix.vectors[2].y + matrix.vectors[3].y; + out.vectors[3].z = + vectors[3].x * matrix.vectors[0].z + + vectors[3].y * matrix.vectors[1].z + + vectors[3].z * matrix.vectors[2].z + matrix.vectors[3].z; + + return out; +} + +// +// kexMatrix::operator= +// + +kexMatrix &kexMatrix::operator=(const kexMatrix &matrix) +{ + vectors[0] = matrix.vectors[0]; + vectors[1] = matrix.vectors[1]; + vectors[2] = matrix.vectors[2]; + vectors[3] = matrix.vectors[3]; + + return *this; +} + +// +// kexMatrix::operator= +// + +kexMatrix &kexMatrix::operator=(const float *m) +{ + for(int i = 0; i < 4; i++) + { + for(int j = 0; j < 4; j++) + { + vectors[i][j] = m[i * 4 + j]; + } + } + + return *this; +} diff --git a/src/lightmap/kexlib/math/plane.cpp b/src/lightmap/kexlib/math/plane.cpp new file mode 100644 index 0000000..50350ec --- /dev/null +++ b/src/lightmap/kexlib/math/plane.cpp @@ -0,0 +1,251 @@ +//----------------------------------------------------------------------------- +// 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: Plane operations +// +//----------------------------------------------------------------------------- + +#include +#include "mathlib.h" + +// +// kexPlane::kexPlane +// + +kexPlane::kexPlane(void) +{ + this->a = 0; + this->b = 0; + this->c = 0; + this->d = 0; +} + +// +// kexPlane::kexPlane +// + +kexPlane::kexPlane(const float a, const float b, const float c, const float d) +{ + this->a = a; + this->b = b; + this->c = c; + this->d = d; +} + +// +// kexPlane::kexPlane +// + +kexPlane::kexPlane(const kexVec3 &pt1, const kexVec3 &pt2, const kexVec3 &pt3) +{ + SetNormal(pt1, pt2, pt3); + this->d = kexVec3::Dot(pt1, Normal()); +} + +// +// kexPlane::kexPlane +// + +kexPlane::kexPlane(const kexVec3 &normal, const kexVec3 &point) +{ + this->a = normal.x; + this->b = normal.y; + this->c = normal.z; + this->d = point.Dot(normal); +} + +// +// kexPlane::kexPlane +// + +kexPlane::kexPlane(const kexPlane &plane) +{ + this->a = plane.a; + this->b = plane.b; + this->c = plane.c; + this->d = plane.d; +} + +// +// kexPlane::SetNormal +// + +kexPlane &kexPlane::SetNormal(const kexVec3 &normal) +{ + Normal() = normal; + return *this; +} + +// +// kexPlane::SetNormal +// + +kexPlane &kexPlane::SetNormal(const kexVec3 &pt1, const kexVec3 &pt2, const kexVec3 &pt3) +{ + Normal() = (pt2 - pt1).Cross(pt3 - pt2).Normalize(); + return *this; +} + +// +// kexPlane::Normal +// + +kexVec3 const &kexPlane::Normal(void) const +{ + return *reinterpret_cast(&a); +} + +// +// kexPlane::Normal +// + +kexVec3 &kexPlane::Normal(void) +{ + return *reinterpret_cast(&a); +} + +// +// kexPlane::Distance +// + +float kexPlane::Distance(const kexVec3 &point) +{ + return point.Dot(Normal()); +} + +// +// kexPlane::SetDistance +// + +kexPlane &kexPlane::SetDistance(const kexVec3 &point) +{ + this->d = point.Dot(Normal()); + return *this; +} + +// +// kexPlane::IsFacing +// + +bool kexPlane::IsFacing(const float yaw) +{ + return -kexMath::Sin(yaw) * a + -kexMath::Cos(yaw) * b < 0; +} + +// +// kexPlane::ToYaw +// + +float kexPlane::ToYaw(void) +{ + float d = Normal().Unit(); + + if(d != 0) + { + float phi; + phi = kexMath::ACos(b / d); + if(a <= 0) + { + phi = -phi; + } + + return phi; + } + + return 0; +} + +// +// kexPlane::ToPitch +// + +float kexPlane::ToPitch(void) +{ + return kexMath::ACos(kexVec3::vecUp.Dot(Normal())); +} + +// +// kexPlane::ToQuat +// + +kexQuat kexPlane::ToQuat(void) +{ + kexVec3 cross = kexVec3::vecUp.Cross(Normal()).Normalize(); + return kexQuat(kexMath::ACos(kexVec3::vecUp.Dot(Normal())), cross); +} + +// +// kexPlane::ToVec4 +// + +kexVec4 const &kexPlane::ToVec4(void) const +{ + return *reinterpret_cast(&a); +} + +// +// kexPlane::ToVec4 +// + +kexVec4 &kexPlane::ToVec4(void) +{ + return *reinterpret_cast(&a); +} + +// +// kexPlane::BestAxis +// + +const kexPlane::planeAxis_t kexPlane::BestAxis(void) const +{ + float na = kexMath::Fabs(a); + float nb = kexMath::Fabs(b); + float nc = kexMath::Fabs(c); + + // figure out what axis the plane lies on + if(na >= nb && na >= nc) + { + return AXIS_YZ; + } + else if(nb >= na && nb >= nc) + { + return AXIS_XZ; + } + + return AXIS_XY; +} + +// +// kexPlane::GetInclination +// + +kexVec3 kexPlane::GetInclination(void) +{ + kexVec3 dir = Normal() * kexVec3::vecUp.Dot(Normal()); + return (kexVec3::vecUp - dir).Normalize(); +} diff --git a/src/lightmap/kexlib/math/pluecker.cpp b/src/lightmap/kexlib/math/pluecker.cpp new file mode 100644 index 0000000..9c1e81c --- /dev/null +++ b/src/lightmap/kexlib/math/pluecker.cpp @@ -0,0 +1,107 @@ +//----------------------------------------------------------------------------- +// 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: Pluecker operations +// This stuff makes my brain hurt... +// +//----------------------------------------------------------------------------- + +#include +#include "mathlib.h" + +// +// kexPluecker::kexPluecker +// + +kexPluecker::kexPluecker(void) +{ + Clear(); +} + +// +// kexPluecker::kexPluecker +// + +kexPluecker::kexPluecker(const kexVec3 &start, const kexVec3 &end, bool bRay) +{ + bRay ? SetRay(start, end) : SetLine(start, end); +} + +// +// kexPluecker::Clear +// + +void kexPluecker::Clear(void) +{ + p[0] = p[1] = p[2] = p[3] = p[4] = p[5] = 0; +} + +// +// kexPluecker::SetLine +// + +void kexPluecker::SetLine(const kexVec3 &start, const kexVec3 &end) +{ + p[0] = start.x * end.y - end.x * start.y; + p[1] = start.x * end.z - end.x * start.z; + p[3] = start.y * end.z - end.y * start.z; + + p[2] = start.x - end.x; + p[5] = end.y - start.y; + p[4] = start.z - end.z; +} + +// +// kexPluecker::SetRay +// + +void kexPluecker::SetRay(const kexVec3 &start, const kexVec3 &dir) +{ + p[0] = start.x * dir.y - dir.x * start.y; + p[1] = start.x * dir.z - dir.x * start.z; + p[3] = start.y * dir.z - dir.y * start.z; + + p[2] = -dir.x; + p[5] = dir.y; + p[4] = -dir.z; +} + +// +// kexPluecker::InnerProduct +// + +float kexPluecker::InnerProduct(const kexPluecker &pluecker) const +{ + return + p[0] * pluecker.p[4] + + p[1] * pluecker.p[5] + + p[2] * pluecker.p[3] + + p[4] * pluecker.p[0] + + p[5] * pluecker.p[1] + + p[3] * pluecker.p[2]; +} diff --git a/src/lightmap/kexlib/math/quaternion.cpp b/src/lightmap/kexlib/math/quaternion.cpp new file mode 100644 index 0000000..6594934 --- /dev/null +++ b/src/lightmap/kexlib/math/quaternion.cpp @@ -0,0 +1,446 @@ +//----------------------------------------------------------------------------- +// 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: Quaternion operations +// +//----------------------------------------------------------------------------- + +#include +#include "mathlib.h" + +// +// kexQuat::kexQuat +// + +kexQuat::kexQuat(void) +{ + Clear(); +} + +// +// kexQuat::kexQuat +// + +kexQuat::kexQuat(const float angle, const float x, const float y, const float z) +{ + float s = kexMath::Sin(angle * 0.5f); + float c = kexMath::Cos(angle * 0.5f); + + this->x = x * s; + this->y = y * s; + this->z = z * s; + this->w = c; +} + +// +// kexQuat::kexQuat +// + +kexQuat::kexQuat(const float angle, kexVec3 &vector) +{ + float s = kexMath::Sin(angle * 0.5f); + float c = kexMath::Cos(angle * 0.5f); + + this->x = vector.x * s; + this->y = vector.y * s; + this->z = vector.z * s; + this->w = c; +} + +// +// kexQuat::kexQuat +// + +kexQuat::kexQuat(const float angle, const kexVec3 &vector) +{ + float s = kexMath::Sin(angle * 0.5f); + float c = kexMath::Cos(angle * 0.5f); + + this->x = vector.x * s; + this->y = vector.y * s; + this->z = vector.z * s; + this->w = c; +} + +// +// kexQuat::Set +// + +void kexQuat::Set(const float x, const float y, const float z, const float w) +{ + this->x = x; + this->y = y; + this->z = z; + this->w = w; +} + +// +// kexQuat::Clear +// + +void kexQuat::Clear(void) +{ + x = y = z = 0.0f; + w = 1.0f; +} + +// +// kexVec3::UnitSq +// + +float kexQuat::UnitSq(void) const +{ + return x * x + y * y + z * z + w * w; +} + +// +// kexVec3::Unit +// + +float kexQuat::Unit(void) const +{ + return kexMath::Sqrt(UnitSq()); +} + +// +// kexQuat::Normalize +// + +kexQuat &kexQuat::Normalize(void) +{ + float d = Unit(); + if(d != 0.0f) + { + d = 1.0f / d; + *this *= d; + } + return *this; +} + +// +// kexQuat::Inverse +// + +kexQuat kexQuat::Inverse(void) const +{ + kexQuat out; + out.Set(-x, -y, -z, -w); + return out; +} + +// +// kexQuat::operator+ +// + +kexQuat kexQuat::operator+(const kexQuat &quat) +{ + kexQuat out; + out.x = x + quat.x; + out.y = y + quat.y; + out.z = z + quat.z; + out.w = w + quat.w; + return out; +} + +// +// kexQuat::operator+= +// + +kexQuat &kexQuat::operator+=(const kexQuat &quat) +{ + x += quat.x; + y += quat.y; + z += quat.z; + w += quat.w; + return *this; +} + +// +// kexQuat::operator- +// + +kexQuat kexQuat::operator-(const kexQuat &quat) +{ + kexQuat out; + out.x = x - quat.x; + out.y = y - quat.y; + out.z = z - quat.z; + out.w = w - quat.w; + return out; +} + +// +// kexQuat::operator-= +// + +kexQuat &kexQuat::operator-=(const kexQuat &quat) +{ + x -= quat.x; + y -= quat.y; + z -= quat.z; + w -= quat.w; + return *this; +} + +// +// kexQuat::operator* +// + +kexQuat kexQuat::operator*(const kexQuat &quat) +{ + kexQuat out; + + out.x = x * quat.w - y * quat.z + quat.x * w + quat.y * z; + out.y = x * quat.z + y * quat.w - quat.x * z + w * quat.y; + out.z = quat.x * y + w * quat.z + z * quat.w - x * quat.y; + out.w = w * quat.w - quat.y * y + z * quat.z + quat.x * x; + + return out; +} + +// +// kexQuat::operator*= +// + +kexQuat &kexQuat::operator*=(const kexQuat &quat) +{ + float tx = x; + float ty = y; + float tz = z; + float tw = w; + + x = tx * quat.w - ty * quat.z + quat.x * tw + quat.y * z; + y = tx * quat.z + ty * quat.w - quat.x * tz + tw * quat.y; + z = quat.x * ty + tw * quat.z + tz * quat.w - tx * quat.y; + w = tw * quat.w - quat.y * ty + tz * quat.z + quat.x * x; + + return *this; +} + +// +// kexQuat::operator* +// + +kexQuat kexQuat::operator*(const float val) const +{ + kexQuat out; + out.x = x * val; + out.y = y * val; + out.z = z * val; + out.w = w * val; + return out; +} + +// +// kexQuat::operator*= +// + +kexQuat &kexQuat::operator*=(const float val) +{ + x *= val; + y *= val; + z *= val; + w *= val; + + return *this; +} + +// +// kexQuat::operator| +// + +kexVec3 kexQuat::operator|(const kexVec3 &vector) +{ + float xx = x * x; + float yx = y * x; + float zx = z * x; + float wx = w * x; + float yy = y * y; + float zy = z * y; + float wy = w * y; + float zz = z * z; + float wz = w * z; + float ww = w * w; + + return kexVec3( + ((yx + yx) - (wz + wz)) * vector.y + + ((wy + wy + zx + zx)) * vector.z + + (((ww + xx) - yy) - zz) * vector.x, + ((yy + (ww - xx)) - zz) * vector.y + + ((zy + zy) - (wx + wx)) * vector.z + + ((wz + wz) + (yx + yx)) * vector.x, + ((wx + wx) + (zy + zy)) * vector.y + + (((ww - xx) - yy) + zz) * vector.z + + ((zx + zx) - (wy + wy)) * vector.x + ); +} + +// +// kexQuat::Dot +// + +float kexQuat::Dot(const kexQuat &quat) const +{ + return (x * quat.x + y * quat.y + z * quat.z + w * quat.w); +} + +// +// kexQuat::Slerp +// + +kexQuat kexQuat::Slerp(const kexQuat &quat, float movement) const +{ + kexQuat rdest = quat; + float d1 = Dot(quat); + float d2 = Dot(quat.Inverse()); + + if(d1 < d2) + { + rdest = quat.Inverse(); + d1 = d2; + } + + if(d1 <= 0.7071067811865001f) + { + float halfcos = kexMath::ACos(d1); + float halfsin = kexMath::Sin(halfcos); + + if(halfsin == 0) + { + kexQuat out; + out.Set(x, y, z, w); + return out; + } + else + { + float d; + float ms1; + float ms2; + + d = 1.0f / halfsin; + ms1 = kexMath::Sin((1.0f - movement) * halfcos) * d; + ms2 = kexMath::Sin(halfcos * movement) * d; + + if(ms2 < 0) + { + rdest = quat.Inverse(); + } + + return *this * ms1 + rdest * ms2; + } + } + else + { + kexQuat out = (rdest - *this) * movement + *this; + out.Normalize(); + return out; + } +} + +// +// kexQuat::RotateFrom +// + +kexQuat kexQuat::RotateFrom(const kexVec3 &location, const kexVec3 &target, float maxAngle) +{ + kexVec3 axis; + kexVec3 dir; + kexVec3 cp; + kexQuat prot; + float an; + + dir = (*this | kexVec3::vecForward); + axis = (target - location).Normalize(); + cp = dir.Cross(axis).Normalize(); + + an = kexMath::ACos(axis.Dot(dir)); + + if(maxAngle != 0 && an >= maxAngle) + { + an = maxAngle; + } + + return (*this * kexQuat(an, cp)); +} + +// +// kexQuat::operator= +// + +kexQuat &kexQuat::operator=(const kexQuat &quat) +{ + x = quat.x; + y = quat.y; + z = quat.z; + w = quat.w; + return *this; +} + +// +// kexQuat::operator= +// + +kexQuat &kexQuat::operator=(const kexVec4 &vec) +{ + x = vec.x; + y = vec.y; + z = vec.z; + w = vec.w; + return *this; +} + +// +// kexQuat::operator= +// + +kexQuat &kexQuat::operator=(const float *vecs) +{ + x = vecs[0]; + y = vecs[1]; + z = vecs[2]; + w = vecs[3]; + return *this; +} + +// +// kexQuat::ToVec3 +// + +kexVec3 const &kexQuat::ToVec3(void) const +{ + return *reinterpret_cast(this); +} + +// +// kexQuat::ToVec3 +// + +kexVec3 &kexQuat::ToVec3(void) +{ + return *reinterpret_cast(this); +} diff --git a/src/lightmap/kexlib/math/random.cpp b/src/lightmap/kexlib/math/random.cpp new file mode 100644 index 0000000..d75c2e1 --- /dev/null +++ b/src/lightmap/kexlib/math/random.cpp @@ -0,0 +1,99 @@ +//----------------------------------------------------------------------------- +// 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: Random operations +// +//----------------------------------------------------------------------------- + +#include +#include "mathlib.h" + +#define RANDOM_MAX 0x7FFF + +int kexRand::seed = 0; + +// +// kexRand::SetSeed +// + +void kexRand::SetSeed(const int randSeed) +{ + seed = randSeed; +} + +// +// kexRand::SysRand +// + +int kexRand::SysRand(void) +{ + return rand(); +} + +// +// kexRand::Int +// + +int kexRand::Int(void) +{ + seed = 1479838765 - 1471521965 * seed; + return seed & RANDOM_MAX; +} + +// +// kexRand::Max +// + +int kexRand::Max(const int max) +{ + if(max == 0) + { + return 0; + } + + return Int() % max; +} + +// +// kexRand::Float +// + +float kexRand::Float(void) +{ + return (float)Max(RANDOM_MAX+1) / ((float)RANDOM_MAX+1); +} + +// +// kexRand::CFloat +// + +float kexRand::CFloat(void) +{ + return (float)(Max(20000) - 10000) * 0.0001f; +} + diff --git a/src/lightmap/kexlib/math/vector.cpp b/src/lightmap/kexlib/math/vector.cpp new file mode 100644 index 0000000..f7ff437 --- /dev/null +++ b/src/lightmap/kexlib/math/vector.cpp @@ -0,0 +1,1268 @@ +//----------------------------------------------------------------------------- +// 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: Vector operations +// +//----------------------------------------------------------------------------- + +#include +#include "mathlib.h" + +const kexVec3 kexVec3::vecRight(1, 0, 0); +const kexVec3 kexVec3::vecUp(0, 0, 1); +const kexVec3 kexVec3::vecForward(0, 1, 0); + +const kexVec2 kexVec2::vecRight(1, 0); +const kexVec2 kexVec2::vecUp(0, 1); +kexVec2 kexVec2::vecZero(0, 0); + +// +// kexVec2::kexVec2 +// + +kexVec2::kexVec2(void) +{ + Clear(); +} + +// +// kexVec2::kexVec2 +// + +kexVec2::kexVec2(const float x, const float y) +{ + Set(x, y); +} + +// +// kexVec2::Set +// + +void kexVec2::Set(const float x, const float y) +{ + this->x = x; + this->y = y; +} + +// +// kexVec2::Clear +// + +void kexVec2::Clear(void) +{ + x = y = 0.0f; +} + +// +// kexVec2::Dot +// + +float kexVec2::Dot(const kexVec2 &vec) const +{ + return (x * vec.x + y * vec.y); +} + +// +// kexVec2::Dot +// + +float kexVec2::Dot(const kexVec2 &vec1, const kexVec2 &vec2) +{ + return (vec1.x * vec2.x + vec1.y * vec2.y); +} + +// +// kexVec2::Dot +// + +float kexVec2::Dot(const kexVec3 &vec) const +{ + return (x * vec.x + y * vec.y); +} + +// +// kexVec2::Dot +// + +float kexVec2::Dot(const kexVec3 &vec1, const kexVec3 &vec2) +{ + return (vec1.x * vec2.x + vec1.y * vec2.y); +} + +// +// kexVec2::Cross +// + +float kexVec2::CrossScalar(const kexVec2 &vec) const +{ + return vec.x * y - vec.y * x; +} + +// +// kexVec2::Cross +// + +kexVec2 kexVec2::Cross(const kexVec2 &vec) const +{ + return kexVec2( + vec.y - y, + x - vec.x + ); +} + +// +// kexVec2::Cross +// + +kexVec2 &kexVec2::Cross(const kexVec2 &vec1, const kexVec2 &vec2) +{ + x = vec2.y - vec1.y; + y = vec1.x - vec2.x; + + return *this; +} + +// +// kexVec2::Cross +// + +kexVec2 kexVec2::Cross(const kexVec3 &vec) const +{ + return kexVec2( + vec.y - y, + x - vec.x + ); +} + +// +// kexVec2::Cross +// + +kexVec2 &kexVec2::Cross(const kexVec3 &vec1, const kexVec3 &vec2) +{ + x = vec2.y - vec1.y; + y = vec1.x - vec2.x; + + return *this; +} + +// +// kexVec2::UnitSq +// + +float kexVec2::UnitSq(void) const +{ + return x * x + y * y; +} + +// +// kexVec2::Unit +// + +float kexVec2::Unit(void) const +{ + return kexMath::Sqrt(UnitSq()); +} + +// +// kexVec2::DistanceSq +// + +float kexVec2::DistanceSq(const kexVec2 &vec) const +{ + return ( + (x - vec.x) * (x - vec.x) + + (y - vec.y) * (y - vec.y) + ); +} + +// +// kexVec2::Distance +// + +float kexVec2::Distance(const kexVec2 &vec) const +{ + return kexMath::Sqrt(DistanceSq(vec)); +} + +// +// kexVec2::Normalize +// + +kexVec2 &kexVec2::Normalize(void) +{ + *this *= kexMath::InvSqrt(UnitSq()); + return *this; +} + +// +// kexVec2::Lerp +// + +kexVec2 kexVec2::Lerp(const kexVec2 &next, float movement) const +{ + return (next - *this) * movement + *this; +} + +// +// kexVec2::Lerp +// + +kexVec2 &kexVec2::Lerp(const kexVec2 &next, const float movement) +{ + *this = (next - *this) * movement + *this; + return *this; +} + +// +// kexVec2::Lerp +// + +kexVec2 &kexVec2::Lerp(const kexVec2 &start, const kexVec2 &next, float movement) +{ + *this = (next - start) * movement + start; + return *this; +} + +// +// kexVec2::ToYaw +// + +float kexVec2::ToYaw(void) const +{ + float d = x * x + y * y; + + if(d == 0.0f) + { + return 0.0f; + } + + return kexMath::ATan2(x, y); +} + +// +// kexVec2::ToString +// + +kexStr kexVec2::ToString(void) const +{ + kexStr str; + str = str + x + " " + y; + return str; +} + +// +// kexVec2::ToFloatPtr +// + +float *kexVec2::ToFloatPtr(void) +{ + return reinterpret_cast(&x); +} + +// +// kexVec2::ToVec3 +// + +kexVec3 kexVec2::ToVec3(void) +{ + return kexVec3(x, y, 0); +} + +// +// kexVec2::operator+ +// + +kexVec2 kexVec2::operator+(const kexVec2 &vec) +{ + return kexVec2(x + vec.x, y + vec.y); +} + +// +// kexVec2::operator+ +// + +kexVec2 kexVec2::operator+(const kexVec2 &vec) const +{ + return kexVec2(x + vec.x, y + vec.y); +} + +// +// kexVec2::operator+ +// + +kexVec2 kexVec2::operator+(kexVec2 &vec) +{ + return kexVec2(x + vec.x, y + vec.y); +} + +// +// kexVec2::operator+= +// + +kexVec2 &kexVec2::operator+=(const kexVec2 &vec) +{ + x += vec.x; + y += vec.y; + return *this; +} + +// +// kexVec2::operator- +// + +kexVec2 kexVec2::operator-(const kexVec2 &vec) const +{ + return kexVec2(x - vec.x, y - vec.y); +} + +// +// kexVec2::operator- +// + +kexVec2 kexVec2::operator-(void) const +{ + return kexVec2(-x, -y); +} + +// +// kexVec2::operator-= +// + +kexVec2 &kexVec2::operator-=(const kexVec2 &vec) +{ + x -= vec.x; + y -= vec.y; + return *this; +} + +// +// kexVec2::operator* +// + +kexVec2 kexVec2::operator*(const kexVec2 &vec) +{ + return kexVec2(x * vec.x, y * vec.y); +} + +// +// kexVec2::operator*= +// + +kexVec2 &kexVec2::operator*=(const kexVec2 &vec) +{ + x *= vec.x; + y *= vec.y; + return *this; +} + +// +// kexVec2::operator* +// + +kexVec2 kexVec2::operator*(const float val) +{ + return kexVec2(x * val, y * val); +} + +// +// kexVec2::operator* +// + +kexVec2 kexVec2::operator*(const float val) const +{ + return kexVec2(x * val, y * val); +} + +// +// kexVec2::operator*= +// + +kexVec2 &kexVec2::operator*=(const float val) +{ + x *= val; + y *= val; + return *this; +} + +// +// kexVec2::operator/ +// + +kexVec2 kexVec2::operator/(const kexVec2 &vec) +{ + return kexVec2(x / vec.x, y / vec.y); +} + +// +// kexVec2::operator/= +// + +kexVec2 &kexVec2::operator/=(const kexVec2 &vec) +{ + x /= vec.x; + y /= vec.y; + return *this; +} + +// +// kexVec2::operator/ +// + +kexVec2 kexVec2::operator/(const float val) +{ + return kexVec2(x / val, y / val); +} + +// +// kexVec2::operator/= +// + +kexVec2 &kexVec2::operator/=(const float val) +{ + x /= val; + y /= val; + return *this; +} + +// +// kexVec2::operator* +// + +kexVec2 kexVec2::operator*(const kexMatrix &mtx) +{ + return kexVec2(mtx.vectors[1].x * y + mtx.vectors[0].x * x + mtx.vectors[3].x, + mtx.vectors[1].y * y + mtx.vectors[0].y * x + mtx.vectors[3].y); +} + +// +// kexVec2::operator* +// + +kexVec2 kexVec2::operator*(const kexMatrix &mtx) const +{ + return kexVec2(mtx.vectors[1].x * y + mtx.vectors[0].x * x + mtx.vectors[3].x, + mtx.vectors[1].y * y + mtx.vectors[0].y * x + mtx.vectors[3].y); +} + +// +// kexVec2::operator*= +// + +kexVec2 &kexVec2::operator*=(const kexMatrix &mtx) +{ + float _x = x; + float _y = y; + + x = mtx.vectors[1].x * _y + mtx.vectors[0].x * _x + mtx.vectors[3].x; + y = mtx.vectors[1].y * _y + mtx.vectors[0].y * _x + mtx.vectors[3].y; + + return *this; +} + +// +// kexVec2::operator= +// + +kexVec2 &kexVec2::operator=(const kexVec2 &vec) +{ + x = vec.x; + y = vec.y; + return *this; +} + +// +// kexVec2::operator= +// + +kexVec2 &kexVec2::operator=(const kexVec3 &vec) +{ + x = vec.x; + y = vec.y; + return *this; +} + +// +// kexVec2::operator= +// + +kexVec2 &kexVec2::operator=(kexVec3 &vec) +{ + x = vec.x; + y = vec.y; + return *this; +} + +// +// kexVec2::operator= +// + +kexVec2 &kexVec2::operator=(const float *vecs) +{ + x = vecs[0]; + y = vecs[2]; + return *this; +} + +// +// kexVec2::operator[] +// + +float kexVec2::operator[](int index) const +{ + assert(index >= 0 && index < 2); + return (&x)[index]; +} + +// +// kexVec2::operator[] +// + +float &kexVec2::operator[](int index) +{ + assert(index >= 0 && index < 2); + return (&x)[index]; +} + +// +// kexVec2::operator== +// + +bool kexVec2::operator==(const kexVec2 &vec) +{ + return ((x == vec.x) && (y == vec.y)); +} + +// +// kexVec3::kexVec3 +// + +kexVec3::kexVec3(void) +{ + Clear(); +} + +// +// kexVec3::kexVec3 +// + +kexVec3::kexVec3(const float x, const float y, const float z) +{ + Set(x, y, z); +} + +// +// kexVec3::Set +// + +void kexVec3::Set(const float x, const float y, const float z) +{ + this->x = x; + this->y = y; + this->z = z; +} + +// +// kexVec3::Clear +// + +void kexVec3::Clear(void) +{ + x = y = z = 0.0f; +} + +// +// kexVec3::Dot +// + +float kexVec3::Dot(const kexVec3 &vec) const +{ + return (x * vec.x + y * vec.y + z * vec.z); +} + +// +// kexVec3::Dot +// + +float kexVec3::Dot(const kexVec3 &vec1, const kexVec3 &vec2) +{ + return (vec1.x * vec2.x + vec1.y * vec2.y + vec1.z * vec2.z); +} + +// +// kexVec3::Cross +// + +kexVec3 kexVec3::Cross(const kexVec3 &vec) const +{ + return kexVec3( + vec.z * y - z * vec.y, + vec.x * z - x * vec.z, + x * vec.y - vec.x * y + ); +} + +// +// kexVec3::Cross +// + +kexVec3 &kexVec3::Cross(const kexVec3 &vec1, const kexVec3 &vec2) +{ + x = vec2.z * vec1.y - vec1.z * vec2.y; + y = vec2.x * vec1.z - vec1.x * vec2.z; + z = vec1.x * vec2.y - vec2.x * vec1.y; + + return *this; +} + +// +// kexVec3::UnitSq +// + +float kexVec3::UnitSq(void) const +{ + return x * x + y * y + z * z; +} + +// +// kexVec3::Unit +// + +float kexVec3::Unit(void) const +{ + return kexMath::Sqrt(UnitSq()); +} + +// +// kexVec3::DistanceSq +// + +float kexVec3::DistanceSq(const kexVec3 &vec) const +{ + return ( + (x - vec.x) * (x - vec.x) + + (y - vec.y) * (y - vec.y) + + (z - vec.z) * (z - vec.z) + ); +} + +// +// kexVec3::Distance +// + +float kexVec3::Distance(const kexVec3 &vec) const +{ + return kexMath::Sqrt(DistanceSq(vec)); +} + +// +// kexVec3::Normalize +// + +kexVec3 &kexVec3::Normalize(void) +{ + *this *= kexMath::InvSqrt(UnitSq()); + return *this; +} + +// +// kexVec3::PointAt +// + +kexAngle kexVec3::PointAt(kexVec3 &location) const +{ + kexVec3 dir = (*this - location).Normalize(); + + return kexAngle(dir.ToYaw(), dir.ToPitch(), 0); +} + +// +// kexVec3::Lerp +// + +kexVec3 kexVec3::Lerp(const kexVec3 &next, float movement) const +{ + return (next - *this) * movement + *this; +} + +// +// kexVec3::Lerp +// + +kexVec3 &kexVec3::Lerp(const kexVec3 &start, const kexVec3 &next, float movement) +{ + *this = (next - start) * movement + start; + return *this; +} + +// +// kexVec3::ToQuat +// + +kexQuat kexVec3::ToQuat(void) +{ + kexVec3 scv = *this * kexMath::InvSqrt(UnitSq()); + return kexQuat(kexMath::ACos(scv.z), vecForward.Cross(scv).Normalize()); +} + +// +// kexVec3::ToYaw +// + +float kexVec3::ToYaw(void) const +{ + float d = x * x + z * z; + + if(d == 0.0f) + { + return 0.0f; + } + + return kexMath::ATan2(x, z); +} + +// +// kexVec3::ToPitch +// + +float kexVec3::ToPitch(void) const +{ + float d = x * x + z * z; + + if(d == 0.0f) + { + if(y > 0.0f) + { + return DEG2RAD(90); + } + else + { + return DEG2RAD(-90); + } + } + + return kexMath::ATan2(y, d); +} + +// +// kexVec3::ToString +// + +kexStr kexVec3::ToString(void) const +{ + kexStr str; + str = str + x + " " + y + " " + z; + return str; +} + +// +// kexVec3::ToFloatPtr +// + +float *kexVec3::ToFloatPtr(void) +{ + return reinterpret_cast(&x); +} + +// +// kexVec3::ToVec2 +// + +kexVec2 kexVec3::ToVec2(void) +{ + return kexVec2(x, y); +} + +// +// kexVec3::ToVec2 +// + +kexVec2 kexVec3::ToVec2(void) const +{ + return kexVec2(x, y); +} + +// +// kexVec3::ScreenProject +// + +kexVec3 kexVec3::ScreenProject(kexMatrix &proj, kexMatrix &model, + const int width, const int height, + const int wx, const int wy) +{ + kexVec4 projVec; + kexVec4 modelVec; + + modelVec.ToVec3() = *this; + modelVec |= model; + + projVec = (modelVec | proj); + projVec.x *= modelVec.w; + projVec.y *= modelVec.w; + projVec.z *= modelVec.w; + + if(projVec.w != 0) + { + projVec.w = 1.0f / projVec.w; + projVec.x *= projVec.w; + projVec.y *= projVec.w; + projVec.z *= projVec.w; + + return kexVec3( + (projVec.x * 0.5f + 0.5f) * width + wx, + (-projVec.y * 0.5f + 0.5f) * height + wy, + (1.0f + projVec.z) * 0.5f); + } + + return kexVec3(*this); +} + +// +// kexVec3::operator+ +// + +kexVec3 kexVec3::operator+(const kexVec3 &vec) +{ + return kexVec3(x + vec.x, y + vec.y, z + vec.z); +} + +// +// kexVec3::operator+ +// + +kexVec3 kexVec3::operator+(const kexVec3 &vec) const +{ + return kexVec3(x + vec.x, y + vec.y, z + vec.z); +} + +// +// kexVec3::operator+ +// + +kexVec3 kexVec3::operator+(kexVec3 &vec) +{ + return kexVec3(x + vec.x, y + vec.y, z + vec.z); +} + +// +// kexVec3::operator+= +// + +kexVec3 &kexVec3::operator+=(const kexVec3 &vec) +{ + x += vec.x; + y += vec.y; + z += vec.z; + return *this; +} + +// +// kexVec3::operator- +// + +kexVec3 kexVec3::operator-(const kexVec3 &vec) const +{ + return kexVec3(x - vec.x, y - vec.y, z - vec.z); +} + +// +// kexVec3::operator- +// + +kexVec3 kexVec3::operator-(void) const +{ + return kexVec3(-x, -y, -z); +} + +// +// kexVec3::operator-= +// + +kexVec3 &kexVec3::operator-=(const kexVec3 &vec) +{ + x -= vec.x; + y -= vec.y; + z -= vec.z; + return *this; +} + +// +// kexVec3::operator* +// + +kexVec3 kexVec3::operator*(const kexVec3 &vec) +{ + return kexVec3(x * vec.x, y * vec.y, z * vec.z); +} + +// +// kexVec3::operator*= +// + +kexVec3 &kexVec3::operator*=(const kexVec3 &vec) +{ + x *= vec.x; + y *= vec.y; + z *= vec.z; + return *this; +} + +// +// kexVec3::operator* +// + +kexVec3 kexVec3::operator*(const float val) +{ + return kexVec3(x * val, y * val, z * val); +} + +// +// kexVec3::operator* +// + +kexVec3 kexVec3::operator*(const float val) const +{ + return kexVec3(x * val, y * val, z * val); +} + +// +// kexVec3::operator*= +// + +kexVec3 &kexVec3::operator*=(const float val) +{ + x *= val; + y *= val; + z *= val; + return *this; +} + +// +// kexVec3::operator/ +// + +kexVec3 kexVec3::operator/(const kexVec3 &vec) +{ + return kexVec3(x / vec.x, y / vec.y, z / vec.z); +} + +// +// kexVec3::operator/= +// + +kexVec3 &kexVec3::operator/=(const kexVec3 &vec) +{ + x /= vec.x; + y /= vec.y; + z /= vec.z; + return *this; +} + +// +// kexVec3::operator/ +// + +kexVec3 kexVec3::operator/(const float val) +{ + return kexVec3(x / val, y / val, z / val); +} + +// +// kexVec3::operator/= +// + +kexVec3 &kexVec3::operator/=(const float val) +{ + x /= val; + y /= val; + z /= val; + return *this; +} + +// +// kexVec3::operator* +// + +kexVec3 kexVec3::operator*(const kexQuat &quat) +{ + float xx = quat.x * quat.x; + float yx = quat.y * quat.x; + float zx = quat.z * quat.x; + float wx = quat.w * quat.x; + float yy = quat.y * quat.y; + float zy = quat.z * quat.y; + float wy = quat.w * quat.y; + float zz = quat.z * quat.z; + float wz = quat.w * quat.z; + float ww = quat.w * quat.w; + + return kexVec3( + ((yx + yx) - (wz + wz)) * y + + ((wy + wy + zx + zx)) * z + + (((ww + xx) - yy) - zz) * x, + ((yy + (ww - xx)) - zz) * y + + ((zy + zy) - (wx + wx)) * z + + ((wz + wz) + (yx + yx)) * x, + ((wx + wx) + (zy + zy)) * y + + (((ww - xx) - yy) + zz) * z + + ((zx + zx) - (wy + wy)) * x + ); +} + +// +// kexVec3::operator* +// + +kexVec3 kexVec3::operator*(const kexMatrix &mtx) +{ + return kexVec3(mtx.vectors[1].x * y + mtx.vectors[2].x * z + mtx.vectors[0].x * x + mtx.vectors[3].x, + mtx.vectors[1].y * y + mtx.vectors[2].y * z + mtx.vectors[0].y * x + mtx.vectors[3].y, + mtx.vectors[1].z * y + mtx.vectors[2].z * z + mtx.vectors[0].z * x + mtx.vectors[3].z); +} + +// +// kexVec3::operator* +// + +kexVec3 kexVec3::operator*(const kexMatrix &mtx) const +{ + return kexVec3(mtx.vectors[1].x * y + mtx.vectors[2].x * z + mtx.vectors[0].x * x + mtx.vectors[3].x, + mtx.vectors[1].y * y + mtx.vectors[2].y * z + mtx.vectors[0].y * x + mtx.vectors[3].y, + mtx.vectors[1].z * y + mtx.vectors[2].z * z + mtx.vectors[0].z * x + mtx.vectors[3].z); +} + +// +// kexVec3::operator*= +// + +kexVec3 &kexVec3::operator*=(const kexMatrix &mtx) +{ + float _x = x; + float _y = y; + float _z = z; + + x = mtx.vectors[1].x * _y + mtx.vectors[2].x * _z + mtx.vectors[0].x * _x + mtx.vectors[3].x; + y = mtx.vectors[1].y * _y + mtx.vectors[2].y * _z + mtx.vectors[0].y * _x + mtx.vectors[3].y; + z = mtx.vectors[1].z * _y + mtx.vectors[2].z * _z + mtx.vectors[0].z * _x + mtx.vectors[3].z; + + return *this; +} + +// +// kexVec3::operator*= +// + +kexVec3 &kexVec3::operator*=(const kexQuat &quat) +{ + float xx = quat.x * quat.x; + float yx = quat.y * quat.x; + float zx = quat.z * quat.x; + float wx = quat.w * quat.x; + float yy = quat.y * quat.y; + float zy = quat.z * quat.y; + float wy = quat.w * quat.y; + float zz = quat.z * quat.z; + float wz = quat.w * quat.z; + float ww = quat.w * quat.w; + float vx = x; + float vy = y; + float vz = z; + + x = ((yx + yx) - (wz + wz)) * vy + + ((wy + wy + zx + zx)) * vz + + (((ww + xx) - yy) - zz) * vx; + y = ((yy + (ww - xx)) - zz) * vy + + ((zy + zy) - (wx + wx)) * vz + + ((wz + wz) + (yx + yx)) * vx; + z = ((wx + wx) + (zy + zy)) * vy + + (((ww - xx) - yy) + zz) * vz + + ((zx + zx) - (wy + wy)) * vx; + + return *this; +} + +// +// kexVec3::operator= +// + +kexVec3 &kexVec3::operator=(const kexVec3 &vec) +{ + x = vec.x; + y = vec.y; + z = vec.z; + return *this; +} + +// +// kexVec3::operator= +// + +kexVec3 &kexVec3::operator=(const float *vecs) +{ + x = vecs[0]; + y = vecs[1]; + z = vecs[2]; + return *this; +} + +// +// kexVec3::operator[] +// + +float kexVec3::operator[](int index) const +{ + assert(index >= 0 && index < 3); + return (&x)[index]; +} + +// +// kexVec3::operator[] +// + +float &kexVec3::operator[](int index) +{ + assert(index >= 0 && index < 3); + return (&x)[index]; +} + +// +// kexVec4::kexVec4 +// + +kexVec4::kexVec4(void) +{ + Clear(); +} + +// +// kexVec4::kexVec4 +// + +kexVec4::kexVec4(const float x, const float y, const float z, const float w) +{ + Set(x, y, z, w); +} + +// +// kexVec4::Set +// + +void kexVec4::Set(const float x, const float y, const float z, const float w) +{ + this->x = x; + this->y = y; + this->z = z; + this->w = w; +} + +// +// kexVec4::Clear +// + +void kexVec4::Clear(void) +{ + x = y = z = w = 0.0f; +} + +// +// kexVec4::ToVec3 +// + +kexVec3 const &kexVec4::ToVec3(void) const +{ + return *reinterpret_cast(this); +} + +// +// kexVec4::ToVec3 +// + +kexVec3 &kexVec4::ToVec3(void) +{ + return *reinterpret_cast(this); +} + +// +// kexVec4::ToFloatPtr +// + +float *kexVec4::ToFloatPtr(void) +{ + return reinterpret_cast(&x); +} + +// +// kexVec4::operator| +// + +kexVec4 kexVec4::operator|(const kexMatrix &mtx) +{ + return kexVec4( + mtx.vectors[1].x * y + mtx.vectors[2].x * z + mtx.vectors[0].x * x + mtx.vectors[3].x, + mtx.vectors[1].y * y + mtx.vectors[2].y * z + mtx.vectors[0].y * x + mtx.vectors[3].y, + mtx.vectors[1].z * y + mtx.vectors[2].z * z + mtx.vectors[0].z * x + mtx.vectors[3].z, + mtx.vectors[1].w * y + mtx.vectors[2].w * z + mtx.vectors[0].w * x + mtx.vectors[3].w); +} + +// +// kexVec4::operator|= +// + +kexVec4 &kexVec4::operator|=(const kexMatrix &mtx) +{ + float _x = x; + float _y = y; + float _z = z; + + x = mtx.vectors[1].x * _y + mtx.vectors[2].x * _z + mtx.vectors[0].x * _x + mtx.vectors[3].x; + y = mtx.vectors[1].y * _y + mtx.vectors[2].y * _z + mtx.vectors[0].y * _x + mtx.vectors[3].y; + z = mtx.vectors[1].z * _y + mtx.vectors[2].z * _z + mtx.vectors[0].z * _x + mtx.vectors[3].z; + w = mtx.vectors[1].w * _y + mtx.vectors[2].w * _z + mtx.vectors[0].w * _x + mtx.vectors[3].w; + + return *this; +} + +// +// kexVec4::operator[] +// + +float kexVec4::operator[](int index) const +{ + assert(index >= 0 && index < 3); + return (&x)[index]; +} + +// +// kexVec4::operator[] +// + +float &kexVec4::operator[](int index) +{ + assert(index >= 0 && index < 3); + return (&x)[index]; +} diff --git a/src/lightmap/kexlib/memheap.cpp b/src/lightmap/kexlib/memheap.cpp new file mode 100644 index 0000000..cd18bd1 --- /dev/null +++ b/src/lightmap/kexlib/memheap.cpp @@ -0,0 +1,403 @@ +//----------------------------------------------------------------------------- +// 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: Memory Heap Management +// +//----------------------------------------------------------------------------- + +#include "lightmap/common.h" + +int kexHeap::numHeapBlocks = 0; +int kexHeap::currentHeapBlockID = -1; + +kexHeapBlock *kexHeap::currentHeapBlock = NULL; +kexHeapBlock *kexHeap::blockList = NULL; + +// +// common heap block types +// +kexHeapBlock hb_static("static", false, NULL, NULL); +kexHeapBlock hb_auto("auto", false, NULL, NULL); +kexHeapBlock hb_file("file", false, NULL, NULL); +kexHeapBlock hb_object("object", false, NULL, NULL); + +// +// kexHeapBlock::kexHeapBlock +// + +kexHeapBlock::kexHeapBlock(const char *name, bool bGarbageCollect, + blockFunc_t funcFree, blockFunc_t funcGC) +{ + this->name = (char*)name; + this->freeFunc = funcFree; + this->gcFunc = funcGC; + this->blocks = NULL; + this->bGC = bGarbageCollect; + this->purgeID = kexHeap::numHeapBlocks++; + + // add heap block to main block list + if(kexHeap::blockList) + { + if(kexHeap::blockList->prev) + { + kexHeap::blockList->prev->next = this; + this->prev = kexHeap::blockList->prev; + this->next = NULL; + kexHeap::blockList->prev = this; + } + else + { + kexHeap::blockList->prev = this; + kexHeap::blockList->next = this; + this->prev = kexHeap::blockList; + this->next = NULL; + } + + } + else + { + kexHeap::blockList = this; + this->prev = NULL; + this->next = NULL; + } +} + +// +// kexHeapBlock::~kexHeapBlock +// + +kexHeapBlock::~kexHeapBlock(void) +{ +} + +// +// kexHeapBlock::operator[] +// +// Should be used with kexHeap::blockList only +// + +kexHeapBlock *kexHeapBlock::operator[](int index) +{ + if(kexHeap::currentHeapBlockID == index) + { + return kexHeap::currentHeapBlock; + } + + kexHeapBlock *heapBlock = this; + + for(int i = 0; i < index; i++) + { + heapBlock = heapBlock->next; + if(heapBlock == NULL) + { + return NULL; + } + } + + kexHeap::currentHeapBlockID = index; + kexHeap::currentHeapBlock = heapBlock; + + return heapBlock; +} + +// +// kexHeap::AddBlock +// + +void kexHeap::AddBlock(memBlock_t *block, kexHeapBlock *heapBlock) +{ + block->prev = NULL; + block->next = heapBlock->blocks; + heapBlock->blocks = block; + + block->heapBlock = heapBlock; + + if(block->next != NULL) + { + block->next->prev = block; + } +} + +// +// kexHeap::RemoveBlock +// + +void kexHeap::RemoveBlock(memBlock_t *block) +{ + if(block->prev == NULL) + { + block->heapBlock->blocks = block->next; + } + else + { + block->prev->next = block->next; + } + + if(block->next != NULL) + { + block->next->prev = block->prev; + } + + block->heapBlock = NULL; +} + +// +// kexHeap::GetBlock +// + +memBlock_t *kexHeap::GetBlock(void *ptr, const char *file, int line) +{ + memBlock_t* block; + + block = (memBlock_t*)((byte*)ptr - sizeof(memBlock_t)); + + if(block->heapTag != kexHeap::HeapTag) + { + Error("kexHeap::GetBlock: found a pointer without heap tag (%s:%d)", file, line); + } + + return block; +} + +// +// kexHeap::Malloc +// + +void *kexHeap::Malloc(int size, kexHeapBlock &heapBlock, const char *file, int line) +{ + memBlock_t *newblock; + + assert(size > 0); + + newblock = NULL; + + if(!(newblock = (memBlock_t*)malloc(sizeof(memBlock_t) + size))) + { + Error("kexHeap::Malloc: failed on allocation of %u bytes (%s:%d)", size, file, line); + } + + newblock->purgeID = heapBlock.purgeID; + newblock->heapTag = kexHeap::HeapTag; + newblock->size = size; + newblock->ptrRef = NULL; + + kexHeap::AddBlock(newblock, &heapBlock); + + return ((byte*)newblock) + sizeof(memBlock_t); +} + +// +// kexHeap::Calloc +// + +void *kexHeap::Calloc(int size, kexHeapBlock &heapBlock, const char *file, int line) +{ + return memset(kexHeap::Malloc(size, heapBlock, file, line), 0, size); +} + +// +// kexHeap::Realloc +// + +void *kexHeap::Realloc(void *ptr, int size, kexHeapBlock &heapBlock, const char *file, int line) +{ + memBlock_t *block; + memBlock_t *newblock; + + if(!ptr) + { + return kexHeap::Malloc(size, heapBlock, file, line); + } + + assert(size >= 0); + + if(size == 0) + { + kexHeap::Free(ptr, file, line); + return NULL; + } + + block = kexHeap::GetBlock(ptr, file, line); + newblock = NULL; + + kexHeap::RemoveBlock(block); + + block->next = NULL; + block->prev = NULL; + + if(block->ptrRef) + { + *block->ptrRef = NULL; + } + + if(!(newblock = (memBlock_t*)realloc(block, sizeof(memBlock_t) + size))) + { + Error("kexHeap::Realloc: failed on allocation of %u bytes (%s:%d)", size, file, line); + } + + newblock->purgeID = heapBlock.purgeID; + newblock->heapTag = kexHeap::HeapTag; + newblock->size = size; + newblock->ptrRef = NULL; + + kexHeap::AddBlock(newblock, &heapBlock); + + return ((byte*)newblock) + sizeof(memBlock_t); +} + +// +// kexHeap::Alloca +// + +void *kexHeap::Alloca(int size, const char *file, int line) +{ + return size == 0 ? NULL : kexHeap::Calloc(size, hb_auto, file, line); +} + +// +// kexHeap::Free +// + +void kexHeap::Free(void *ptr, const char *file, int line) +{ + memBlock_t* block; + + block = kexHeap::GetBlock(ptr, file, line); + if(block->ptrRef) + { + *block->ptrRef = NULL; + } + + kexHeap::RemoveBlock(block); + + // free back to system + free(block); +} + +// +// kexHeap::Purge +// + +void kexHeap::Purge(kexHeapBlock &heapBlock, const char *file, int line) +{ + memBlock_t *block; + memBlock_t *next; + + for(block = heapBlock.blocks; block != NULL;) + { + next = block->next; + + if(block->heapTag != kexHeap::HeapTag) + { + Error("kexHeap::Purge: Purging without heap tag (%s:%d)", file, line); + } + + if(block->ptrRef) + { + *block->ptrRef = NULL; + } + + free(block); + block = next; + } + + heapBlock.blocks = NULL; +} + +// +// kexHeap::SetCacheRef +// + +void kexHeap::SetCacheRef(void **ptr, const char *file, int line) +{ + kexHeap::GetBlock(*ptr, file, line)->ptrRef = ptr; +} + +// +// kexHeap::GarbageCollect +// + +void kexHeap::GarbageCollect(const char *file, int line) +{ + kexHeap::Purge(hb_auto, file, line); +} + +// +// kexHeap::CheckBlocks +// + +void kexHeap::CheckBlocks(const char *file, int line) +{ + memBlock_t *block; + memBlock_t *prev; + kexHeapBlock *heapBlock; + + for(heapBlock = kexHeap::blockList; heapBlock; heapBlock = heapBlock->next) + { + prev = NULL; + + for(block = heapBlock->blocks; block != NULL; block = block->next) + { + if(block->heapTag != kexHeap::HeapTag) + { + Error("kexHeap::CheckBlocks: found block without heap tag (%s:%d)", file, line); + } + if(block->prev != prev) + { + Error("kexHeap::CheckBlocks: bad link list found (%s:%d)", file, line); + } + + prev = block; + } + } +} + +// +// kexHeap::Touch +// + +void kexHeap::Touch(void *ptr, const char *file, int line) +{ +} + +// +// kexHeap::Usage +// + +int kexHeap::Usage(const kexHeapBlock &heapBlock) +{ + int bytes = 0; + memBlock_t *block; + + for(block = heapBlock.blocks; block != NULL; block = block->next) + { + bytes += block->size; + } + + return bytes; +} diff --git a/src/lightmap/kexlib/memheap.h b/src/lightmap/kexlib/memheap.h new file mode 100644 index 0000000..01bfe6a --- /dev/null +++ b/src/lightmap/kexlib/memheap.h @@ -0,0 +1,114 @@ +//----------------------------------------------------------------------------- +// 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. +// + +#ifndef __MEM_HEAP_H__ +#define __MEM_HEAP_H__ + +typedef void (*blockFunc_t)(void*); + +class kexHeapBlock; + +typedef struct memBlock_s +{ + int heapTag; + int purgeID; + int size; + kexHeapBlock *heapBlock; + void **ptrRef; + struct memBlock_s *prev; + struct memBlock_s *next; +} memBlock_t; + +class kexHeapBlock +{ +public: + kexHeapBlock(const char *name, bool bGarbageCollect, + blockFunc_t funcFree, blockFunc_t funcGC); + ~kexHeapBlock(void); + + kexHeapBlock *operator[](int index); + + char *name; + memBlock_t *blocks; + bool bGC; + blockFunc_t freeFunc; + blockFunc_t gcFunc; + int purgeID; + kexHeapBlock *prev; + kexHeapBlock *next; +}; + +class kexHeap +{ +public: + static void *Malloc(int size, kexHeapBlock &heapBlock, const char *file, int line); + static void *Calloc(int size, kexHeapBlock &heapBlock, const char *file, int line); + static void *Realloc(void *ptr, int size, kexHeapBlock &heapBlock, const char *file, int line); + static void *Alloca(int size, const char *file, int line); + static void Free(void *ptr, const char *file, int line); + static void Purge(kexHeapBlock &heapBlock, const char *file, int line); + static void GarbageCollect(const char *file, int line); + static void CheckBlocks(const char *file, int line); + static void Touch(void *ptr, const char *file, int line); + static int Usage(const kexHeapBlock &heapBlock); + static void SetCacheRef(void **ptr, const char *file, int line); + + static int numHeapBlocks; + static kexHeapBlock *currentHeapBlock; + static int currentHeapBlockID; + static kexHeapBlock *blockList; + +private: + static void AddBlock(memBlock_t *block, kexHeapBlock *heapBlock); + static void RemoveBlock(memBlock_t *block); + static memBlock_t *GetBlock(void *ptr, const char *file, int line); + + static const int HeapTag = 0x03151983; +}; + +extern kexHeapBlock hb_static; +extern kexHeapBlock hb_auto; +extern kexHeapBlock hb_file; +extern kexHeapBlock hb_object; + +#define Mem_Malloc(s, hb) (kexHeap::Malloc(s, hb, __FILE__,__LINE__)) +#define Mem_Calloc(s, hb) (kexHeap::Calloc(s, hb, __FILE__,__LINE__)) +#define Mem_Realloc(p, s, hb) (kexHeap::Realloc(p, s, hb, __FILE__,__LINE__)) +#define Mem_Alloca(s) (kexHeap::Alloca(s, __FILE__,__LINE__)) +#define Mem_Free(p) (kexHeap::Free(p, __FILE__,__LINE__)) +#define Mem_Purge(hb) (kexHeap::Purge(hb, __FILE__,__LINE__)) +#define Mem_GC() (kexHeap::GarbageCollect(__FILE__,__LINE__)) +#define Mem_CheckBlocks() (kexHeap::CheckBlocks(__FILE__,__LINE__)) +#define Mem_Touch(p) (kexHeap::Touch(p, __FILE__,__LINE__)) +#define Mem_CacheRef(p) (kexHeap::SetCacheRef(p, __FILE__,__LINE__)) + +#define Mem_AllocStatic(s) (Mem_Calloc(s, hb_static)) + +#define Mem_Strdup(s, hb) (strcpy((char*)Mem_Malloc(strlen(s)+1, hb), s)) +#define Mem_Strdupa(s) (strcpy((char*)Mem_Alloca(strlen(s)+1), s)) + +#endif diff --git a/src/lightmap/kexlib/parser.cpp b/src/lightmap/kexlib/parser.cpp new file mode 100644 index 0000000..c4abbcd --- /dev/null +++ b/src/lightmap/kexlib/parser.cpp @@ -0,0 +1,1134 @@ +//----------------------------------------------------------------------------- +// 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: Script token/parser system +// +//----------------------------------------------------------------------------- + +#include "lightmap/common.h" +#include "parser.h" + +//#define SC_DEBUG + +#define COMMENT_NONE 0 +#define COMMENT_SINGLELINE 1 +#define COMMENT_MULTILINE 2 + +typedef enum +{ + CHAR_NUMBER, + CHAR_LETTER, + CHAR_SYMBOL, + CHAR_QUOTE, + CHAR_SPECIAL, + CHAR_EOF +} chartype_t; + +#ifdef SC_DEBUG + +// +// SC_DebugPrintf +// + +static void SC_DebugPrintf(const char *str, ...) +{ + char buf[1024]; + va_list v; + + if(!verbose) + { + return; + } + + va_start(v, str); + vsprintf(buf, str,v); + va_end(v); + + fprintf(debugfile, buf); +} +#endif + +static kexParser parserLocal; +kexParser *parser = &parserLocal; + +// +// kexLexer::kexLexer +// + +kexLexer::kexLexer(const char *filename, char *buf, int bufSize) +{ + buffer = buf; + buffsize = bufSize; + pointer_start = buffer; + pointer_end = buffer + buffsize; + linepos = 1; + rowpos = 1; + buffpos = 0; + tokentype = TK_NONE; + name = filename; +} + +// +// kexLexer::~kexLexer +// + +kexLexer::~kexLexer(void) +{ + if(buffer) + { + Mem_Free(buffer); + } + buffer = NULL; + buffsize = 0; + pointer_start = NULL; + pointer_end = NULL; + linepos = 0; + rowpos = 0; + buffpos = 0; +} + +// +// kexLexer::CheckState +// + +bool kexLexer::CheckState(void) +{ +#ifdef SC_DEBUG + SC_DebugPrintf("(%s): checking script state: %i : %i\n", + name, buffpos, buffsize); +#endif + + if(buffpos < buffsize) + { + return true; + } + + return false; +} + +// +// kexLexer::CheckKeywords +// + +void kexLexer::CheckKeywords(void) +{ + if(!kexStr::Compare(token, "define")) + { + tokentype = TK_DEFINE; + } + else if(!kexStr::Compare(token, "include")) + { + tokentype = TK_INCLUDE; + } + else if(!kexStr::Compare(token, "setdir")) + { + tokentype = TK_SETDIR; + } + else if(!kexStr::Compare(token, "undef")) + { + tokentype = TK_UNDEF; + } +} + +// +// kexLexer::ClearToken +// + +void kexLexer::ClearToken(void) +{ + tokentype = TK_NONE; + memset(token, 0, SC_TOKEN_LEN); +} + +// +// kexLexer::GetNumber +// + +int kexLexer::GetNumber(void) +{ +#ifdef SC_DEBUG + SC_DebugPrintf("get number (%s)\n", token); +#endif + + Find(); + + if(tokentype != TK_NUMBER) + { + parserLocal.HandleError("%s is not a number", token); + } + + return atoi(token); +} + +// +// kexLexer::GetFloat +// + +double kexLexer::GetFloat(void) +{ +#ifdef SC_DEBUG + SC_DebugPrintf("get float (%s)\n", token); +#endif + + Find(); + + if(tokentype != TK_NUMBER) + { + parserLocal.HandleError("%s is not a float", token); + } + + return atof(token); +} + +// +// kexLexer::GetVector3 +// + +kexVec3 kexLexer::GetVector3(void) +{ +#ifdef SC_DEBUG + SC_DebugPrintf("get vector3 (%s)\n", token); +#endif + + float x, y, z; + + ExpectNextToken(TK_LBRACK); + x = (float)GetFloat(); + y = (float)GetFloat(); + z = (float)GetFloat(); + ExpectNextToken(TK_RBRACK); + + return kexVec3(x, y, z); +} + +// +// kexLexer::GetVector4 +// + +kexVec4 kexLexer::GetVector4(void) +{ +#ifdef SC_DEBUG + SC_DebugPrintf("get vector4 (%s)\n", token); +#endif + + float x, y, z, w; + + ExpectNextToken(TK_LBRACK); + x = (float)GetFloat(); + y = (float)GetFloat(); + z = (float)GetFloat(); + w = (float)GetFloat(); + ExpectNextToken(TK_RBRACK); + + return kexVec4(x, y, z, w); +} + +// +// kexLexer::GetVectorString3 +// + +kexVec3 kexLexer::GetVectorString3(void) +{ + kexVec3 vec; + + GetString(); + sscanf(StringToken(), "%f %f %f", &vec.x, &vec.y, &vec.z); + + return vec; +} + +// +// kexLexer::GetVectorString4 +// + +kexVec4 kexLexer::GetVectorString4(void) +{ + kexVec4 vec; + + GetString(); + sscanf(StringToken(), "%f %f %f %f", &vec.x, &vec.y, &vec.z, &vec.w); + + return vec; +} + +// +// kexLexer::GetString +// + +void kexLexer::GetString(void) +{ + ExpectNextToken(TK_STRING); + strcpy(stringToken, token); +} + +// +// kexLexer::MustMatchToken +// + +void kexLexer::MustMatchToken(int type) +{ +#ifdef SC_DEBUG + SC_DebugPrintf("must match %i\n", type); + SC_DebugPrintf("tokentype %i\n", tokentype); +#endif + + if(tokentype != type) + { + const char *string; + + switch(type) + { + case TK_NUMBER: + string = "a number"; + break; + case TK_STRING: + string = "a string"; + break; + case TK_POUND: + string = "a pound sign"; + break; + case TK_COLON: + string = "a colon"; + break; + case TK_SEMICOLON: + string = "a semicolon"; + break; + case TK_LBRACK: + string = "{"; + break; + case TK_RBRACK: + string = "}"; + break; + case TK_LSQBRACK: + string = "["; + break; + case TK_RSQBRACK: + string = "]"; + break; + case TK_LPAREN: + string = "("; + break; + case TK_RPAREN: + string = ")"; + break; + case TK_COMMA: + string = "a comma"; + break; + default: + string = NULL; + parserLocal.HandleError("Invalid token: %s", token); + break; + } + + parserLocal.HandleError("Expected %s, but found: %s (%i : %i)", + string, token, tokentype, type); + } +} + +// +// kexLexer::ExpectNextToken +// + +void kexLexer::ExpectNextToken(int type) +{ +#ifdef SC_DEBUG + SC_DebugPrintf("expect %i\n", type); +#endif + Find(); + MustMatchToken(type); +} + +// +// kexLexer::GetNumberToken +// + +void kexLexer::GetNumberToken(char initial) +{ + int c = initial; + int i = 0; + + tokentype = TK_NUMBER; + + while(parserLocal.CharCode()[c] == CHAR_NUMBER) + { + token[i++] = c; + c = GetChar(); + } + +#ifdef SC_DEBUG + SC_DebugPrintf("get number (%s)\n", token); +#endif + + Rewind(); +} + +// +// kexLexer::GetLetterToken +// + +void kexLexer::GetLetterToken(char initial) +{ + int c = initial; + int i = 0; + bool haschar = false; + + while(parserLocal.CharCode()[c] == CHAR_LETTER || + (haschar && parserLocal.CharCode()[c] == CHAR_NUMBER)) + { + token[i++] = c; + c = GetChar(); + haschar = true; + } + + tokentype = TK_IDENIFIER; + +#ifdef SC_DEBUG + SC_DebugPrintf("get letter (%s)\n", token); +#endif + + Rewind(); + CheckKeywords(); +} + +// +// kexLexer::GetSymbolToken +// + +void kexLexer::GetSymbolToken(char c) +{ + switch(c) + { + case '#': + tokentype = TK_POUND; + token[0] = c; + break; + case ':': + tokentype = TK_COLON; + token[0] = c; + break; + case ';': + tokentype = TK_SEMICOLON; + token[0] = c; + break; + case '=': + tokentype = TK_EQUAL; + token[0] = c; + break; + case '.': + tokentype = TK_PERIOD; + token[0] = c; + break; + case '{': + tokentype = TK_LBRACK; + token[0] = c; + break; + case '}': + tokentype = TK_RBRACK; + token[0] = c; + break; + case '(': + tokentype = TK_LPAREN; + token[0] = c; + break; + case ')': + tokentype = TK_RPAREN; + token[0] = c; + break; + case '[': + tokentype = TK_LSQBRACK; + token[0] = c; + break; + case ']': + tokentype = TK_RSQBRACK; + token[0] = c; + break; + case ',': + tokentype = TK_COMMA; + token[0] = c; + break; + case '\'': + tokentype = TK_QUOTE; + token[0] = c; + break; + case '/': + tokentype = TK_FORWARDSLASH; + token[0] = c; + break; + default: + parserLocal.HandleError("Unknown symbol: %c", c); + break; + } + +#ifdef SC_DEBUG + SC_DebugPrintf("get symbol (%s)\n", token); +#endif +} + +// +// kexLexer::GetStringToken +// + +void kexLexer::GetStringToken(void) +{ + int i = 0; + char c = GetChar(); + + while(parserLocal.CharCode()[c] != CHAR_QUOTE) + { + token[i++] = c; + c = GetChar(); + } + + tokentype = TK_STRING; + +#ifdef SC_DEBUG + SC_DebugPrintf("get string (%s)\n", token); +#endif +} + +// +// kexLexer::Find +// + +bool kexLexer::Find(void) +{ + char c = 0; + int comment = COMMENT_NONE; + + ClearToken(); + + while(CheckState()) + { + c = GetChar(); + + if(comment == COMMENT_NONE) + { + if(c == '/') + { + char gc = GetChar(); + + if(gc != '/' && gc != '*') + { + Rewind(); + } + else + { + if(gc == '*') + { + comment = COMMENT_MULTILINE; + } + else + { + comment = COMMENT_SINGLELINE; + } + } + } + } + else if(comment == COMMENT_MULTILINE) + { + if(c == '*') + { + char gc = GetChar(); + + if(gc != '/') + { + Rewind(); + } + else + { + comment = COMMENT_NONE; + continue; + } + } + } + + if(comment == COMMENT_NONE) + { + byte bc = ((byte)c); + + if(parserLocal.CharCode()[bc] != CHAR_SPECIAL) + { + switch(parserLocal.CharCode()[bc]) + { + case CHAR_NUMBER: + GetNumberToken(c); + return true; + case CHAR_LETTER: + GetLetterToken(c); + return true; + case CHAR_QUOTE: + GetStringToken(); + return true; + case CHAR_SYMBOL: + GetSymbolToken(c); + return true; + case CHAR_EOF: + tokentype = TK_EOF; +#ifdef SC_DEBUG + SC_DebugPrintf("EOF token\n"); +#endif + return true; + default: + break; + } + } + } + + if(c == '\n') + { + linepos++; + rowpos = 1; + + if(comment == COMMENT_SINGLELINE) + { + comment = COMMENT_NONE; + } + } + } + + return false; +} + +// +// kexLexer::GetChar +// + +char kexLexer::GetChar(void) +{ + int c; + +#ifdef SC_DEBUG + SC_DebugPrintf("(%s): get char\n", name); +#endif + + rowpos++; + c = buffer[buffpos++]; + + if(parserLocal.CharCode()[c] == CHAR_EOF) + { + c = 0; + } + +#ifdef SC_DEBUG + SC_DebugPrintf("get char: %i\n", c); +#endif + + return c; +} + +// +// kexLexer::Matches +// + +bool kexLexer::Matches(const char *string) +{ + return !strcmp(token, string); +} + +// +// kexLexer::Rewind +// + +void kexLexer::Rewind(void) +{ +#ifdef SC_DEBUG + SC_DebugPrintf("(%s): rewind\n", name); +#endif + + rowpos--; + buffpos--; +} + +// +// kexLexer::SkipLine +// + +void kexLexer::SkipLine(void) +{ +#ifdef SC_DEBUG + SC_DebugPrintf("SkipLine\n"); +#endif + + int curline = linepos; + + while(CheckState()) + { + Find(); + + if(curline != linepos) + { + return; + } + } +} + +// +// kexLexer::GetIDForTokenList +// + +int kexLexer::GetIDForTokenList(const sctokens_t *tokenlist, const char *token) +{ + int i; + for(i = 0; tokenlist[i].id != -1; i++) + { + if(tokenlist[i].token == NULL) + { + continue; + } + + if(!strcmp(token, tokenlist[i].token)) + { + return tokenlist[i].id; + } + } + + return i; +} + +// +// kexLexer::ExpectTokenListID +// + +void kexLexer::ExpectTokenListID(const sctokens_t *tokenlist, int id) +{ + Find(); + if(GetIDForTokenList(tokenlist, token) != id) + { + parserLocal.HandleError("Expected \"%s\" but found %s", + tokenlist[id].token, token); + } +} + +// +// kexLexer::AssignFromTokenList +// + +void kexLexer::AssignFromTokenList(const sctokens_t *tokenlist, char *str, int id, bool expect) +{ + if(expect) + { + ExpectTokenListID(tokenlist, id); + } + ExpectNextToken(TK_EQUAL); + GetString(); + strcpy(str, stringToken); +} + +// +// kexLexer::AssignFromTokenList +// + +void kexLexer::AssignFromTokenList(const sctokens_t *tokenlist, unsigned int *var, int id, bool expect) +{ + if(expect) + { + ExpectTokenListID(tokenlist, id); + } + ExpectNextToken(TK_EQUAL); + *var = GetNumber(); +} + +// +// kexLexer::AssignFromTokenList +// + +void kexLexer::AssignFromTokenList(const sctokens_t *tokenlist, unsigned short *var, int id, bool expect) +{ + if(expect) + { + ExpectTokenListID(tokenlist, id); + } + ExpectNextToken(TK_EQUAL); + *var = GetNumber(); +} + +// +// kexLexer::AssignFromTokenList +// + +void kexLexer::AssignFromTokenList(const sctokens_t *tokenlist, float *var, int id, bool expect) +{ + if(expect) + { + ExpectTokenListID(tokenlist, id); + } + ExpectNextToken(TK_EQUAL); + *var = (float)GetFloat(); +} + +// +// kexLexer::AssignVectorFromTokenList +// + +void kexLexer::AssignVectorFromTokenList(const sctokens_t *tokenlist, float *var, int id, bool expect) +{ + if(expect) + { + ExpectTokenListID(tokenlist, id); + } + ExpectNextToken(TK_EQUAL); + ExpectNextToken(TK_LBRACK); + var[0] = (float)GetFloat(); + var[1] = (float)GetFloat(); + var[2] = (float)GetFloat(); + ExpectNextToken(TK_RBRACK); +} + +// +// kexLexer::AssignFromTokenList +// + +void kexLexer::AssignFromTokenList(const sctokens_t *tokenlist, arraytype_t type, + void **data, int count, int id, bool expect, kexHeapBlock &hb) +{ + void *buf; + + if(expect) + { + ExpectTokenListID(tokenlist, id); + } + else if(count <= 0) + { + parserLocal.HandleError("Parsing \"%s\" array with count = 0", + tokenlist[id].token); + } + + ExpectNextToken(TK_EQUAL); + ExpectNextToken(TK_LBRACK); + + buf = NULL; + + if(count <= 0) + { + // skip null arrays. note that parser will assume a -1 followed by a + // closing bracket + + Find(); // skip the -1 number + ExpectNextToken(TK_RBRACK); + } + else + { + int i; + size_t len; + + switch(type) + { + case AT_SHORT: + len = sizeof(short); + break; + case AT_INTEGER: + len = sizeof(int); + break; + case AT_FLOAT: + len = sizeof(float); + break; + case AT_DOUBLE: + len = sizeof(double); + break; + case AT_VECTOR: + len = sizeof(float) * 3; + break; + default: + break; + } + + buf = (void*)Mem_Malloc(len * count, hb); + + switch(type) + { + case AT_SHORT: + { + word *wbuf = (word*)buf; + + for(i = 0; i < count; i++) + { + wbuf[i] = GetNumber(); + } + } + break; + case AT_INTEGER: + { + int *ibuf = (int*)buf; + + for(i = 0; i < count; i++) + { + ibuf[i] = GetNumber(); + } + } + break; + case AT_FLOAT: + { + float *fbuf = (float*)buf; + + for(i = 0; i < count; i++) + { + fbuf[i] = (float)GetFloat(); + } + } + break; + case AT_DOUBLE: + { + double *dbuf = (double*)buf; + + for(i = 0; i < count; i++) + { + dbuf[i] = GetFloat(); + } + } + break; + case AT_VECTOR: + { + float *fbuf = (float*)buf; + for(i = 0; i < count; i++) + { + fbuf[i * 3 + 0] = (float)GetFloat(); + fbuf[i * 3 + 1] = (float)GetFloat(); + fbuf[i * 3 + 2] = (float)GetFloat(); + } + } + break; + default: + break; + } + + ExpectNextToken(TK_RBRACK); + } + + *data = buf; +} + +// +// kexParser::kexParser +// + +kexParser::kexParser(void) +{ + int i; + + numNestedFilenames = 0; + numLexers = 0; + + for(i = 0; i < 256; i++) + { + charcode[i] = CHAR_SPECIAL; + } + for(i = '!'; i <= '~'; i++) + { + charcode[i] = CHAR_SYMBOL; + } + for(i = '0'; i <= '9'; i++) + { + charcode[i] = CHAR_NUMBER; + } + for(i = 'A'; i <= 'Z'; i++) + { + charcode[i] = CHAR_LETTER; + } + for(i = 'a'; i <= 'z'; i++) + { + charcode[i] = CHAR_LETTER; + } + + charcode['"'] = CHAR_QUOTE; + charcode['_'] = CHAR_LETTER; + charcode['-'] = CHAR_NUMBER; + charcode['.'] = CHAR_NUMBER; + charcode[127] = CHAR_EOF; +} + +// +// kexParser::~kexParser +// + +kexParser::~kexParser(void) +{ +} + +// +// kexParser::GetNestedFileName +// + +const char *kexParser::GetNestedFileName(void) const +{ + if(numNestedFilenames <= 0) + { + return NULL; + } + + return nestedFilenames[numNestedFilenames-1]; +} + +// +// kexParser::PushFileName +// + +void kexParser::PushFileName(const char *name) +{ +#ifdef SC_DEBUG + SC_DebugPrintf("push nested file %s\n", name); +#endif + strcpy(nestedFilenames[numNestedFilenames++], name); +} + +// +// kexParser::PopFileName +// + +void kexParser::PopFileName(void) +{ +#ifdef SC_DEBUG + SC_DebugPrintf("nested file pop\n"); +#endif + memset(nestedFilenames[--numNestedFilenames], 0, 256); +} + +// +// kexParser::PushLexer +// + +void kexParser::PushLexer(const char *filename, char *buf, int bufSize) +{ + if(numLexers >= MAX_NESTED_PARSERS) + { + Error("Reached max number of nested lexers (%i)", numLexers); + } + + lexers[numLexers] = new kexLexer(filename, buf, bufSize); + currentLexer = lexers[numLexers]; + + numLexers++; +} + +// +// kexParser::PopLexer +// + +void kexParser::PopLexer(void) +{ + delete lexers[--numLexers]; + if(numLexers <= 0) + { + currentLexer = NULL; + } + else + { + currentLexer = lexers[numLexers - 1]; + } +} + +// +// kexParser::HandleError +// + +void kexParser::HandleError(const char *msg, ...) +{ + char buf[1024]; + va_list v; + + va_start(v,msg); + vsprintf(buf,msg,v); + va_end(v); + + Error("%s : %s\n(line = %i, pos = %i)", + GetNestedFileName(), + buf, currentLexer->LinePos(), currentLexer->RowPos()); +} + +// +// kexParser::OpenExternalFile +// + +int kexParser::OpenExternalFile(const char *name, byte **buffer) const +{ + kexStr fPath; + FILE *fp; + + fPath = name; + fPath.NormalizeSlashes(); + + if((fp = fopen(fPath.c_str(), "rb"))) + { + size_t length; + + fseek(fp, 0, SEEK_END); + length = ftell(fp); + fseek(fp, 0, SEEK_SET); + + *buffer = (byte*)Mem_Calloc(length+1, hb_file); + + if(fread(*buffer, 1, length, fp) == length) + { + fclose(fp); + return length; + } + + Mem_Free(*buffer); + *buffer = NULL; + fclose(fp); + } + + return -1; +} + +// +// kexParser::Open +// + +kexLexer *kexParser::Open(const char* filename) +{ +#ifdef SC_DEBUG + SC_DebugPrintf("opening %s\n", filename); +#endif + + int buffsize; + byte *buffer; + + buffsize = OpenExternalFile(filename, (byte**)(&buffer)); + + if(buffsize <= 0) + { + printf("kexParser::Open: %s not found\n", filename); + return NULL; + } + + // push out a new lexer + PushLexer(filename, (char*)buffer, buffsize); + PushFileName(filename); + + return currentLexer; +} + +// +// kexParser::Close +// + +void kexParser::Close(void) +{ + PopLexer(); + PopFileName(); +} diff --git a/src/lightmap/kexlib/parser.h b/src/lightmap/kexlib/parser.h new file mode 100644 index 0000000..80af970 --- /dev/null +++ b/src/lightmap/kexlib/parser.h @@ -0,0 +1,175 @@ +//----------------------------------------------------------------------------- +// 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. +// + +#ifndef __PARSER_H__ +#define __PARSER_H__ + +#define MAX_NESTED_PARSERS 128 +#define MAX_NESTED_FILENAMES 128 + +typedef enum +{ + TK_NONE, + TK_NUMBER, + TK_STRING, + TK_POUND, + TK_COLON, + TK_SEMICOLON, + TK_PERIOD, + TK_QUOTE, + TK_FORWARDSLASH, + TK_EQUAL, + TK_LBRACK, + TK_RBRACK, + TK_LPAREN, + TK_RPAREN, + TK_LSQBRACK, + TK_RSQBRACK, + TK_COMMA, + TK_IDENIFIER, + TK_DEFINE, + TK_UNDEF, + TK_INCLUDE, + TK_SETDIR, + TK_EOF +} tokentype_t; + +typedef enum +{ + AT_SHORT, + AT_INTEGER, + AT_FLOAT, + AT_DOUBLE, + AT_VECTOR +} arraytype_t; + +#define SC_TOKEN_LEN 512 + +typedef struct +{ + int id; + const char *token; +} sctokens_t; + +class kexLexer +{ +public: + kexLexer(const char *filename, char *buf, int bufSize); + ~kexLexer(void); + + bool CheckState(void); + void CheckKeywords(void); + void MustMatchToken(int type); + void ExpectNextToken(int type); + bool Find(void); + char GetChar(void); + void Rewind(void); + void SkipLine(void); + bool Matches(const char *string); + int GetNumber(void); + double GetFloat(void); + kexVec3 GetVector3(void); + kexVec4 GetVector4(void); + kexVec3 GetVectorString3(void); + kexVec4 GetVectorString4(void); + void GetString(void); + int GetIDForTokenList(const sctokens_t *tokenlist, const char *token); + void ExpectTokenListID(const sctokens_t *tokenlist, int id); + void AssignFromTokenList(const sctokens_t *tokenlist, + char *str, int id, bool expect); + void AssignFromTokenList(const sctokens_t *tokenlist, + unsigned int *var, int id, bool expect); + void AssignFromTokenList(const sctokens_t *tokenlist, + unsigned short *var, int id, bool expect); + void AssignFromTokenList(const sctokens_t *tokenlist, + float *var, int id, bool expect); + void AssignVectorFromTokenList(const sctokens_t *tokenlist, + float *var, int id, bool expect); + void AssignFromTokenList(const sctokens_t *tokenlist, + arraytype_t type, void **data, int count, + int id, bool expect, kexHeapBlock &hb); + + int LinePos(void) { return linepos; } + int RowPos(void) { return rowpos; } + int BufferPos(void) { return buffpos; } + int BufferSize(void) { return buffsize; } + char *Buffer(void) { return buffer; } + char *StringToken(void) { return stringToken; } + const char *Token(void) const { return token; } + const int TokenType(void) const { return tokentype; } + +private: + void ClearToken(void); + void GetNumberToken(char initial); + void GetLetterToken(char initial); + void GetSymbolToken(char c); + void GetStringToken(void); + + char token[SC_TOKEN_LEN]; + char stringToken[MAX_FILEPATH]; + char* buffer; + char* pointer_start; + char* pointer_end; + int linepos; + int rowpos; + int buffpos; + int buffsize; + int tokentype; + const char *name; +}; + +class kexParser +{ +public: + kexParser(void); + ~kexParser(void); + + kexLexer *Open(const char *filename); + void Close(void); + void HandleError(const char *msg, ...); + void PushLexer(const char *filename, char *buf, int bufSize); + void PopLexer(void); + void PushFileName(const char *name); + void PopFileName(void); + byte *CharCode(void) { return charcode; } + const kexLexer *CurrentLexer(void) const { return currentLexer; } + +private: + int OpenExternalFile(const char *name, byte **buffer) const; + const char *GetNestedFileName(void) const; + + kexLexer *currentLexer; + kexLexer *lexers[MAX_NESTED_PARSERS]; + int numLexers; + byte charcode[256]; + char nestedFilenames[MAX_NESTED_FILENAMES][MAX_FILEPATH]; + int numNestedFilenames; +}; + +extern kexParser *parser; + +#endif diff --git a/src/lightmap/lightmap.cpp b/src/lightmap/lightmap.cpp new file mode 100644 index 0000000..3091846 --- /dev/null +++ b/src/lightmap/lightmap.cpp @@ -0,0 +1,1213 @@ +//----------------------------------------------------------------------------- +// 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: Lightmap and lightgrid building module +// +//----------------------------------------------------------------------------- + +#include "common.h" +#include "surfaces.h" +#include "trace.h" +#include "mapData.h" +#include "lightmap.h" +#include "worker.h" +#include "kexlib/binFile.h" + +//#define EXPORT_TEXELS_OBJ + +kexWorker lightmapWorker; + +const kexVec3 kexLightmapBuilder::gridSize(64, 64, 128); + +// +// LightmapWorkerFunc +// + +static void LightmapWorkerFunc(void *data, int id) +{ + kexLightmapBuilder *builder = static_cast(data); + builder->LightSurface(id); +} + +// +// LightGridWorkerFunc +// + +static void LightGridWorkerFunc(void *data, int id) +{ + kexLightmapBuilder *builder = static_cast(data); + builder->LightGrid(id); +} + +// +// kexLightmapBuilder::kexLightmapBuilder +// + +kexLightmapBuilder::kexLightmapBuilder(void) +{ + this->textureWidth = 128; + this->textureHeight = 128; + this->allocBlocks = NULL; + this->numTextures = 0; + this->samples = 16; + this->extraSamples = 2; + this->ambience = 0.0f; + this->tracedTexels = 0; + this->gridMap = NULL; +} + +// +// kexLightmapBuilder::~kexLightmapBuilder +// + +kexLightmapBuilder::~kexLightmapBuilder(void) +{ +} + +// +// kexLightmapBuilder::NewTexture +// +// Allocates a new texture pointer +// + +void kexLightmapBuilder::NewTexture(void) +{ + numTextures++; + + allocBlocks = (int**)Mem_Realloc(allocBlocks, sizeof(int*) * numTextures, hb_static); + allocBlocks[numTextures-1] = (int*)Mem_Calloc(sizeof(int) * textureWidth, hb_static); + + memset(allocBlocks[numTextures-1], 0, sizeof(int) * textureWidth); + + byte *texture = (byte*)Mem_Calloc((textureWidth * textureHeight) * 3, hb_static); + textures.Push(texture); +} + +// +// kexLightMapBuilder::MakeRoomForBlock +// +// Determines where to map a new block on to +// the lightmap texture +// + +bool kexLightmapBuilder::MakeRoomForBlock(const int width, const int height, + int *x, int *y, int *num) +{ + int i; + int j; + int k; + int bestRow1; + int bestRow2; + + *num = -1; + + if(allocBlocks == NULL) + { + return false; + } + + for(k = 0; k < numTextures; ++k) + { + bestRow1 = textureHeight; + + for(i = 0; i <= textureWidth - width; i++) + { + bestRow2 = 0; + + for(j = 0; j < width; j++) + { + if(allocBlocks[k][i + j] >= bestRow1) + { + break; + } + + if(allocBlocks[k][i + j] > bestRow2) + { + bestRow2 = allocBlocks[k][i + j]; + } + } + + // found a free block + if(j == width) + { + *x = i; + *y = bestRow1 = bestRow2; + } + } + + if(bestRow1 + height > textureHeight) + { + // no room + continue; + } + + for(i = 0; i < width; i++) + { + // store row offset + allocBlocks[k][*x + i] = bestRow1 + height; + } + + *num = k; + return true; + } + + return false; +} + +// +// kexLightmapBuilder::GetBoundsFromSurface +// + +kexBBox kexLightmapBuilder::GetBoundsFromSurface(const surface_t *surface) +{ + kexVec3 low(M_INFINITY, M_INFINITY, M_INFINITY); + kexVec3 hi(-M_INFINITY, -M_INFINITY, -M_INFINITY); + + kexBBox bounds; + bounds.Clear(); + + for(int i = 0; i < surface->numVerts; i++) + { + for(int j = 0; j < 3; j++) + { + if(surface->verts[i][j] < low[j]) + { + low[j] = surface->verts[i][j]; + } + if(surface->verts[i][j] > hi[j]) + { + hi[j] = surface->verts[i][j]; + } + } + } + + bounds.min = low; + bounds.max = hi; + + return bounds; +} + +// +// kexLightmapBuilder::EmitFromCeiling +// +// Traces to the ceiling surface. Will emit +// 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) +{ + *dist = normal.Dot(map->GetSunDirection()); + + if(*dist <= 0) + { + // plane is not even facing the sunlight + return false; + } + + trace.Trace(origin, origin + (map->GetSunDirection() * 32768)); + + if(trace.fraction == 1 || trace.hitSurface == NULL) + { + // nothing was hit + return false; + } + + if(trace.hitSurface->bSky == false) + { + // not a ceiling/sky surface + return false; + } + + return true; +} + +// +// kexLightmapBuilder::LightTexelSample +// +// Traces a line from the texel's origin to the sunlight direction +// and against all nearby thing lights +// + +kexVec3 kexLightmapBuilder::LightTexelSample(kexTrace &trace, const kexVec3 &origin, surface_t *surface) +{ + kexVec3 lightOrigin; + kexVec3 dir; + kexVec3 color; + kexPlane plane; + float dist; + float radius; + float intensity; + float colorAdd; + + plane = surface->plane; + color.Clear(); + + // check all thing lights + for(unsigned int i = 0; i < map->thingLights.Length(); i++) + { + thingLight_t *tl = map->thingLights[i]; + + // try to early out if PVS data exists + if(!map->CheckPVS(surface->subSector, tl->ssect)) + { + continue; + } + + lightOrigin.Set(tl->origin.x, + tl->origin.y, + !tl->bCeiling ? + tl->sector->floorheight + tl->height : + tl->sector->ceilingheight - tl->height); + + if(plane.Distance(lightOrigin) - plane.d < 0) + { + // completely behind the plane + continue; + } + + radius = tl->radius; + intensity = tl->intensity; + + if(origin.DistanceSq(lightOrigin) > (radius*radius)) + { + // not within range + continue; + } + + trace.Trace(lightOrigin, origin); + + if(trace.fraction != 1) + { + // this light is occluded by something + continue; + } + + dir = (lightOrigin - origin); + dist = dir.Unit(); + + dir.Normalize(); + + float r = MAX(radius - dist, 0); + + colorAdd = ((r * plane.Normal().Dot(dir)) / radius) * intensity; + kexMath::Clamp(colorAdd, 0, 1); + + if(tl->falloff != 1) + { + colorAdd = kexMath::Pow(colorAdd, tl->falloff); + } + + // accumulate results + color = color.Lerp(tl->rgb, colorAdd); + kexMath::Clamp(color, 0, 1); + + tracedTexels++; + } + + if(surface->type != ST_CEILING && map->bSSectsVisibleToSky[surface->subSector - map->mapSSects]) + { + // see if it's exposed to sunlight + if(EmitFromCeiling(trace, surface, origin, plane.Normal(), &dist)) + { + dist = (dist * 4); + kexMath::Clamp(dist, 0, 1); + + color = color.Lerp(map->GetSunColor(), dist); + kexMath::Clamp(color, 0, 1); + + tracedTexels++; + } + } + + // trace against surface lights + for(unsigned int i = 0; i < map->lightSurfaces.Length(); ++i) + { + kexLightSurface *surfaceLight = map->lightSurfaces[i]; + + // try to early out if PVS data exists + if(!map->CheckPVS(surface->subSector, surfaceLight->Surface()->subSector)) + { + continue; + } + + if(surfaceLight->TraceSurface(map, trace, surface, origin, &dist)) + { + dist = (dist * surfaceLight->Intensity()); + kexMath::Clamp(dist, 0, 1); + + color = color.Lerp(surfaceLight->GetRGB(), kexMath::Pow(dist, surfaceLight->FallOff())); + kexMath::Clamp(color, 0, 1); + + tracedTexels++; + } + } + + return color; +} + +// +// kexLightmapBuilder::BuildSurfaceParams +// +// Determines a lightmap block in which to map to +// the lightmap texture. Width and height of the block +// is calcuated and steps are computed to determine where +// each texel will be positioned on the surface +// + +void kexLightmapBuilder::BuildSurfaceParams(surface_t *surface) +{ + kexPlane *plane; + kexBBox bounds; + kexVec3 roundedSize; + int i; + kexPlane::planeAxis_t axis; + kexVec3 tCoords[2]; + kexVec3 tOrigin; + int width; + int height; + float d; + + plane = &surface->plane; + bounds = GetBoundsFromSurface(surface); + + // round off dimentions + for(i = 0; i < 3; i++) + { + bounds.min[i] = samples * kexMath::Floor(bounds.min[i] / samples); + bounds.max[i] = samples * kexMath::Ceil(bounds.max[i] / samples); + + roundedSize[i] = (bounds.max[i] - bounds.min[i]) / samples + 1; + } + + tCoords[0].Clear(); + tCoords[1].Clear(); + + axis = plane->BestAxis(); + + switch(axis) + { + case kexPlane::AXIS_YZ: + width = (int)roundedSize.y; + height = (int)roundedSize.z; + tCoords[0].y = 1.0f / samples; + tCoords[1].z = 1.0f / samples; + break; + + case kexPlane::AXIS_XZ: + width = (int)roundedSize.x; + height = (int)roundedSize.z; + tCoords[0].x = 1.0f / samples; + tCoords[1].z = 1.0f / samples; + break; + + case kexPlane::AXIS_XY: + width = (int)roundedSize.x; + height = (int)roundedSize.y; + tCoords[0].x = 1.0f / samples; + tCoords[1].y = 1.0f / samples; + break; + } + + // clamp width + if(width > textureWidth) + { + tCoords[0] *= ((float)textureWidth / (float)width); + width = textureWidth; + } + + // clamp height + if(height > textureHeight) + { + tCoords[1] *= ((float)textureHeight / (float)height); + height = textureHeight; + } + + surface->lightmapCoords = (float*)Mem_Calloc(sizeof(float) * + surface->numVerts * 2, hb_static); + + surface->textureCoords[0] = tCoords[0]; + surface->textureCoords[1] = tCoords[1]; + + tOrigin = bounds.min; + + // project tOrigin and tCoords so they lie on the plane + d = (plane->Distance(bounds.min) - plane->d) / plane->Normal()[axis]; + tOrigin[axis] -= d; + + for(i = 0; i < 2; i++) + { + tCoords[i].Normalize(); + d = plane->Distance(tCoords[i]) / plane->Normal()[axis]; + tCoords[i][axis] -= d; + } + + surface->bounds = bounds; + surface->lightmapDims[0] = width; + surface->lightmapDims[1] = height; + surface->lightmapOrigin = tOrigin; + surface->lightmapSteps[0] = tCoords[0] * (float)samples; + surface->lightmapSteps[1] = tCoords[1] * (float)samples; +} + +// +// kexLightmapBuilder::TraceSurface +// +// Steps through each texel and traces a line to the world. +// For each non-occluded trace, color is accumulated and saved off +// into the lightmap texture based on what block is mapped to +// + +void kexLightmapBuilder::TraceSurface(surface_t *surface) +{ + kexVec3 colorSamples[256][256]; + int sampleWidth; + int sampleHeight; + kexVec3 normal; + kexVec3 pos; + kexVec3 tDelta; + int i; + int j; + kexTrace trace; + byte *currentTexture; + byte rgb[3]; + bool bShouldLookupTexture = false; + + trace.Init(*map); + memset(colorSamples, 0, sizeof(colorSamples)); + + sampleWidth = surface->lightmapDims[0]; + sampleHeight = surface->lightmapDims[1]; + + 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++) + { + for(j = 0; j < sampleWidth; j++) + { + // convert the texel into world-space coordinates. + // this will be the origin in which a line will be traced from + pos = surface->lightmapOrigin + normal + + (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 + colorSamples[i][j] += LightTexelSample(trace, pos, surface); + + // if nothing at all was traced and color is completely black + // then this surface will not go through the extra rendering + // step in rendering the lightmap + if(colorSamples[i][j].UnitSq() != 0) + { + bShouldLookupTexture = true; + } + } + } + +#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) + { + surface->lightmapNum = -1; + return; + } + else + { + int x = 0, y = 0; + int width = surface->lightmapDims[0]; + int height = surface->lightmapDims[1]; + + // 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)) + { + Error("Lightmap allocation failed\n"); + return; + } + } + + // calculate texture coordinates + for(i = 0; i < surface->numVerts; i++) + { + tDelta = surface->verts[i] - surface->bounds.min; + surface->lightmapCoords[i * 2 + 0] = + (tDelta.Dot(surface->textureCoords[0]) + x + 0.5f) / (float)textureWidth; + surface->lightmapCoords[i * 2 + 1] = + (tDelta.Dot(surface->textureCoords[1]) + y + 0.5f) / (float)textureHeight; + } + + surface->lightmapOffs[0] = x; + surface->lightmapOffs[1] = y; + } + + lightmapWorker.LockMutex(); + currentTexture = textures[surface->lightmapNum]; + lightmapWorker.UnlockMutex(); + + // store results to lightmap texture + for(i = 0; i < sampleHeight; i++) + { + for(j = 0; j < sampleWidth; j++) + { + // get texture offset + int offs = (((textureWidth * (i + surface->lightmapOffs[1])) + + 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); + + currentTexture[offs + j * 3 + 0] = rgb[0]; + currentTexture[offs + j * 3 + 1] = rgb[1]; + currentTexture[offs + j * 3 + 2] = rgb[2]; + } + } +} + +// +// kexLightmapBuilder::LightSurface +// + +void kexLightmapBuilder::LightSurface(const int surfid) +{ + static int processed = 0; + float remaining; + int numsurfs = surfaces.Length(); + + // TODO: this should NOT happen, but apparently, it can randomly occur + if(surfaces.Length() == 0) + { + return; + } + + BuildSurfaceParams(surfaces[surfid]); + TraceSurface(surfaces[surfid]); + + lightmapWorker.LockMutex(); + remaining = (float)processed / (float)numsurfs; + processed++; + + printf("%i%c surfaces done\r", (int)(remaining * 100.0f), '%'); + lightmapWorker.UnlockMutex(); +} + +// +// kexLightmapBuilder::LightCellSample +// +// Traces a line from the cell's origin to the sunlight direction +// and against all nearby thing lights +// + +kexVec3 kexLightmapBuilder::LightCellSample(const int gridid, kexTrace &trace, + const kexVec3 &origin, const mapSubSector_t *sub) +{ + kexVec3 color; + kexVec3 dir; + mapSector_t *mapSector; + float intensity; + float radius; + float dist; + float colorAdd; + thingLight_t *tl; + kexVec3 lightOrigin; + bool bInSkySector; + kexVec3 org; + + mapSector = map->GetSectorFromSubSector(sub); + bInSkySector = map->bSkySectors[mapSector - map->mapSectors]; + + trace.Trace(origin, origin + (map->GetSunDirection() * 32768)); + + // did we traced a ceiling surface with a sky texture? + if(trace.fraction != 1 && trace.hitSurface != NULL) + { + if(trace.hitSurface->bSky && origin.z + gridSize[2] > mapSector->floorheight) + { + color = map->GetSunColor(); + // this cell is inside a sector with a sky texture and is also exposed to sunlight. + // mark this cell as a sun type. cells of this type will simply sample the + // sector's light level + gridMap[gridid].sunShadow = 2; + return color; + } + + // if this cell is inside a sector with a sky texture but is NOT exposed to sunlight, then + // mark this cell as a sun shade type. cells of this type will halve the sector's light level + if(bInSkySector) + { + gridMap[gridid].sunShadow = 1; + } + } + // if the cell is technically inside a sector with a sky but is not actually on an actual surface + // then this cell is considered occluded + else if(bInSkySector && !map->PointInsideSubSector(origin.x, origin.y, sub)) + { + gridMap[gridid].sunShadow = 1; + } + + // trace against all thing lights + for(unsigned int i = 0; i < map->thingLights.Length(); i++) + { + tl = map->thingLights[i]; + + lightOrigin.Set(tl->origin.x, + tl->origin.y, + !tl->bCeiling ? + (float)tl->sector->floorheight + 16 : + (float)tl->sector->ceilingheight - 16); + + radius = tl->radius; + intensity = tl->intensity * 4; + + if(intensity < 1.0f) + { + intensity = 1.0f; + } + + if(origin.DistanceSq(lightOrigin) > (radius*radius)) + { + // not within range + continue; + } + + trace.Trace(origin, lightOrigin); + + if(trace.fraction != 1) + { + // something is occluding it + continue; + } + + dir = (lightOrigin - origin); + dist = dir.Unit(); + + dir.Normalize(); + + colorAdd = (radius / (dist * dist)) * intensity; + kexMath::Clamp(colorAdd, 0, 1); + + // accumulate results + color = color.Lerp(tl->rgb, colorAdd); + kexMath::Clamp(color, 0, 1); + } + + org = origin; + + // if the cell is sticking out from the ground then at least + // clamp the origin to the ground level so it can at least + // have a chance to be sampled by the light + if(origin.z + gridSize[2] > mapSector->floorheight) + { + org.z = (float)mapSector->floorheight + 2; + } + + // trace against all light surfaces + for(unsigned int i = 0; i < map->lightSurfaces.Length(); ++i) + { + kexLightSurface *surfaceLight = map->lightSurfaces[i]; + + if(surfaceLight->TraceSurface(map, trace, NULL, org, &dist)) + { + dist = (dist * (surfaceLight->Intensity() * 0.5f)) * 0.5f; + kexMath::Clamp(dist, 0, 1); + + // accumulate results + color = color.Lerp(surfaceLight->GetRGB(), dist); + kexMath::Clamp(color, 0, 1); + } + } + + return color; +} + +// +// kexLightmapBuilder::LightGrid +// + +void kexLightmapBuilder::LightGrid(const int gridid) +{ + static int processed = 0; + float remaining; + int x, y, z; + int mod; + int secnum; + bool bInRange; + int gx = (int)gridBlock.x; + int gy = (int)gridBlock.y; + kexTrace trace; + mapSubSector_t *ss; + + // convert grid id to xyz coordinates + mod = gridid; + z = mod / (gx * gy); + mod -= z * (gx * gy); + + y = mod / gx; + mod -= y * gx; + + x = mod; + + // get world-coordinates + kexVec3 org(worldGrid.min[0] + x * gridSize[0], + worldGrid.min[1] + y * gridSize[1], + worldGrid.min[2] + z * gridSize[2]); + + ss = NULL; + secnum = ((int)gridBlock.x * y) + x; + + // determine what sector this cell is in + if(gridSectors[secnum] == NULL) + { + ss = map->PointInSubSector((int)org.x, (int)org.y); + gridSectors[secnum] = ss; + } + else + { + ss = gridSectors[secnum]; + } + + trace.Init(*map); + kexBBox bounds = gridBound + org; + + bInRange = false; + + // is this cell even inside the world? + for(int i = 0; i < map->numSSects; ++i) + { + if(bounds.IntersectingBox(map->ssLeafBounds[i])) + { + bInRange = true; + break; + } + } + + processed++; + + if(!bInRange) + { + // ignore if not in the world + return; + } + + // mark grid cell and accumulate color results + gridMap[gridid].marked = 1; + gridMap[gridid].color += LightCellSample(gridid, trace, org, ss); + + kexMath::Clamp(gridMap[gridid].color, 0, 1); + + lightmapWorker.LockMutex(); + remaining = (float)processed / (float)numLightGrids; + + printf("%i%c cells done\r", (int)(remaining * 100.0f), '%'); + lightmapWorker.UnlockMutex(); +} + +// +// kexLightmapBuilder::CreateLightmaps +// + +void kexLightmapBuilder::CreateLightmaps(kexDoomMap &doomMap) +{ + map = &doomMap; + + printf("------------- Building light grid -------------\n"); + CreateLightGrid(); + + printf("------------- Tracing surfaces -------------\n"); + lightmapWorker.RunThreads(surfaces.Length(), this, LightmapWorkerFunc); + + while(!lightmapWorker.FinishedAllJobs()) + { + Delay(1000); + } + + printf("Texels traced: %i\n\n", tracedTexels); + lightmapWorker.Destroy(); +} + +// +// kexLightmapBuilder::CreateLightGrid +// + +void kexLightmapBuilder::CreateLightGrid(void) +{ + int count; + int numNodes; + kexVec3 mins, maxs; + + // get the bounding box of the root BSP node + numNodes = map->numNodes-1; + if(numNodes < 0) + { + numNodes = 0; + } + + worldGrid = map->nodeBounds[numNodes]; + + // determine the size of the grid block + for(int i = 0; i < 3; ++i) + { + mins[i] = gridSize[i] * kexMath::Ceil(worldGrid.min[i] / gridSize[i]); + maxs[i] = gridSize[i] * kexMath::Floor(worldGrid.max[i] / gridSize[i]); + gridBlock[i] = (maxs[i] - mins[i]) / gridSize[i] + 1; + } + + worldGrid.min = mins; + worldGrid.max = maxs; + + gridBound.min = -(gridSize * 0.5f); + gridBound.max = (gridSize * 0.5f); + + // get the total number of grid cells + count = (int)(gridBlock.x * gridBlock.y * gridBlock.z); + numLightGrids = count; + + // allocate data + gridMap = (gridMap_t*)Mem_Calloc(sizeof(gridMap_t) * count, hb_static); + gridSectors = (mapSubSector_t**)Mem_Calloc(sizeof(mapSubSector_t*) * + (int)(gridBlock.x * gridBlock.y), hb_static); + + // process all grid cells + lightmapWorker.RunThreads(count, this, LightGridWorkerFunc); + + while(!lightmapWorker.FinishedAllJobs()) + { + Delay(1000); + } + + printf("\nGrid cells: %i\n\n", count); +} + +// +// kexLightmapBuilder::AddLightGridLump +// + +void kexLightmapBuilder::AddLightGridLump(kexWadFile &wadFile) +{ + kexBinFile lumpFile; + int lumpSize = 0; + int bit; + int bitmask; + byte *data; + + lumpSize = 28 + numLightGrids; + + for(int i = 0; i < numLightGrids; ++i) + { + if(gridMap[i].marked) + { + lumpSize += 4; + } + } + + lumpSize += 512; // add some extra slop + + data = (byte*)Mem_Calloc(lumpSize, hb_static); + lumpFile.SetBuffer(data); + + lumpFile.Write32(numLightGrids); + lumpFile.Write16((short)worldGrid.min[0]); + lumpFile.Write16((short)worldGrid.min[1]); + lumpFile.Write16((short)worldGrid.min[2]); + lumpFile.Write16((short)worldGrid.max[0]); + lumpFile.Write16((short)worldGrid.max[1]); + lumpFile.Write16((short)worldGrid.max[2]); + lumpFile.Write16((short)gridSize.x); + lumpFile.Write16((short)gridSize.y); + lumpFile.Write16((short)gridSize.z); + lumpFile.Write16((short)gridBlock.x); + lumpFile.Write16((short)gridBlock.y); + lumpFile.Write16((short)gridBlock.z); + + bit = 0; + bitmask = 0; + + for(int i = 0; i < numLightGrids; ++i) + { +#if 0 + bit |= (gridMap[i].marked << bitmask); + + if(++bitmask == 8) + { + lumpFile.Write8(bit); + bit = 0; + bitmask = 0; + } +#else + lumpFile.Write8(gridMap[i].marked); +#endif + } + +#if 0 + if(bit) + { + lumpFile.Write8(bit); + } +#endif + + for(int i = 0; i < numLightGrids; ++i) + { + if(gridMap[i].marked) + { + lumpFile.Write8((byte)(gridMap[i].color[0] * 255.0f)); + lumpFile.Write8((byte)(gridMap[i].color[1] * 255.0f)); + lumpFile.Write8((byte)(gridMap[i].color[2] * 255.0f)); + } + } + + bit = 0; + bitmask = 0; + + for(int i = 0; i < numLightGrids; ++i) + { + if(gridMap[i].marked) + { +#if 0 + bit |= (gridMap[i].sunShadow << bitmask); + bitmask += 2; + + if(bitmask >= 8) + { + lumpFile.Write8(bit); + bit = 0; + bitmask = 0; + } +#else + lumpFile.Write8(gridMap[i].sunShadow); +#endif + } + } + +#if 0 + if(bit) + { + lumpFile.Write8(bit); + } +#endif + + wadFile.AddLump("LM_CELLS", lumpFile.BufferAt() - lumpFile.Buffer(), data); +} + +// +// kexLightmapBuilder::CreateLightmapLump +// + +void kexLightmapBuilder::AddLightmapLumps(kexWadFile &wadFile) +{ + int lumpSize = 0; + unsigned int i; + byte *data, *surfs, *txcrd, *lmaps; + int offs; + int size; + int j; + int coordOffsets; + kexBinFile lumpFile; + + // try to guess the actual lump size + lumpSize += ((textureWidth * textureHeight) * 3) * textures.Length(); + lumpSize += (12 * surfaces.Length()); + lumpSize += sizeof(kexVec3); + lumpSize += 2048; // add some extra slop + + for(i = 0; i < surfaces.Length(); i++) + { + lumpSize += (surfaces[i]->numVerts * 2) * sizeof(float); + } + + data = (byte*)Mem_Calloc(lumpSize, hb_static); + lumpFile.SetBuffer(data); + + lumpFile.WriteVector(map->GetSunDirection()); + wadFile.AddLump("LM_SUN", sizeof(kexVec3), data); + + offs = lumpFile.BufferAt() - lumpFile.Buffer(); + surfs = data + offs; + + coordOffsets = 0; + size = 0; + + // begin writing LM_SURFS lump + for(i = 0; i < surfaces.Length(); i++) + { + lumpFile.Write16(surfaces[i]->type); + lumpFile.Write16(surfaces[i]->typeIndex); + lumpFile.Write16(surfaces[i]->lightmapNum); + lumpFile.Write16(surfaces[i]->numVerts * 2); + lumpFile.Write32(coordOffsets); + + size += 12; + coordOffsets += (surfaces[i]->numVerts * 2); + } + + offs = lumpFile.BufferAt() - lumpFile.Buffer(); + wadFile.AddLump("LM_SURFS", size, data); + txcrd = data + offs; + + size = 0; + + // begin writing LM_TXCRD lump + for(i = 0; i < surfaces.Length(); i++) + { + for(j = 0; j < surfaces[i]->numVerts * 2; j++) + { + lumpFile.WriteFloat(surfaces[i]->lightmapCoords[j]); + size += 4; + } + } + + offs = (lumpFile.BufferAt() - lumpFile.Buffer()) - offs; + wadFile.AddLump("LM_TXCRD", size, txcrd); + lmaps = txcrd + offs; + + // begin writing LM_LMAPS lump + lumpFile.Write32(textures.Length()); + lumpFile.Write32(textureWidth); + lumpFile.Write32(textureHeight); + + size = 12; + + for(i = 0; i < textures.Length(); i++) + { + for(j = 0; j < (textureWidth * textureHeight) * 3; j++) + { + lumpFile.Write8(textures[i][j]); + size++; + } + } + + offs = (lumpFile.BufferAt() - lumpFile.Buffer()) - offs; + wadFile.AddLump("LM_LMAPS", size, lmaps); +} + +// +// kexLightmapBuilder::WriteTexturesToTGA +// + +void kexLightmapBuilder::WriteTexturesToTGA(void) +{ + kexBinFile file; + + for(unsigned int i = 0; i < textures.Length(); i++) + { + file.Create(Va("lightmap_%02d.tga", i)); + file.Write16(0); + file.Write16(2); + file.Write16(0); + file.Write16(0); + file.Write16(0); + file.Write16(0); + file.Write16(textureWidth); + file.Write16(textureHeight); + file.Write16(24); + + for(int j = 0; j < (textureWidth * textureHeight) * 3; j += 3) + { + file.Write8(textures[i][j+2]); + file.Write8(textures[i][j+1]); + file.Write8(textures[i][j+0]); + } + file.Close(); + } +} + +// +// kexLightmapBuilder::ExportTexelsToObjFile +// + +void kexLightmapBuilder::ExportTexelsToObjFile(FILE *f, const kexVec3 &org, int indices) +{ +#define BLOCK(idx, offs) (org[idx] + offs)/256.0f + fprintf(f, "o texel_%03d\n", indices); + fprintf(f, "v %f %f %f\n", -BLOCK(1, -6), BLOCK(2, -6), -BLOCK(0, 6)); + fprintf(f, "v %f %f %f\n", -BLOCK(1, -6), BLOCK(2, 6), -BLOCK(0, 6)); + fprintf(f, "v %f %f %f\n", -BLOCK(1, -6), BLOCK(2, 6), -BLOCK(0, -6)); + fprintf(f, "v %f %f %f\n", -BLOCK(1, -6), BLOCK(2, -6), -BLOCK(0, -6)); + fprintf(f, "v %f %f %f\n", -BLOCK(1, 6), BLOCK(2, -6), -BLOCK(0, 6)); + fprintf(f, "v %f %f %f\n", -BLOCK(1, 6), BLOCK(2, 6), -BLOCK(0, 6)); + fprintf(f, "v %f %f %f\n", -BLOCK(1, 6), BLOCK(2, 6), -BLOCK(0, -6)); + fprintf(f, "v %f %f %f\n", -BLOCK(1, 6), BLOCK(2, -6), -BLOCK(0, -6)); + fprintf(f, "f %i %i %i %i\n", indices+1, indices+2, indices+3, indices+4); + fprintf(f, "f %i %i %i %i\n", indices+5, indices+8, indices+7, indices+6); + fprintf(f, "f %i %i %i %i\n", indices+1, indices+5, indices+6, indices+2); + fprintf(f, "f %i %i %i %i\n", indices+2, indices+6, indices+7, indices+3); + fprintf(f, "f %i %i %i %i\n", indices+3, indices+7, indices+8, indices+4); + fprintf(f, "f %i %i %i %i\n\n", indices+5, indices+1, indices+4, indices+8); +#undef BLOCK +} + +// +// kexLightmapBuilder::WriteBlock +// + +void kexLightmapBuilder::WriteBlock(FILE *f, const int i, const kexVec3 &org, int indices, kexBBox &box) +{ +#define BLOCK(idx, offs) (org[idx] + box[offs][idx])/256.0f + fprintf(f, "o texel_%03d\n", i); + fprintf(f, "v %f %f %f\n", -BLOCK(1, 0), BLOCK(2, 0), -BLOCK(0, 1)); + fprintf(f, "v %f %f %f\n", -BLOCK(1, 0), BLOCK(2, 1), -BLOCK(0, 1)); + fprintf(f, "v %f %f %f\n", -BLOCK(1, 0), BLOCK(2, 1), -BLOCK(0, 0)); + fprintf(f, "v %f %f %f\n", -BLOCK(1, 0), BLOCK(2, 0), -BLOCK(0, 0)); + fprintf(f, "v %f %f %f\n", -BLOCK(1, 1), BLOCK(2, 0), -BLOCK(0, 1)); + fprintf(f, "v %f %f %f\n", -BLOCK(1, 1), BLOCK(2, 1), -BLOCK(0, 1)); + fprintf(f, "v %f %f %f\n", -BLOCK(1, 1), BLOCK(2, 1), -BLOCK(0, 0)); + fprintf(f, "v %f %f %f\n", -BLOCK(1, 1), BLOCK(2, 0), -BLOCK(0, 0)); + fprintf(f, "f %i %i %i %i\n", indices+1, indices+2, indices+3, indices+4); + fprintf(f, "f %i %i %i %i\n", indices+5, indices+8, indices+7, indices+6); + fprintf(f, "f %i %i %i %i\n", indices+1, indices+5, indices+6, indices+2); + fprintf(f, "f %i %i %i %i\n", indices+2, indices+6, indices+7, indices+3); + fprintf(f, "f %i %i %i %i\n", indices+3, indices+7, indices+8, indices+4); + fprintf(f, "f %i %i %i %i\n\n", indices+5, indices+1, indices+4, indices+8); +#undef BLOCK +} diff --git a/src/lightmap/lightmap.h b/src/lightmap/lightmap.h new file mode 100644 index 0000000..800933a --- /dev/null +++ b/src/lightmap/lightmap.h @@ -0,0 +1,93 @@ +//----------------------------------------------------------------------------- +// 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. +// + +#ifndef __LIGHTMAP_H__ +#define __LIGHTMAP_H__ + +#include "surfaces.h" + +#define LIGHTMAP_MAX_SIZE 1024 + +class kexTrace; + +class kexLightmapBuilder +{ +public: + kexLightmapBuilder(void); + ~kexLightmapBuilder(void); + + void BuildSurfaceParams(surface_t *surface); + void TraceSurface(surface_t *surface); + void CreateLightGrid(void); + void CreateLightmaps(kexDoomMap &doomMap); + void LightSurface(const int surfid); + void LightGrid(const int gridid); + void WriteTexturesToTGA(void); + void AddLightGridLump(kexWadFile &wadFile); + void AddLightmapLumps(kexWadFile &wadFile); + + int samples; + float ambience; + int textureWidth; + int textureHeight; + + static const kexVec3 gridSize; + +private: + void NewTexture(void); + bool MakeRoomForBlock(const int width, const int height, int *x, int *y, int *num); + 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 mapSubSector_t *sub); + bool EmitFromCeiling(kexTrace &trace, const surface_t *surface, const kexVec3 &origin, + const kexVec3 &normal, float *dist); + void ExportTexelsToObjFile(FILE *f, const kexVec3 &org, int indices); + void WriteBlock(FILE *f, const int i, const kexVec3 &org, int indices, kexBBox &box); + + typedef struct + { + byte marked; + byte sunShadow; + kexVec3 color; + } gridMap_t; + + kexDoomMap *map; + kexArray textures; + int **allocBlocks; + int numTextures; + int extraSamples; + int tracedTexels; + int numLightGrids; + gridMap_t *gridMap; + mapSubSector_t **gridSectors; + kexBBox worldGrid; + kexBBox gridBound; + kexVec3 gridBlock; +}; + +#endif diff --git a/src/lightmap/lightsurface.cpp b/src/lightmap/lightsurface.cpp new file mode 100644 index 0000000..3ffd6f7 --- /dev/null +++ b/src/lightmap/lightsurface.cpp @@ -0,0 +1,464 @@ +//----------------------------------------------------------------------------- +// 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: Special surfaces that contains origin points used for +// emitting light. Surfaces can be subdivided for more +// accurate light casting +// +//----------------------------------------------------------------------------- + +#include "common.h" +#include "mapData.h" +#include "trace.h" +#include "lightSurface.h" + +// +// kexLightSurface::kexLightSurface +// + +kexLightSurface::kexLightSurface(void) +{ +} + +// +// kexLightSurface::~kexLightSurface +// + +kexLightSurface::~kexLightSurface(void) +{ +} + +// +// kexLightSurface::Init +// + +void kexLightSurface::Init(const surfaceLightDef_t &lightSurfaceDef, + surface_t *surface, + const bool bWall, + const bool bNoCenterPoint) +{ + this->outerCone = lightSurfaceDef.outerCone; + this->innerCone = lightSurfaceDef.innerCone; + this->falloff = lightSurfaceDef.falloff; + this->intensity = lightSurfaceDef.intensity; + this->distance = lightSurfaceDef.distance; + this->rgb = lightSurfaceDef.rgb; + this->surface = surface; + this->bWall = bWall; + this->bNoCenterPoint = bNoCenterPoint; +} + +// +// kexLightSurface::CreateCenterOrigin +// +// Creates a single origin point if we're not +// intending on subdividing this light surface +// + +void kexLightSurface::CreateCenterOrigin(void) +{ + if(!bWall) + { + kexVec3 center; + + for(int i = 0; i < surface->numVerts; ++i) + { + center += surface->verts[i]; + } + + origins.Push(center / (float)surface->numVerts); + } + else + { + origins.Push(kexVec3((surface->verts[1].x + surface->verts[0].x) * 0.5f, + (surface->verts[1].y + surface->verts[0].y) * 0.5f, + (surface->verts[2].z + surface->verts[0].z) * 0.5f)); + } +} + +// +// kexLightSurface::Clip +// +// Splits surface vertices into two groups while adding new ones +// caused by the split +// + +void kexLightSurface::Clip(vertexBatch_t &points, const kexVec3 &normal, float dist, + vertexBatch_t *frontPoints, vertexBatch_t *backPoints) +{ + kexArray dists; + kexArray sides; + + // determines what sides the vertices lies on + for(unsigned int i = 0; i < points.Length(); ++i) + { + float d = points[i].Dot(normal) - dist; + + dists.Push(d); + + if(d > 0.1f) + { + sides.Push(1); // front + } + else if(d < -0.1f) + { + sides.Push(-1); // directly on the split plane + } + else + { + sides.Push(0); // back + } + } + + // add points + for(unsigned int i = 0; i < points.Length(); ++i) + { + int next; + float frac; + kexVec3 pt1, pt2, pt3; + + switch(sides[i]) + { + case -1: + backPoints->Push(points[i]); + break; + + case 1: + frontPoints->Push(points[i]); + break; + + default: + frontPoints->Push(points[i]); + backPoints->Push(points[i]); + + // point is on the split plane so no new split vertex is needed + continue; + + } + + // check if the edge crosses the split plane + next = (i + 1) % points.Length(); + + if(sides[next] == 0 || sides[next] == sides[i]) + { + // didn't cross + continue; + } + + pt1 = points[i]; + pt2 = points[next]; + + // insert a new point caused by the split + frac = dists[i] / (dists[i] - dists[next]); + pt3 = pt1.Lerp(pt2, frac); + + frontPoints->Push(pt3); + backPoints->Push(pt3); + } +} + +// +// kexLightSurface::SubdivideRecursion +// +// Recursively divides the surface +// + +bool kexLightSurface::SubdivideRecursion(vertexBatch_t &surfPoints, float divide, + kexArray &points) +{ + kexBBox bounds; + kexVec3 splitNormal; + float dist; + vertexBatch_t *frontPoints; + vertexBatch_t *backPoints; + + // get bounds from current set of points + for(unsigned int i = 0; i < surfPoints.Length(); ++i) + { + bounds.AddPoint(surfPoints[i]); + } + + for(int i = 0; i < 3; ++i) + { + // check if its large enough to be divided + if((bounds.max[i] - bounds.min[i]) > divide) + { + splitNormal.Clear(); + splitNormal[i] = 1; + + dist = (bounds.max[i] + bounds.min[i]) * 0.5f; + + frontPoints = new vertexBatch_t; + backPoints = new vertexBatch_t; + + // start clipping + Clip(surfPoints, splitNormal, dist, frontPoints, backPoints); + + if(!SubdivideRecursion(*frontPoints, divide, points)) + { + points.Push(frontPoints); + } + else + { + delete frontPoints; + } + + if(!SubdivideRecursion(*backPoints, divide, points)) + { + points.Push(backPoints); + } + else + { + delete backPoints; + } + + return true; + } + } + + return false; +} + +// +// kexLightSurface::Subdivide +// + +void kexLightSurface::Subdivide(const float divide) +{ + kexArray points; + vertexBatch_t surfPoints; + + for(int i = 0; i < surface->numVerts; ++i) + { + surfPoints.Push(surface->verts[i]); + } + + SubdivideRecursion(surfPoints, divide, points); + + // from each group of vertices caused by the split, begin + // creating a origin point based on the center of that group + for(unsigned int i = 0; i < points.Length(); ++i) + { + vertexBatch_t *vb = points[i]; + kexVec3 center; + + for(unsigned int j = 0; j < vb->Length(); ++j) + { + center += (*vb)[j]; + } + + origins.Push(center / (float)vb->Length()); + } + + for(unsigned int i = 0; i < points.Length(); ++i) + { + vertexBatch_t *vb = points[i]; + + vb->Empty(); + delete vb; + } +} + +// +// kexLightSurface::TraceSurface +// + +bool kexLightSurface::TraceSurface(kexDoomMap *doomMap, kexTrace &trace, const surface_t *surf, + const kexVec3 &origin, float *dist) +{ + kexVec3 normal; + kexVec3 lnormal; + kexVec3 center; + bool bInside; + float angle; + float curDist; + + *dist = -M_INFINITY; + + // light surface will always be fullbright + if(surf == surface) + { + *dist = 1; + return true; + } + + bInside = false; + + // nudge the origin around to see if it's actually in the subsector + float nudges[4] = { -2, 2, -4, 4 }; + + for(int x = 0; x < 4; x++) + { + for(int y = 0; y < 4; y++) + { + if(doomMap->PointInsideSubSector(origin.x + nudges[x], + origin.y + nudges[y], surface->subSector)) + { + bInside = true; + break; + } + } + + if(bInside) + { + break; + } + } + + lnormal = surface->plane.Normal(); + + if(surf) + { + normal = surf->plane.Normal(); + + if(normal.Dot(lnormal) > 0) + { + // not facing the light surface + return false; + } + } + else + { + normal = kexVec3::vecUp; + } + + // we need to pick the closest sample point on the light surface. what really sucks is + // that we have to trace each one... which could really blow up the compile time + for(unsigned int i = 0; i < origins.Length(); ++i) + { + center = origins[i]; + + if(!bWall && origin.z > center.z) + { + // origin is not going to seen or traced by the light surface + // so don't even bother. this also fixes some bizzare light + // bleeding issues + continue; + } + + if(bWall) + { + angle = (origin - center).ToVec2().Normalize().Dot(lnormal.ToVec2()); + } + else + { + kexVec3 dir = (origin - center).Normalize(); + + if(surf) + { + if(normal.Dot(dir) >= 0) + { + // not even facing the light surface + continue; + } + } + + angle = dir.Dot(lnormal); + + if(angle > innerCone) + { + angle = innerCone; + } + } + + if(!bInside && angle < outerCone) + { + // out of the cone range + continue; + } + + if(bWall) + { + if(origin.z >= surface->verts[0].z && origin.z <= surface->verts[2].z) + { + // since walls are always vertically straight, we can cheat a little by adjusting + // the sampling point height. this also allows us to do accurate light emitting + // while just using one sample point + center.z = origin.z; + } + } + + // trace the origin to the center of the light surface. nudge by the normals in + // case the start/end points are directly on or inside the surface + trace.Trace(center + lnormal, origin + normal); + + if(trace.fraction != 1) + { + // something is obstructing it + continue; + } + + float d = origin.Distance(center); + + if(d <= 0) + { + // this origin point must be right on the light surface so just mark it as + // full bright if that's the case + curDist = 1; + } + else + { + curDist = distance / d; + } + + if(curDist >= 1) + { + curDist = 1; + + // might get large unlit gaps near the surface. this looks a lot worse for + // non-wall light surfaces so just clamp to full bright and exit out. + if(!bWall) + { + *dist = 1; + return true; + } + } + + // determine how much to fade out + if(angle < innerCone) + { + float div = (innerCone - outerCone); + + if(div != 0) + { + curDist *= ((angle - outerCone) / div); + } + else + { + curDist *= angle; + } + } + + if(curDist > *dist) + { + *dist = curDist; + } + } + + return *dist > 0; +} diff --git a/src/lightmap/lightsurface.h b/src/lightmap/lightsurface.h new file mode 100644 index 0000000..d9d4768 --- /dev/null +++ b/src/lightmap/lightsurface.h @@ -0,0 +1,92 @@ +//----------------------------------------------------------------------------- +// 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. +// + +#ifndef __LIGHT_SURFACE_H__ +#define __LIGHT_SURFACE_H__ + +#include "surfaces.h" + +typedef struct +{ + int tag; + float outerCone; + float innerCone; + float falloff; + float distance; + float intensity; + bool bIgnoreFloor; + bool bIgnoreCeiling; + bool bNoCenterPoint; + kexVec3 rgb; +} surfaceLightDef_t; + +class kexDoomMap; +class kexTrace; + +class kexLightSurface +{ +public: + kexLightSurface(void); + ~kexLightSurface(void); + + void Init(const surfaceLightDef_t &lightSurfaceDef, surface_t *surface, + const bool bWall, const bool bNoCenterPoint); + void Subdivide(const float divide); + void CreateCenterOrigin(void); + bool TraceSurface(kexDoomMap *doomMap, kexTrace &trace, const surface_t *surface, + const kexVec3 &origin, float *dist); + + const float OuterCone(void) const { return outerCone; } + const float InnerCone(void) const { return innerCone; } + const float FallOff(void) const { return falloff; } + const float Distance(void) const { return distance; } + const float Intensity(void) const { return intensity; } + const kexVec3 GetRGB(void) const { return rgb; } + const bool IsAWall(void) const { return bWall; } + const bool NoCenterPoint(void) const { return bNoCenterPoint; } + const surface_t *Surface(void) const { return surface; } + const vertexBatch_t Origins(void) const { return origins; } + +private: + bool SubdivideRecursion(vertexBatch_t &surfPoints, float divide, + kexArray &points); + void Clip(vertexBatch_t &points, const kexVec3 &normal, float dist, + vertexBatch_t *frontPoints, vertexBatch_t *backPoints); + + float outerCone; + float innerCone; + float falloff; + float distance; + float intensity; + kexVec3 rgb; + bool bWall; + bool bNoCenterPoint; + vertexBatch_t origins; + surface_t *surface; +}; + +#endif diff --git a/src/lightmap/mapdata.cpp b/src/lightmap/mapdata.cpp new file mode 100644 index 0000000..4169a30 --- /dev/null +++ b/src/lightmap/mapdata.cpp @@ -0,0 +1,957 @@ +//----------------------------------------------------------------------------- +// 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: General doom map utilities and data preperation +// +//----------------------------------------------------------------------------- + +#include "common.h" +#include "wad.h" +#include "kexlib/parser.h" +#include "mapData.h" +#include "lightSurface.h" + +const kexVec3 kexDoomMap::defaultSunColor(1, 1, 1); +const kexVec3 kexDoomMap::defaultSunDirection(0.45f, 0.3f, 0.9f); + +// +// kexDoomMap::kexDoomMap +// + +kexDoomMap::kexDoomMap(void) +{ + this->mapLines = NULL; + this->mapVerts = NULL; + this->mapSides = NULL; + this->mapSectors = NULL; + this->mapSegs = NULL; + this->mapSSects = NULL; + this->nodes = NULL; + this->leafs = NULL; + this->segLeafLookup = NULL; + this->ssLeafLookup = NULL; + this->ssLeafCount = NULL; + this->segSurfaces[0] = NULL; + this->segSurfaces[1] = NULL; + this->segSurfaces[2] = NULL; + this->leafSurfaces[0] = NULL; + this->leafSurfaces[1] = NULL; + this->vertexes = NULL; + this->mapPVS = NULL; + this->mapDef = NULL; + + this->numLeafs = 0; + this->numLines = 0; + this->numVerts = 0; + this->numSides = 0; + this->numSectors = 0; + this->numSegs = 0; + this->numSSects = 0; + this->numNodes = 0; + this->numVertexes = 0; +} + +// +// kexDoomMap::~kexDoomMap +// + +kexDoomMap::~kexDoomMap(void) +{ +} + +// +// kexDoomMap::BuildMapFromWad +// + +void kexDoomMap::BuildMapFromWad(kexWadFile &wadFile) +{ + wadFile.GetMapLump(ML_THINGS, &mapThings, &numThings); + wadFile.GetMapLump(ML_VERTEXES, &mapVerts, &numVerts); + wadFile.GetMapLump(ML_LINEDEFS, &mapLines, &numLines); + wadFile.GetMapLump(ML_SIDEDEFS, &mapSides, &numSides); + wadFile.GetMapLump(ML_SECTORS, &mapSectors, &numSectors); + + wadFile.GetGLMapLump(ML_GL_SEGS, &mapSegs, &numSegs); + wadFile.GetGLMapLump(ML_GL_SSECT, &mapSSects, &numSSects); + wadFile.GetGLMapLump(ML_GL_NODES, &nodes, &numNodes); + wadFile.GetGLMapLump(ML_GL_PVS, &mapPVS, 0); + + if(mapSegs == NULL) + { + Error("kexDoomMap::BuildMapFromWad: SEGS lump not found\n"); + return; + } + if(mapSSects == NULL) + { + Error("kexDoomMap::BuildMapFromWad: SSECTORS lump not found\n"); + return; + } + if(nodes == NULL) + { + Error("kexDoomMap::BuildMapFromWad: NODES lump not found\n"); + return; + } + + for(unsigned int i = 0; i < mapDefs.Length(); ++i) + { + if(mapDefs[i].map == wadFile.currentmap) + { + mapDef = &mapDefs[i]; + break; + } + } + + printf("------------- Level Info -------------\n"); + printf("Vertices: %i\n", numVerts); + printf("Segments: %i\n", numSegs); + printf("Subsectors: %i\n", numSSects); + + BuildVertexes(wadFile); + BuildNodeBounds(); + BuildLeafs(); + BuildPVS(); + CheckSkySectors(); +} + +// +// kexDoomMap::CheckSkySectors +// + +void kexDoomMap::CheckSkySectors(void) +{ + char name[9]; + + bSkySectors = (bool*)Mem_Calloc(sizeof(bool) * numSectors, hb_static); + bSSectsVisibleToSky = (bool*)Mem_Calloc(sizeof(bool) * numSSects, hb_static); + + for(int i = 0; i < numSectors; ++i) + { + if(mapDef->sunIgnoreTag != 0 && mapSectors[i].tag == mapDef->sunIgnoreTag) + { + continue; + } + + strncpy(name, mapSectors[i].ceilingpic, 8); + name[8] = 0; + + if(!strncmp(name, "F_SKY001", 8)) + { + bSkySectors[i] = true; + } + } + + // try to early out by quickly checking which subsector can potentially + // see a sky sector + for(int i = 0; i < numSSects; ++i) + { + for(int j = 0; j < numSSects; ++j) + { + mapSector_t *sec = GetSectorFromSubSector(&mapSSects[j]); + + if(bSkySectors[sec - mapSectors] == false) + { + continue; + } + + if(CheckPVS(&mapSSects[i], &mapSSects[j])) + { + bSSectsVisibleToSky[i] = true; + break; + } + } + } +} + +// +// kexDoomMap::BuildPVS +// + +void kexDoomMap::BuildPVS(void) +{ + // don't do anything if already loaded + if(mapPVS != NULL) + { + return; + } + + int len = ((numSSects + 7) / 8) * numSSects; + mapPVS = (byte*)Mem_Malloc(len, hb_static); + memset(mapPVS, 0xff, len); +} + +// +// kexDoomMap::CheckPVS +// + +bool kexDoomMap::CheckPVS(mapSubSector_t *s1, mapSubSector_t *s2) +{ + byte *vis; + int n1, n2; + + n1 = s1 - mapSSects; + n2 = s2 - mapSSects; + + vis = &mapPVS[(((numSSects + 7) / 8) * n1)]; + + return ((vis[n2 >> 3] & (1 << (n2 & 7))) != 0); +} + +// +// kexDoomMap::BuildVertexes +// + +void kexDoomMap::BuildVertexes(kexWadFile &wadFile) +{ + byte *data; + int count; + glVert_t *verts; + lump_t *lump; + + if(!(lump = wadFile.GetGLMapLump(static_cast(ML_GL_VERTS)))) + { + Error("kexDoomMap::BuildVertexes: GL_VERTS lump not found\n"); + return; + } + + data = wadFile.GetLumpData(lump); + + if(*((int*)data) != gNd2) + { + Error("kexDoomMap::BuildVertexes: GL_VERTS must be version 2 only"); + return; + } + + verts = (glVert_t*)(data + GL_VERT_OFFSET); + count = (lump->size - GL_VERT_OFFSET) / sizeof(glVert_t); + + numVertexes = numVerts + count; + vertexes = (vertex_t*)Mem_Calloc(sizeof(vertex_t) * numVertexes, hb_static); + + for(int i = 0; i < numVerts; i++) + { + vertexes[i].x = F(mapVerts[i].x << 16); + vertexes[i].y = F(mapVerts[i].y << 16); + } + + for(int i = 0; i < count; i++) + { + vertexes[numVerts + i].x = F(verts[i].x); + vertexes[numVerts + i].y = F(verts[i].y); + } +} + +// +// kexDoomMap::BuildNodeBounds +// + +void kexDoomMap::BuildNodeBounds(void) +{ + int i; + int j; + kexVec3 point; + float high = -M_INFINITY; + float low = M_INFINITY; + + nodeBounds = (kexBBox*)Mem_Calloc(sizeof(kexBBox) * numNodes, hb_static); + + for(i = 0; i < numSectors; ++i) + { + if(mapSectors[i].ceilingheight > high) + { + high = mapSectors[i].ceilingheight; + } + if(mapSectors[i].floorheight < low) + { + low = mapSectors[i].floorheight; + } + } + + for(i = 0; i < numNodes; ++i) + { + nodeBounds[i].Clear(); + + for(j = 0; j < 2; ++j) + { + point.Set(nodes[i].bbox[j][BOXLEFT], nodes[i].bbox[j][BOXBOTTOM], low); + nodeBounds[i].AddPoint(point); + point.Set(nodes[i].bbox[j][BOXRIGHT], nodes[i].bbox[j][BOXTOP], high); + nodeBounds[i].AddPoint(point); + } + } +} + +// +// kexDoomMap::BuildLeafs +// + +void kexDoomMap::BuildLeafs(void) +{ + mapSubSector_t *ss; + leaf_t *lf; + int i; + int j; + kexVec3 point; + mapSector_t *sector; + int count; + + leafs = (leaf_t*)Mem_Calloc(sizeof(leaf_t*) * numSegs * 2, hb_static); + numLeafs = numSSects; + + ss = mapSSects; + + segLeafLookup = (int*)Mem_Calloc(sizeof(int) * numSegs, hb_static); + ssLeafLookup = (int*)Mem_Calloc(sizeof(int) * numSSects, hb_static); + ssLeafCount = (int*)Mem_Calloc(sizeof(int) * numSSects, hb_static); + ssLeafBounds = (kexBBox*)Mem_Calloc(sizeof(kexBBox) * numSSects, hb_static); + + count = 0; + + for(i = 0; i < numSSects; ++i, ++ss) + { + ssLeafCount[i] = ss->numsegs; + ssLeafLookup[i] = ss->firstseg; + + ssLeafBounds[i].Clear(); + sector = GetSectorFromSubSector(ss); + + if(ss->numsegs) + { + for(j = 0; j < ss->numsegs; ++j) + { + glSeg_t *seg = &mapSegs[ss->firstseg + j]; + lf = &leafs[count++]; + + segLeafLookup[ss->firstseg + j] = i; + + lf->vertex = GetSegVertex(seg->v1); + lf->seg = seg; + + point.Set(lf->vertex->x, lf->vertex->y, sector->floorheight); + ssLeafBounds[i].AddPoint(point); + + point.z = sector->ceilingheight; + ssLeafBounds[i].AddPoint(point); + } + } + } +} + +// +// kexDoomMap::GetSegVertex +// + +vertex_t *kexDoomMap::GetSegVertex(int index) +{ + if(index & 0x8000) + { + index = (index & 0x7FFF) + numVerts; + } + + return &vertexes[index]; +} + +// +// kexDoomMap::GetSideDef +// + +mapSideDef_t *kexDoomMap::GetSideDef(const glSeg_t *seg) +{ + mapLineDef_t *line; + + if(seg->linedef == NO_LINE_INDEX) + { + // skip minisegs + return NULL; + } + + line = &mapLines[seg->linedef]; + return &mapSides[line->sidenum[seg->side]]; +} + +// +// kexDoomMap::GetFrontSector +// + +mapSector_t *kexDoomMap::GetFrontSector(const glSeg_t *seg) +{ + mapSideDef_t *side = GetSideDef(seg); + + if(side == NULL) + { + return NULL; + } + + return &mapSectors[side->sector]; +} + +// +// kexDoomMap::GetBackSector +// + +mapSector_t *kexDoomMap::GetBackSector(const glSeg_t *seg) +{ + mapLineDef_t *line; + + if(seg->linedef == NO_LINE_INDEX) + { + // skip minisegs + return NULL; + } + + line = &mapLines[seg->linedef]; + + if(line->flags & ML_TWOSIDED) + { + mapSideDef_t *backSide = &mapSides[line->sidenum[seg->side^1]]; + return &mapSectors[backSide->sector]; + } + + return NULL; +} + +// +// kexDoomMap::GetSectorFromSubSector +// + +mapSector_t *kexDoomMap::GetSectorFromSubSector(const mapSubSector_t *sub) +{ + mapSector_t *sector = NULL; + + // try to find a sector that the subsector belongs to + for(int i = 0; i < sub->numsegs; i++) + { + glSeg_t *seg = &mapSegs[sub->firstseg + i]; + if(seg->side != NO_SIDE_INDEX) + { + sector = GetFrontSector(seg); + break; + } + } + + return sector; +} + +// +// kexDoomMap::PointInSubSector +// + +mapSubSector_t *kexDoomMap::PointInSubSector(const int x, const int y) +{ + mapNode_t *node; + int side; + int nodenum; + kexVec3 dp1; + kexVec3 dp2; + float d; + + // single subsector is a special case + if(!numNodes) + { + return &mapSSects[0]; + } + + nodenum = numNodes - 1; + + while(!(nodenum & NF_SUBSECTOR) ) + { + node = &nodes[nodenum]; + + kexVec3 pt1(F(node->x << 16), F(node->y << 16), 0); + kexVec3 pt2(F(node->dx << 16), F(node->dy << 16), 0); + kexVec3 pos(F(x << 16), F(y << 16), 0); + + dp1 = pt1 - pos; + dp2 = (pt2 + pt1) - pos; + d = dp1.Cross(dp2).z; + + side = FLOATSIGNBIT(d); + + nodenum = node->children[side ^ 1]; + } + + return &mapSSects[nodenum & ~NF_SUBSECTOR]; +} + +// +// kexDoomMap::PointInsideSubSector +// + +bool kexDoomMap::PointInsideSubSector(const float x, const float y, const mapSubSector_t *sub) +{ + surface_t *surf; + int i; + kexVec2 p(x, y); + kexVec2 dp1, dp2; + kexVec2 pt1, pt2; + + surf = leafSurfaces[0][sub - mapSSects]; + + // check to see if the point is inside the subsector leaf + for(i = 0; i < surf->numVerts; i++) + { + pt1 = surf->verts[i].ToVec2(); + pt2 = surf->verts[(i+1)%surf->numVerts].ToVec2(); + + dp1 = pt1 - p; + dp2 = pt2 - p; + + if(dp1.CrossScalar(dp2) < 0) + { + continue; + } + + // this point is outside the subsector leaf + return false; + } + + return true; +} + +// +// kexDoomMap::LineIntersectSubSector +// + +bool kexDoomMap::LineIntersectSubSector(const kexVec3 &start, const kexVec3 &end, + const mapSubSector_t *sub, kexVec2 &out) +{ + surface_t *surf; + kexVec2 p1, p2; + kexVec2 s1, s2; + kexVec2 pt; + kexVec2 v; + float d, u; + float newX; + float ab; + int i; + + surf = leafSurfaces[0][sub - mapSSects]; + p1 = start.ToVec2(); + p2 = end.ToVec2(); + + for(i = 0; i < surf->numVerts; i++) + { + s1 = surf->verts[i].ToVec2(); + s2 = surf->verts[(i+1)%surf->numVerts].ToVec2(); + + if((p1 == p2) || (s1 == s2)) + { + // zero length + continue; + } + + if((p1 == s1) || (p2 == s1) || (p1 == s2) || (p2 == s2)) + { + // shares end point + continue; + } + + // translate to origin + pt = p2 - p1; + s1 -= p1; + s2 -= p1; + + // normalize + u = pt.UnitSq(); + d = kexMath::InvSqrt(u); + v = (pt * d); + + // rotate points s1 and s2 so they're on the positive x axis + newX = s1.Dot(v); + s1.y = s1.CrossScalar(v); + s1.x = newX; + + newX = s2.Dot(v); + s2.y = s2.CrossScalar(v); + s2.x = newX; + + if((s1.y < 0 && s2.y < 0) || (s1.y >= 0 && s2.y >= 0)) + { + // s1 and s2 didn't cross + continue; + } + + ab = s2.x + (s1.x - s2.x) * s2.y / (s2.y - s1.y); + + if(ab < 0 || ab > (u * d)) + { + // s1 and s2 crosses but outside of points p1 and p2 + continue; + } + + // intersected + out = p1 + (v * ab); + return true; + } + + return false; +} + +// +// kexDoomMap::GetSunColor +// + +const kexVec3 &kexDoomMap::GetSunColor(void) const +{ + if(mapDef != NULL) + { + return mapDef->sunColor; + } + + return defaultSunColor; +} + +// +// kexDoomMap::GetSunDirection +// + +const kexVec3 &kexDoomMap::GetSunDirection(void) const +{ + if(mapDef != NULL) + { + return mapDef->sunDir; + } + + return defaultSunDirection; +} + +// +// kexDoomMap::ParseConfigFile +// + +void kexDoomMap::ParseConfigFile(const char *file) +{ + kexLexer *lexer; + + if(!(lexer = parser->Open(file))) + { + Error("kexDoomMap::ParseConfigFile: %s not found\n", file); + return; + } + + while(lexer->CheckState()) + { + lexer->Find(); + + // check for mapdef block + if(lexer->Matches("mapdef")) + { + mapDef_t mapDef; + + mapDef.map = -1; + mapDef.sunIgnoreTag = 0; + + lexer->ExpectNextToken(TK_LBRACK); + lexer->Find(); + + while(lexer->TokenType() != TK_RBRACK) + { + if(lexer->Matches("map")) + { + mapDef.map = lexer->GetNumber(); + } + else if(lexer->Matches("sun_ignore_tag")) + { + mapDef.sunIgnoreTag = lexer->GetNumber(); + } + else if(lexer->Matches("sun_direction")) + { + mapDef.sunDir = lexer->GetVectorString3(); + } + else if(lexer->Matches("sun_color")) + { + mapDef.sunColor = lexer->GetVectorString3(); + mapDef.sunColor /= 255.0f; + } + + lexer->Find(); + } + + mapDefs.Push(mapDef); + } + + // check for lightdef block + if(lexer->Matches("lightdef")) + { + lightDef_t lightDef; + + lightDef.doomednum = -1; + lightDef.height = 0; + lightDef.intensity = 2; + lightDef.falloff = 1; + lightDef.bCeiling = false; + + lexer->ExpectNextToken(TK_LBRACK); + lexer->Find(); + + while(lexer->TokenType() != TK_RBRACK) + { + if(lexer->Matches("doomednum")) + { + lightDef.doomednum = lexer->GetNumber(); + } + else if(lexer->Matches("rgb")) + { + lightDef.rgb = lexer->GetVectorString3(); + lightDef.rgb /= 255.0f; + } + else if(lexer->Matches("height")) + { + lightDef.height = (float)lexer->GetFloat(); + } + else if(lexer->Matches("radius")) + { + lightDef.radius = (float)lexer->GetFloat(); + } + else if(lexer->Matches("intensity")) + { + lightDef.intensity = (float)lexer->GetFloat(); + } + else if(lexer->Matches("falloff")) + { + lightDef.falloff = (float)lexer->GetFloat(); + } + else if(lexer->Matches("ceiling")) + { + lightDef.bCeiling = true; + } + + lexer->Find(); + } + + lightDefs.Push(lightDef); + } + + if(lexer->Matches("surfaceLight")) + { + surfaceLightDef_t surfaceLight; + + surfaceLight.tag = 0; + surfaceLight.outerCone = 1.0f; + surfaceLight.innerCone = 0; + surfaceLight.falloff = 1.0f; + surfaceLight.intensity = 1.0f; + surfaceLight.distance = 32.0f; + surfaceLight.bIgnoreCeiling = false; + surfaceLight.bIgnoreFloor = false; + surfaceLight.bNoCenterPoint = false; + + lexer->ExpectNextToken(TK_LBRACK); + lexer->Find(); + + while(lexer->TokenType() != TK_RBRACK) + { + if(lexer->Matches("tag")) + { + surfaceLight.tag = lexer->GetNumber(); + } + else if(lexer->Matches("rgb")) + { + surfaceLight.rgb = lexer->GetVectorString3(); + surfaceLight.rgb /= 255.0f; + } + else if(lexer->Matches("cone_outer")) + { + surfaceLight.outerCone = (float)lexer->GetFloat() / 180.0f; + } + else if(lexer->Matches("cone_inner")) + { + surfaceLight.innerCone = (float)lexer->GetFloat() / 180.0f; + } + else if(lexer->Matches("falloff")) + { + surfaceLight.falloff = (float)lexer->GetFloat(); + } + else if(lexer->Matches("intensity")) + { + surfaceLight.intensity = (float)lexer->GetFloat(); + } + else if(lexer->Matches("distance")) + { + surfaceLight.distance = (float)lexer->GetFloat(); + } + else if(lexer->Matches("bIgnoreCeiling")) + { + surfaceLight.bIgnoreCeiling = true; + } + else if(lexer->Matches("bIgnoreFloor")) + { + surfaceLight.bIgnoreFloor = true; + } + else if(lexer->Matches("bNoCenterPoint")) + { + surfaceLight.bNoCenterPoint = true; + } + + lexer->Find(); + } + + surfaceLightDefs.Push(surfaceLight); + } + } + + // we're done with the file + parser->Close(); +} + +// +// kexDoomMap::CreateLights +// + +void kexDoomMap::CreateLights(void) +{ + mapThing_t *thing; + thingLight_t *thingLight; + unsigned int j; + int numSurfLights; + kexVec2 pt; + + // + // add lights from thing sources + // + for(int i = 0; i < numThings; ++i) + { + lightDef_t *lightDef = NULL; + + thing = &mapThings[i]; + + for(j = 0; j < lightDefs.Length(); ++j) + { + if(thing->type == lightDefs[j].doomednum) + { + lightDef = &lightDefs[j]; + break; + } + } + + if(!lightDef) + { + continue; + } + + if(lightDef->radius >= 0) + { + // ignore if all skills aren't set + if(!(thing->options & 7)) + { + continue; + } + } + + thingLight = new thingLight_t; + + thingLight->mapThing = thing; + thingLight->rgb = lightDef->rgb; + thingLight->intensity = lightDef->intensity; + thingLight->falloff = lightDef->falloff; + thingLight->radius = lightDef->radius >= 0 ? lightDef->radius : thing->angle; + thingLight->height = lightDef->height; + thingLight->bCeiling = lightDef->bCeiling; + thingLight->ssect = PointInSubSector(thing->x, thing->y); + thingLight->sector = GetSectorFromSubSector(thingLight->ssect); + + thingLight->origin.Set(thing->x, thing->y); + thingLights.Push(thingLight); + } + + printf("Thing lights: %i\n", thingLights.Length()); + + numSurfLights = 0; + + // + // add surface lights + // + for(j = 0; j < surfaces.Length(); ++j) + { + surface_t *surface = surfaces[j]; + + for(unsigned int k = 0; k < surfaceLightDefs.Length(); ++k) + { + surfaceLightDef_t *surfaceLightDef = &surfaceLightDefs[k]; + + if(surface->type >= ST_MIDDLESEG && surface->type <= ST_LOWERSEG) + { + glSeg_t *seg = (glSeg_t*)surface->data; + + if(mapLines[seg->linedef].tag == surfaceLightDef->tag) + { + kexLightSurface *lightSurface = new kexLightSurface; + + lightSurface->Init(*surfaceLightDef, surface, true, false); + lightSurface->CreateCenterOrigin(); + lightSurfaces.Push(lightSurface); + numSurfLights++; + } + } + else + { + mapSubSector_t *sub = surface->subSector; + mapSector_t *sector = GetSectorFromSubSector(sub); + + if(!sector || surface->numVerts <= 0) + { + // eh.... + continue; + } + + if(surface->type == ST_CEILING && surfaceLightDef->bIgnoreCeiling) + { + continue; + } + + if(surface->type == ST_FLOOR && surfaceLightDef->bIgnoreFloor) + { + continue; + } + + if(sector->tag == surfaceLightDef->tag) + { + kexLightSurface *lightSurface = new kexLightSurface; + + lightSurface->Init(*surfaceLightDef, surface, false, surfaceLightDef->bNoCenterPoint); + lightSurface->Subdivide(16); + lightSurfaces.Push(lightSurface); + numSurfLights++; + } + } + } + } + + printf("Surface lights: %i\n", numSurfLights); +} + +// +// kexDoomMap::CleanupThingLights +// + +void kexDoomMap::CleanupThingLights(void) +{ + for(unsigned int i = 0; i < thingLights.Length(); i++) + { + delete thingLights[i]; + } +} diff --git a/src/lightmap/mapdata.h b/src/lightmap/mapdata.h new file mode 100644 index 0000000..d12ea5b --- /dev/null +++ b/src/lightmap/mapdata.h @@ -0,0 +1,276 @@ +//----------------------------------------------------------------------------- +// 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. +// + +#ifndef __MAPDATA_H__ +#define __MAPDATA_H__ + +#include "wad.h" +#include "surfaces.h" +#include "lightSurface.h" + +#define NO_SIDE_INDEX -1 +#define NO_LINE_INDEX 0xFFFF +#define NF_SUBSECTOR 0x8000 + +enum +{ + BOXTOP, + BOXBOTTOM, + BOXLEFT, + BOXRIGHT +}; + +typedef enum +{ + ML_BLOCKING = 1, // Solid, is an obstacle. + ML_BLOCKMONSTERS = 2, // Blocks monsters only. + ML_TWOSIDED = 4, // Backside will not be present at all if not two sided. + ML_TRANSPARENT1 = 2048, // 25% or 75% transcluency? + ML_TRANSPARENT2 = 4096 // 25% or 75% transcluency? +} mapFlags_t; + +typedef struct +{ + short x; + short y; +} mapVertex_t; + +typedef struct +{ + short textureoffset; + short rowoffset; + char toptexture[8]; + char bottomtexture[8]; + char midtexture[8]; + short sector; +} mapSideDef_t; + +typedef struct +{ + short v1; + short v2; + short flags; + short special; + short tag; + short sidenum[2]; // sidenum[1] will be -1 if one sided +} mapLineDef_t; + +typedef struct +{ + short floorheight; + short ceilingheight; + char floorpic[8]; + char ceilingpic[8]; + short lightlevel; + short special; + short tag; +} mapSector_t; + +typedef struct +{ + // Partition line from (x,y) to x+dx,y+dy) + short x; + short y; + short dx; + short dy; + + // Bounding box for each child, + // clip against view frustum. + short bbox[2][4]; + + // If NF_SUBSECTOR its a subsector, + // else it's a node of another subtree. + word children[2]; +} mapNode_t; + +typedef struct +{ + word v1; + word v2; + short angle; + word linedef; + short side; + short offset; +} mapSeg_t; + +typedef struct mapSubSector_s +{ + word numsegs; + word firstseg; +} mapSubSector_t; + +typedef struct +{ + short x; + short y; + short angle; + short type; + short options; +} mapThing_t; + +typedef struct +{ + int x; + int y; +} glVert_t; + +typedef struct +{ + word v1; + word v2; + word linedef; + int16_t side; + word partner; +} glSeg_t; + +typedef struct +{ + float x; + float y; +} vertex_t; + +typedef struct +{ + vertex_t *vertex; + glSeg_t *seg; +} leaf_t; + +typedef struct +{ + int doomednum; + float height; + float radius; + float intensity; + float falloff; + bool bCeiling; + kexVec3 rgb; +} lightDef_t; + +typedef struct +{ + int map; + int sunIgnoreTag; + kexVec3 sunDir; + kexVec3 sunColor; +} mapDef_t; + +typedef struct +{ + mapThing_t *mapThing; + kexVec2 origin; + kexVec3 rgb; + float intensity; + float falloff; + float height; + float radius; + bool bCeiling; + mapSector_t *sector; + mapSubSector_t *ssect; +} thingLight_t; + +class kexDoomMap +{ +public: + kexDoomMap(void); + ~kexDoomMap(void); + + void BuildMapFromWad(kexWadFile &wadFile); + mapSideDef_t *GetSideDef(const glSeg_t *seg); + mapSector_t *GetFrontSector(const glSeg_t *seg); + mapSector_t *GetBackSector(const glSeg_t *seg); + mapSector_t *GetSectorFromSubSector(const mapSubSector_t *sub); + mapSubSector_t *PointInSubSector(const int x, const int y); + bool PointInsideSubSector(const float x, const float y, const mapSubSector_t *sub); + bool LineIntersectSubSector(const kexVec3 &start, const kexVec3 &end, + const mapSubSector_t *sub, kexVec2 &out); + vertex_t *GetSegVertex(int index); + bool CheckPVS(mapSubSector_t *s1, mapSubSector_t *s2); + + void ParseConfigFile(const char *file); + void CreateLights(void); + void CleanupThingLights(void); + + const kexVec3 &GetSunColor(void) const; + const kexVec3 &GetSunDirection(void) const; + + mapThing_t *mapThings; + mapLineDef_t *mapLines; + mapVertex_t *mapVerts; + mapSideDef_t *mapSides; + mapSector_t *mapSectors; + glSeg_t *mapSegs; + mapSubSector_t *mapSSects; + mapNode_t *nodes; + leaf_t *leafs; + vertex_t *vertexes; + byte *mapPVS; + + bool *bSkySectors; + bool *bSSectsVisibleToSky; + + int numThings; + int numLines; + int numVerts; + int numSides; + int numSectors; + int numSegs; + int numSSects; + int numNodes; + int numLeafs; + int numVertexes; + + int *segLeafLookup; + int *ssLeafLookup; + int *ssLeafCount; + kexBBox *ssLeafBounds; + + kexBBox *nodeBounds; + + surface_t **segSurfaces[3]; + surface_t **leafSurfaces[2]; + + kexArray thingLights; + kexArray lightSurfaces; + +private: + void BuildLeafs(void); + void BuildNodeBounds(void); + void CheckSkySectors(void); + void BuildVertexes(kexWadFile &wadFile); + void BuildPVS(void); + + kexArray lightDefs; + kexArray surfaceLightDefs; + kexArray mapDefs; + + mapDef_t *mapDef; + + static const kexVec3 defaultSunColor; + static const kexVec3 defaultSunDirection; +}; + +#endif diff --git a/src/lightmap/surfaces.cpp b/src/lightmap/surfaces.cpp new file mode 100644 index 0000000..f39e8c0 --- /dev/null +++ b/src/lightmap/surfaces.cpp @@ -0,0 +1,364 @@ +//----------------------------------------------------------------------------- +// 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: Prepares geometry from map structures +// +//----------------------------------------------------------------------------- + +#include "common.h" +#include "surfaces.h" +#include "mapData.h" + +//#define EXPORT_OBJ + +kexArray surfaces; + +// +// Surface_AllocateFromSeg +// + +static void Surface_AllocateFromSeg(kexDoomMap &doomMap, glSeg_t *seg) +{ + mapSideDef_t *side; + surface_t *surf; + float top, bTop; + float bottom, bBottom; + mapSector_t *front; + mapSector_t *back; + vertex_t *v1; + vertex_t *v2; + + if(seg->linedef == NO_LINE_INDEX) + { + return; + } + + side = doomMap.GetSideDef(seg); + front = doomMap.GetFrontSector(seg); + back = doomMap.GetBackSector(seg); + + top = front->ceilingheight; + bottom = front->floorheight; + + v1 = doomMap.GetSegVertex(seg->v1); + v2 = doomMap.GetSegVertex(seg->v2); + + if(back) + { + bTop = back->ceilingheight; + bBottom = back->floorheight; + + if(bTop == top && bBottom == bottom) + { + return; + } + + // bottom seg + if(bottom < bBottom) + { + if(side->bottomtexture[0] != '-') + { + surf = (surface_t*)Mem_Calloc(sizeof(surface_t), hb_static); + surf->numVerts = 4; + surf->verts = (kexVec3*)Mem_Calloc(sizeof(kexVec3) * 4, hb_static); + + surf->verts[0].x = surf->verts[2].x = v1->x; + surf->verts[0].y = surf->verts[2].y = v1->y; + surf->verts[1].x = surf->verts[3].x = v2->x; + surf->verts[1].y = surf->verts[3].y = v2->y; + surf->verts[0].z = surf->verts[1].z = bottom; + surf->verts[2].z = surf->verts[3].z = bBottom; + + surf->plane.SetNormal(surf->verts[0], surf->verts[1], surf->verts[2]); + surf->plane.SetDistance(surf->verts[0]); + surf->type = ST_LOWERSEG; + surf->typeIndex = seg - doomMap.mapSegs; + surf->subSector = &doomMap.mapSSects[doomMap.segLeafLookup[seg - doomMap.mapSegs]]; + + doomMap.segSurfaces[1][surf->typeIndex] = surf; + surf->data = (glSeg_t*)seg; + + surfaces.Push(surf); + } + + bottom = bBottom; + } + + // top seg + if(top > bTop) + { + bool bSky = false; + int frontidx = front-doomMap.mapSectors; + int backidx = back-doomMap.mapSectors; + + if(doomMap.bSkySectors[frontidx] && doomMap.bSkySectors[backidx]) + { + if(front->ceilingheight != back->ceilingheight && side->toptexture[0] == '-') + { + bSky = true; + } + } + + if(side->toptexture[0] != '-' || bSky) + { + surf = (surface_t*)Mem_Calloc(sizeof(surface_t), hb_static); + surf->numVerts = 4; + surf->verts = (kexVec3*)Mem_Calloc(sizeof(kexVec3) * 4, hb_static); + + surf->verts[0].x = surf->verts[2].x = v1->x; + surf->verts[0].y = surf->verts[2].y = v1->y; + surf->verts[1].x = surf->verts[3].x = v2->x; + surf->verts[1].y = surf->verts[3].y = v2->y; + surf->verts[0].z = surf->verts[1].z = bTop; + surf->verts[2].z = surf->verts[3].z = top; + + surf->plane.SetNormal(surf->verts[0], surf->verts[1], surf->verts[2]); + surf->plane.SetDistance(surf->verts[0]); + surf->type = ST_UPPERSEG; + surf->typeIndex = seg - doomMap.mapSegs; + surf->bSky = bSky; + surf->subSector = &doomMap.mapSSects[doomMap.segLeafLookup[seg - doomMap.mapSegs]]; + + doomMap.segSurfaces[2][surf->typeIndex] = surf; + surf->data = (glSeg_t*)seg; + + surfaces.Push(surf); + } + + top = bTop; + } + } + + // middle seg + if(back == NULL) + { + surf = (surface_t*)Mem_Calloc(sizeof(surface_t), hb_static); + surf->numVerts = 4; + surf->verts = (kexVec3*)Mem_Calloc(sizeof(kexVec3) * 4, hb_static); + + surf->verts[0].x = surf->verts[2].x = v1->x; + surf->verts[0].y = surf->verts[2].y = v1->y; + surf->verts[1].x = surf->verts[3].x = v2->x; + surf->verts[1].y = surf->verts[3].y = v2->y; + surf->verts[0].z = surf->verts[1].z = bottom; + surf->verts[2].z = surf->verts[3].z = top; + + surf->plane.SetNormal(surf->verts[0], surf->verts[1], surf->verts[2]); + surf->plane.SetDistance(surf->verts[0]); + surf->type = ST_MIDDLESEG; + surf->typeIndex = seg - doomMap.mapSegs; + surf->subSector = &doomMap.mapSSects[doomMap.segLeafLookup[seg - doomMap.mapSegs]]; + + doomMap.segSurfaces[0][surf->typeIndex] = surf; + surf->data = (glSeg_t*)seg; + + surfaces.Push(surf); + } +} + +// +// Surface_AllocateFromLeaf +// +// Plane normals should almost always be known +// unless slopes are involved.... +// + +static void Surface_AllocateFromLeaf(kexDoomMap &doomMap) +{ + surface_t *surf; + leaf_t *leaf; + mapSector_t *sector = NULL; + int i; + int j; + + printf("------------- Building leaf surfaces -------------\n"); + + doomMap.leafSurfaces[0] = (surface_t**)Mem_Calloc(sizeof(surface_t*) * + doomMap.numSSects, hb_static); + doomMap.leafSurfaces[1] = (surface_t**)Mem_Calloc(sizeof(surface_t*) * + doomMap.numSSects, hb_static); + + for(i = 0; i < doomMap.numSSects; i++) + { + printf("subsectors: %i / %i\r", i+1, doomMap.numSSects); + + if(doomMap.ssLeafCount[i] < 3) + { + continue; + } + + sector = doomMap.GetSectorFromSubSector(&doomMap.mapSSects[i]); + + // I will be NOT surprised if some users tries to do something stupid with + // sector hacks + if(sector == NULL) + { + Error("Surface_AllocateFromLeaf: subsector %i has no sector\n", i); + return; + } + + surf = (surface_t*)Mem_Calloc(sizeof(surface_t), hb_static); + surf->numVerts = doomMap.ssLeafCount[i]; + surf->verts = (kexVec3*)Mem_Calloc(sizeof(kexVec3) * surf->numVerts, hb_static); + surf->subSector = &doomMap.mapSSects[i]; + + // floor verts + for(j = 0; j < surf->numVerts; j++) + { + leaf = &doomMap.leafs[doomMap.ssLeafLookup[i] + (surf->numVerts - 1) - j]; + + surf->verts[j].x = leaf->vertex->x; + surf->verts[j].y = leaf->vertex->y; + surf->verts[j].z = sector->floorheight; + } + + surf->plane.SetNormal(kexVec3(0, 0, 1)); + surf->plane.SetDistance(surf->verts[0]); + surf->type = ST_FLOOR; + surf->typeIndex = i; + + doomMap.leafSurfaces[0][i] = surf; + surf->data = (mapSector_t*)sector; + + surfaces.Push(surf); + + surf = (surface_t*)Mem_Calloc(sizeof(surface_t), hb_static); + surf->numVerts = doomMap.ssLeafCount[i]; + surf->verts = (kexVec3*)Mem_Calloc(sizeof(kexVec3) * surf->numVerts, hb_static); + surf->subSector = &doomMap.mapSSects[i]; + + if(doomMap.bSkySectors[sector-doomMap.mapSectors]) + { + surf->bSky = true; + } + + // ceiling verts + for(j = 0; j < surf->numVerts; j++) + { + leaf = &doomMap.leafs[doomMap.ssLeafLookup[i] + j]; + + surf->verts[j].x = leaf->vertex->x; + surf->verts[j].y = leaf->vertex->y; + surf->verts[j].z = sector->ceilingheight; + } + + surf->plane.SetNormal(kexVec3(0, 0, -1)); + surf->plane.SetDistance(surf->verts[0]); + surf->type = ST_CEILING; + surf->typeIndex = i; + + doomMap.leafSurfaces[1][i] = surf; + surf->data = (mapSector_t*)sector; + + surfaces.Push(surf); + } + + printf("\nLeaf surfaces: %i\n", surfaces.Length() - doomMap.numSSects); +} + +// +// Surface_AllocateFromMap +// + +void Surface_AllocateFromMap(kexDoomMap &doomMap) +{ + doomMap.segSurfaces[0] = (surface_t**)Mem_Calloc(sizeof(surface_t*) * + doomMap.numSegs, hb_static); + doomMap.segSurfaces[1] = (surface_t**)Mem_Calloc(sizeof(surface_t*) * + doomMap.numSegs, hb_static); + doomMap.segSurfaces[2] = (surface_t**)Mem_Calloc(sizeof(surface_t*) * + doomMap.numSegs, hb_static); + + printf("------------- Building seg surfaces -------------\n"); + + for(int i = 0; i < doomMap.numSegs; i++) + { + Surface_AllocateFromSeg(doomMap, &doomMap.mapSegs[i]); + printf("segs: %i / %i\r", i+1, doomMap.numSegs); + } + + 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); + } + } + + 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"); + } + + fclose(f); +#endif +} diff --git a/src/lightmap/surfaces.h b/src/lightmap/surfaces.h new file mode 100644 index 0000000..081f5f4 --- /dev/null +++ b/src/lightmap/surfaces.h @@ -0,0 +1,73 @@ +//----------------------------------------------------------------------------- +// 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. +// + +#ifndef __SURFACES_H__ +#define __SURFACES_H__ + +typedef enum +{ + ST_UNKNOWN = 0, + ST_MIDDLESEG, + ST_UPPERSEG, + ST_LOWERSEG, + ST_CEILING, + ST_FLOOR +} surfaceType_t; + +typedef kexArray vertexBatch_t; + +// convert from fixed point(FRACUNIT) to floating point +#define F(x) (((float)(x))/65536.0f) + +typedef struct +{ + kexPlane plane; + int lightmapNum; + int lightmapOffs[2]; + int lightmapDims[2]; + kexVec3 lightmapOrigin; + kexVec3 lightmapSteps[2]; + kexVec3 textureCoords[2]; + kexBBox bounds; + int numVerts; + kexVec3 *verts; + float *lightmapCoords; + surfaceType_t type; + int typeIndex; + void *data; + bool bSky; + struct mapSubSector_s *subSector; +} surface_t; + +extern kexArray surfaces; + +class kexDoomMap; +class kexWadFile; + +void Surface_AllocateFromMap(kexDoomMap &doomMap); + +#endif diff --git a/src/lightmap/trace.cpp b/src/lightmap/trace.cpp new file mode 100644 index 0000000..bcb5c43 --- /dev/null +++ b/src/lightmap/trace.cpp @@ -0,0 +1,285 @@ +//----------------------------------------------------------------------------- +// 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: General class module for handling ray tracing of the +// world geometry. Ideally, all of this needs to be revisited... +// +//----------------------------------------------------------------------------- + +#include "common.h" +#include "mapData.h" +#include "trace.h" + +// +// kexTrace::kexTrace +// + +kexTrace::kexTrace(void) +{ + this->map = NULL; +} + +// +// kexTrace::~kexTrace +// + +kexTrace::~kexTrace(void) +{ +} + +// +// kexTrace::Init +// + +void kexTrace::Init(kexDoomMap &doomMap) +{ + map = &doomMap; +} + +// +// kexTrace::Trace +// + +void kexTrace::Trace(const kexVec3 &startVec, const kexVec3 &endVec) +{ + start = startVec; + end = endVec; + dir = (end - start).Normalize(); + hitNormal.Clear(); + hitVector.Clear(); + hitSurface = NULL; + fraction = 1; + + if(map == NULL) + { + return; + } + + TraceBSPNode(map->numNodes - 1); +} + +// +// kexTrace::TraceSurface +// + +void kexTrace::TraceSurface(surface_t *surface) +{ + kexPlane *plane; + kexVec3 hit; + kexVec3 edge1; + kexVec3 edge2; + kexVec3 normal; + float d1; + float d2; + float d; + float frac; + int i; + kexPluecker r; + + if(surface == NULL) + { + return; + } + + plane = &surface->plane; + + d1 = plane->Distance(start) - plane->d; + d2 = plane->Distance(end) - plane->d; + + if(d1 <= d2 || d1 < 0 || d2 > 0) + { + // trace is either completely in front or behind the plane + return; + } + + frac = (d1 / (d1 - d2)); + + if(frac > 1 || frac < 0) + { + // not a valid contact + return; + } + + if(frac >= fraction) + { + // farther than the current contact + return; + } + + hit = start.Lerp(end, frac); + normal = plane->Normal(); + + r.SetRay(start, dir); + + // segs are always made up of 4 vertices, so its safe to assume 4 edges here + if(surface->type >= ST_MIDDLESEG && surface->type <= ST_LOWERSEG) + { + kexPluecker p1; + kexPluecker p2; + kexPluecker p3; + kexPluecker p4; + byte sideBits = 0; + + p1.SetLine(surface->verts[2], surface->verts[3]); // top edge + p2.SetLine(surface->verts[1], surface->verts[0]); // bottom edge + p3.SetLine(surface->verts[3], surface->verts[1]); // right edge + p4.SetLine(surface->verts[0], surface->verts[2]); // left edge + + // this sucks so much..... I am surprised this even works at all + d = r.InnerProduct(p1)-0.001f; + sideBits |= (FLOATSIGNBIT(d) << 0); + d = r.InnerProduct(p2)-0.001f; + sideBits |= (FLOATSIGNBIT(d) << 1); + d = r.InnerProduct(p3)-0.001f; + sideBits |= (FLOATSIGNBIT(d) << 2); + d = r.InnerProduct(p4)-0.001f; + sideBits |= (FLOATSIGNBIT(d) << 3); + + if(sideBits != 0xF) + { + return; + } + } + else if(surface->type == ST_FLOOR || surface->type == ST_CEILING) + { + kexPluecker p; + kexVec3 v1; + kexVec3 v2; + + for(i = 0; i < surface->numVerts; i++) + { + v1 = surface->verts[i]; + v2 = surface->verts[(i+1)%surface->numVerts]; + + p.SetLine(v2, v1); + + if(r.InnerProduct(p) > 0.01f) + { + return; + } + } + } + + hitNormal = normal; + hitVector = hit; + hitSurface = surface; + fraction = frac; +} + +// +// kexTrace::TraceSubSector +// + +void kexTrace::TraceSubSector(int num) +{ + mapSubSector_t *sub; + int i; + int j; + + sub = &map->mapSSects[num]; + + if(!map->ssLeafBounds[num].LineIntersect(start, end)) + { + return; + } + + // test line segments + for(i = 0; i < sub->numsegs; i++) + { + int segnum = sub->firstseg + i; + + for(j = 0; j < 3; j++) + { + if(j == 0) + { + int linenum = map->mapSegs[segnum].linedef; + + if(linenum != NO_LINE_INDEX && map->mapLines[linenum].flags & + (ML_TWOSIDED|ML_TRANSPARENT1|ML_TRANSPARENT2)) + { + // don't trace transparent 2-sided lines + continue; + } + } + TraceSurface(map->segSurfaces[j][segnum]); + } + } + + // test subsector leafs + for(j = 0; j < 2; j++) + { + TraceSurface(map->leafSurfaces[j][num]); + } +} + +// +// kexTrace::TraceBSPNode +// + +void kexTrace::TraceBSPNode(int num) +{ + mapNode_t *node; + kexVec3 dp1; + kexVec3 dp2; + float d; + byte side; + + if(num & NF_SUBSECTOR) + { + TraceSubSector(num & (~NF_SUBSECTOR)); + return; + } + + if(!map->nodeBounds[num & (~NF_SUBSECTOR)].LineIntersect(start, end)) + { + return; + } + + node = &map->nodes[num]; + + kexVec3 pt1(F(node->x << 16), F(node->y << 16), 0); + kexVec3 pt2(F(node->dx << 16), F(node->dy << 16), 0); + + dp1 = pt1 - start; + dp2 = (pt2 + pt1) - start; + d = dp1.Cross(dp2).z; + + side = FLOATSIGNBIT(d); + + TraceBSPNode(node->children[side ^ 1]); + + dp1 = pt1 - end; + dp2 = (pt2 + pt1) - end; + d = dp1.Cross(dp2).z; + + // don't trace if both ends of the ray are on the same side + if(side != FLOATSIGNBIT(d)) + { + TraceBSPNode(node->children[side]); + } +} diff --git a/src/lightmap/trace.h b/src/lightmap/trace.h new file mode 100644 index 0000000..285800c --- /dev/null +++ b/src/lightmap/trace.h @@ -0,0 +1,59 @@ +//----------------------------------------------------------------------------- +// 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. +// + +#ifndef __TRACE_H__ +#define __TRACE_H__ + +class kexDoomMap; + +class kexTrace +{ +public: + kexTrace(void); + ~kexTrace(void); + + void Init(kexDoomMap &doomMap); + void Trace(const kexVec3 &startVec, const kexVec3 &endVec); + + kexVec3 start; + kexVec3 end; + kexVec3 dir; + kexVec3 hitNormal; + kexVec3 hitVector; + surface_t *hitSurface; + float fraction; + +private: + void TraceBSPNode(int num); + void TraceSubSector(int num); + void TraceSurface(surface_t *surface); + + kexDoomMap *map; + +}; + +#endif diff --git a/src/lightmap/wad.cpp b/src/lightmap/wad.cpp new file mode 100644 index 0000000..704d093 --- /dev/null +++ b/src/lightmap/wad.cpp @@ -0,0 +1,326 @@ +//----------------------------------------------------------------------------- +// 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: General wad loading mechanics +// +//----------------------------------------------------------------------------- + +#include "common.h" +#include "wad.h" + +// +// kexWadFile::~kexWadFile +// + +kexWadFile::~kexWadFile(void) +{ + Close(); +} + +// +// kexWadFile::Open +// + +bool kexWadFile::Open(const char *fileName) +{ + if(!file.Open(fileName)) + { + return false; + } + + memcpy(&header, file.Buffer(), sizeof(wadHeader_t)); + lumps = (lump_t*)file.GetOffset(2); + wadName = fileName; + bWriting = false; + return true; +} + +// +// kexWadFile::Write +// + +void kexWadFile::Write(const char *fileName) +{ + assert(bWriting == true); + + file.Create(fileName); + file.Write8(header.id[0]); + file.Write8(header.id[1]); + file.Write8(header.id[2]); + file.Write8(header.id[3]); + file.Write32(header.lmpcount); + file.Write32(header.lmpdirpos); + + for(unsigned int i = 0; i < writeLumpList.Length(); i++) + { + byte *data = writeDataList[i]; + + if(!data) + { + continue; + } + + for(int j = 0; j < writeLumpList[i].size; j++) + { + file.Write8(data[j]); + } + } + + for(unsigned int i = 0; i < writeLumpList.Length(); i++) + { + file.Write32(writeLumpList[i].filepos); + file.Write32(writeLumpList[i].size); + for(int j = 0; j < 8; j++) + { + file.Write8(writeLumpList[i].name[j]); + } + } +} + +// +// kexWadFile::Close +// + +void kexWadFile::Close(void) +{ + if(file.Opened()) + { + file.Close(); + } +} + +// +// kexWadFile::GetLumpFromName +// + +lump_t *kexWadFile::GetLumpFromName(const char *name) +{ + char n[9]; + + for(int i = 0; i < header.lmpcount; i++) + { + // could be optimized here but I really don't feel like + // wasting time on this + strncpy(n, lumps[i].name, 8); + n[8] = 0; + + if(!strncmp(n, name, 8)) + { + return &lumps[i]; + } + } + + return NULL; +} + +// +// kexWadFile::GetLumpData +// + +byte *kexWadFile::GetLumpData(const lump_t *lump) +{ + file.SetOffset(lump->filepos); + return &file.Buffer()[lump->filepos]; +} + +// +// kexWadFile::GetLumpData +// + +byte *kexWadFile::GetLumpData(const char *name) +{ + lump_t *lump = GetLumpFromName(name); + + if(!lump) + { + return NULL; + } + + file.SetOffset(lump->filepos); + return &file.Buffer()[lump->filepos]; +} + +// +// kexWadFile::SetCurrentMap +// + +void kexWadFile::SetCurrentMap(const int map) +{ + lump_t *lump = GetLumpFromName(Va("MAP%02d", map)); + + if(lump == NULL) + { + Error("kexWadFile::SetCurrentMap: MAP%02d not found\n", map); + return; + } + + mapLumpID = lump - lumps; + + lump = GetLumpFromName(Va("GL_MAP%02d", map)); + + if(lump == NULL) + { + Error("kexWadFile::SetCurrentMap: GL_MAP%02d not found\n", map); + return; + } + + glMapLumpID = lump - lumps; + currentmap = map; +} + +// +// kexWadFile::GetMapLump +// + +lump_t *kexWadFile::GetMapLump(mapLumps_t lumpID) +{ + if(mapLumpID + lumpID >= header.lmpcount) + { + return NULL; + } + + return &lumps[mapLumpID + lumpID]; +} + +// +// kexWadFile::GetGLMapLump +// + +lump_t *kexWadFile::GetGLMapLump(glMapLumps_t lumpID) +{ + if(glMapLumpID + lumpID >= header.lmpcount) + { + return NULL; + } + + return &lumps[glMapLumpID + lumpID]; +} + +// +// kexWadFile::InitForWrite +// + +void kexWadFile::InitForWrite(void) +{ + header.id[0] = 'P'; + header.id[1] = 'W'; + header.id[2] = 'A'; + header.id[3] = 'D'; + + writePos = sizeof(wadHeader_t); + bWriting = true; + + header.lmpcount = 0; + header.lmpdirpos = writePos; +} + +// +// kexWadFile::CopyLumpsFromWadFile +// + +void kexWadFile::CopyLumpsFromWadFile(kexWadFile &wadFile) +{ + lump_t lump; + + assert(bWriting == true); + + for(int i = 0; i < wadFile.header.lmpcount; ++i) + { + lump = wadFile.lumps[i]; + AddLump(wadFile.lumps[i].name, lump.size, &wadFile.file.Buffer()[lump.filepos]); + } +} + +// +// kexWadFile::CopyLumpsFromWadFile +// + +void kexWadFile::CopyLumpsFromWadFile(kexWadFile &wadFile, kexArray &lumpIgnoreList) +{ + lump_t lump; + unsigned int j; + bool bSkip; + + assert(bWriting == true); + + for(int i = 0; i < wadFile.header.lmpcount; ++i) + { + lump = wadFile.lumps[i]; + bSkip = false; + + for(j = 0; j < lumpIgnoreList.Length(); ++j) + { + if(lumpIgnoreList[j] == i) + { + bSkip = true; + break; + } + } + + if(bSkip) + { + continue; + } + + AddLump(wadFile.lumps[i].name, lump.size, &wadFile.file.Buffer()[lump.filepos]); + } +} + +// +// kexWadFile::AddLump +// + +void kexWadFile::AddLump(const char *name, const int size, byte *data) +{ + lump_t lump; + + assert(bWriting == true); + + lump.filepos = writePos; + lump.size = size; + strncpy(lump.name, name, 8); + + writeLumpList.Push(lump); + writeDataList.Push(data); + + header.lmpcount++; + header.lmpdirpos += lump.size; + writePos += lump.size; +} + +// +// kexWadFile::CreateBackup +// + +void kexWadFile::CreateBackup(void) +{ + kexStr backupName = wadName; + + backupName.StripExtension(); + file.Duplicate(backupName + ".wad.bak"); +} diff --git a/src/lightmap/wad.h b/src/lightmap/wad.h new file mode 100644 index 0000000..26f61a7 --- /dev/null +++ b/src/lightmap/wad.h @@ -0,0 +1,164 @@ +//----------------------------------------------------------------------------- +// 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. +// + +#ifndef __WAD_H__ +#define __WAD_H__ + +#include "kexlib/binFile.h" + +typedef enum +{ + ML_HEADER = 0, + ML_THINGS, + ML_LINEDEFS, + ML_SIDEDEFS, + ML_VERTEXES, + ML_SEGS, + ML_SUBSECTORS, + ML_NODES, + ML_SECTORS, + ML_REJECT, + ML_BLOCKMAP, + ML_LIGHTMAP, + ML_NUMLUMPS +} mapLumps_t; + +typedef enum +{ + ML_GL_LABEL = 0, // A separator name, GL_ExMx or GL_MAPxx + ML_GL_VERTS, // Extra Vertices + ML_GL_SEGS, // Segs, from linedefs & minisegs + ML_GL_SSECT, // SubSectors, list of segs + ML_GL_NODES, // GL BSP nodes + ML_GL_PVS // PVS portals +} glMapLumps_t; + +typedef enum +{ + ML_LM_LABEL = 0, + ML_LM_CELLS, + ML_LM_SUN, + ML_LM_SURFS, + ML_LM_TXCRD, + ML_LM_LMAPS +} lmMapLumps_t; + +#define gNd2 0x32644E67 + +#define GL_VERT_OFFSET 4 + +typedef struct +{ + char id[4]; + int lmpcount; + int lmpdirpos; +} wadHeader_t; + +typedef struct +{ + int filepos; + int size; + char name[8]; +} lump_t; + +class kexWadFile +{ +public: + ~kexWadFile(void); + + wadHeader_t header; + lump_t *lumps; + unsigned int size; + int mapLumpID; + int glMapLumpID; + kexStr wadName; + int currentmap; + + lump_t *GetLumpFromName(const char *name); + lump_t *GetMapLump(mapLumps_t lumpID); + lump_t *GetGLMapLump(glMapLumps_t lumpID); + byte *GetLumpData(const lump_t *lump); + byte *GetLumpData(const char *name); + void SetCurrentMap(const int map); + bool Open(const char *fileName); + void Write(const char *fileName); + void Close(void); + void CreateBackup(void); + void InitForWrite(void); + void CopyLumpsFromWadFile(kexWadFile &wadFile); + void CopyLumpsFromWadFile(kexWadFile &wadFile, kexArray &lumpIgnoreList); + void AddLump(const char *name, const int size, byte *data); + + template + void GetMapLump(mapLumps_t lumpID, type **ptr, int *count) + { + if(mapLumpID + lumpID >= header.lmpcount) + { + return; + } + lump_t *lump = GetMapLump(lumpID); + if(lump == NULL || lump->size == 0) + { + return; + } + *ptr = (type*)GetLumpData(lump); + + if(count) + { + *count = lump->size / sizeof(type); + } + } + + template + void GetGLMapLump(glMapLumps_t lumpID, type **ptr, int *count) + { + if(glMapLumpID + lumpID >= header.lmpcount) + { + return; + } + lump_t *lump = GetGLMapLump(lumpID); + if(lump == NULL || lump->size == 0) + { + return; + } + *ptr = (type*)GetLumpData(lump); + + if(count) + { + *count = lump->size / sizeof(type); + } + } + +private: + kexBinFile file; + bool bWriting; + int writePos; + kexArray writeLumpList; + kexArray writeDataList; +}; + +#endif diff --git a/src/lightmap/worker.cpp b/src/lightmap/worker.cpp new file mode 100644 index 0000000..f1b22e8 --- /dev/null +++ b/src/lightmap/worker.cpp @@ -0,0 +1,418 @@ +//----------------------------------------------------------------------------- +// 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" + +int kexWorker::maxWorkThreads = MAX_THREADS; + +// +// WorkThread +// + +void *WorkThread(void *p) +{ + jobFuncArgs_t *args = (jobFuncArgs_t*)p; + kexWorker *worker = args->worker; + + while(1) + { + int jobid; + + worker->LockMutex(); + + if(worker->FinishedAllJobs()) + { + worker->UnlockMutex(); + break; + } + + jobid = worker->DispatchJob(); + worker->UnlockMutex(); + + worker->RunJob(args->data, jobid); + } + + return 0; +} + +// +// kexWorker::kexWorker +// + +kexWorker::kexWorker(void) +{ + this->numWorkLoad = 0; + this->jobsWorked = 0; + this->job = NULL; + + memset(this->jobArgs, 0, sizeof(this->jobArgs)); +} + +// +// kexWorker::~kexWorker +// + +kexWorker::~kexWorker(void) +{ +} + +// +// kexWorker::LockMutex +// + +void kexWorker::LockMutex(void) +{ + mutex.lock(); +} + +// +// kexWorker::UnlockMutex +// + +void kexWorker::UnlockMutex(void) +{ + mutex.unlock(); +} + +// +// kexWorker::Destroy +// + +void kexWorker::Destroy(void) +{ +} + +// +// 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) + { + threads[i].join(); + } +} + +///////////////////////////////////////////////////////////////////////////// +// Stuff from old main.cpp + +static kexStr basePath; + +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(void) +{ + return time(0); +} + +// +// FilePath +// + +const kexStr &FilePath(void) +{ + return basePath; +} + +// +// Main +// + +#if 0 +int main(int argc, char **argv) +{ + kexWadFile wadFile; + kexWadFile outWadFile; + kexDoomMap doomMap; + lump_t *lmLump; + kexArray 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 diff --git a/src/lightmap/worker.h b/src/lightmap/worker.h new file mode 100644 index 0000000..e339eef --- /dev/null +++ b/src/lightmap/worker.h @@ -0,0 +1,76 @@ +//----------------------------------------------------------------------------- +// 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. +// + +#ifndef __WORKER_H__ +#define __WORKER_H__ + +#include +#include + +#define MAX_THREADS 128 + +class kexWorker; + +typedef void(*jobFunc_t)(void*, int); + +typedef struct +{ + kexWorker *worker; + void *data; + int jobID; +} jobFuncArgs_t; + +class kexWorker +{ +public: + kexWorker(void); + ~kexWorker(void); + + void RunThreads(const int count, void *data, jobFunc_t jobFunc); + void LockMutex(void); + void UnlockMutex(void); + void Destroy(void); + + bool FinishedAllJobs(void) { return jobsWorked == numWorkLoad; } + int DispatchJob(void) { 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(void) 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; +}; + +#endif