From 2add3fb381ea24faabb701697fa7ba17edf95987 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sun, 2 Jan 2011 18:02:27 +0000 Subject: [PATCH] - Merge voxels back into trunk. Even if it needs further tweaking, it should at least be stable now. SVN r3086 (trunk) --- src/CMakeLists.txt | 1 + src/asm_ia32/a.asm | 263 +++++- src/asm_ia32/tmap.asm | 34 +- src/c_dispatch.h | 1 + src/memarena.cpp | 376 ++++++++ src/memarena.h | 83 ++ src/r_data.cpp | 304 ++++++- src/r_data.h | 2 + src/r_defs.h | 95 +- src/r_draw.cpp | 91 ++ src/r_draw.h | 7 +- src/r_drawt.cpp | 60 +- src/r_main.cpp | 27 +- src/r_main.h | 3 +- src/r_things.cpp | 1329 +++++++++++++++++++++++++--- src/r_things.h | 8 + src/resourcefiles/file_wad.cpp | 1 + src/resourcefiles/resourcefile.cpp | 3 +- src/tables.h | 2 +- src/textures/buildtexture.cpp | 2 + src/v_video.cpp | 56 +- src/v_video.h | 5 +- src/w_wad.h | 1 + zdoom.vcproj | 8 + 24 files changed, 2556 insertions(+), 206 deletions(-) create mode 100644 src/memarena.cpp create mode 100644 src/memarena.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 992e8bb146..1bfe9ab7ce 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -658,6 +658,7 @@ add_executable( zdoom WIN32 m_misc.cpp m_png.cpp m_random.cpp + memarena.cpp md5.cpp name.cpp nodebuild.cpp diff --git a/src/asm_ia32/a.asm b/src/asm_ia32/a.asm index d68fba116f..eaf7474550 100644 --- a/src/asm_ia32/a.asm +++ b/src/asm_ia32/a.asm @@ -28,6 +28,9 @@ %define setupmvlineasm _setupmvlineasm %define mvlineasm1 _mvlineasm1 %define mvlineasm4 _mvlineasm4 + +%define R_SetupDrawSlab _R_SetupDrawSlab +%define R_DrawSlab _R_DrawSlab %endif EXTERN ylookup ; near @@ -44,9 +47,6 @@ EXTERN dc_dest EXTERN dc_source EXTERN dc_texturefrac -mvlineasm4_counter: - dd 0 - SECTION .text ALIGN 16 @@ -59,8 +59,45 @@ setvlinebpl_: mov [fixchain2ma+2], eax mov [fixchain2mb+2], eax selfmod fixchain1a, fixchain2mb+6 + +setdrawslabbpl: + mov dword [voxbpl1+2], eax + mov dword [voxbpl2+2], eax + mov dword [voxbpl3+2], eax + mov dword [voxbpl4+2], eax + mov dword [voxbpl5+2], eax + mov dword [voxbpl6+2], eax + mov dword [voxbpl7+2], eax + mov dword [voxbpl8+2], eax + selfmod voxbpl1, voxpl8+6 ret + SECTION .data + +lastslabcolormap: + dd 4 + + SECTION .text + +GLOBAL R_SetupDrawSlab +GLOBAL @R_SetupDrawSlab@4 +R_SetupDrawSlab: + mov ecx, [esp+4] +@R_SetupDrawSlab@4: + cmp [lastslabcolormap], ecx + je .done + mov [lastslabcolormap], ecx + mov dword [voxpal1+2], ecx + mov dword [voxpal2+2], ecx + mov dword [voxpal3+2], ecx + mov dword [voxpal4+2], ecx + mov dword [voxpal5+2], ecx + mov dword [voxpal6+2], ecx + mov dword [voxpal7+2], ecx + mov dword [voxpal8+2], ecx +.done ret + + ; pass it log2(texheight) ALIGN 16 @@ -549,6 +586,226 @@ mvcase0: jmp beginmvlineasm4 align 16 + +;************************************************************************* +;***************************** Voxel Slabs ******************************* +;************************************************************************* + +GLOBAL R_DrawSlab +R_DrawSlab: + push ebx + push ebp + push esi + push edi + + mov eax, [esp+5*4+0] + mov ebx, [esp+5*4+4] + mov ecx, [esp+5*4+8] + mov edx, [esp+5*4+12] + mov esi, [esp+5*4+16] + mov edi, [esp+5*4+20] + + cmp eax, 2 + je voxbegdraw2 + ja voxskip2 + xor eax, eax +voxbegdraw1: + mov ebp, ebx + shr ebp, 16 + add ebx, edx + dec ecx + mov al, byte [esi+ebp] +voxpal1: mov al, byte [eax+88888888h] + mov byte [edi], al +voxbpl1: lea edi, [edi+88888888h] + jnz voxbegdraw1 + jmp voxskipslab5 + +voxbegdraw2: + mov ebp, ebx + shr ebp, 16 + add ebx, edx + xor eax, eax + dec ecx + mov al, byte [esi+ebp] +voxpal2: mov al, byte [eax+88888888h] + mov ah, al + mov word [edi], ax +voxbpl2: lea edi, [edi+88888888h] + jnz voxbegdraw2 + jmp voxskipslab5 + +voxskip2: + cmp eax, 4 + jne voxskip4 + xor eax, eax +voxbegdraw4: + mov ebp, ebx + add ebx, edx + shr ebp, 16 + xor eax, eax + mov al, byte [esi+ebp] +voxpal3: mov al, byte [eax+88888888h] + mov ah, al + shl eax, 8 + mov al, ah + shl eax, 8 + mov al, ah + mov dword [edi], eax +voxbpl3: add edi, 88888888h + dec ecx + jnz voxbegdraw4 + jmp voxskipslab5 + +voxskip4: + add eax, edi + + test edi, 1 + jz voxskipslab1 + cmp edi, eax + je voxskipslab1 + + push eax + push ebx + push ecx + push edi +voxbegslab1: + mov ebp, ebx + add ebx, edx + shr ebp, 16 + xor eax, eax + mov al, byte [esi+ebp] +voxpal4: mov al, byte [eax+88888888h] + mov byte [edi], al +voxbpl4: add edi, 88888888h + dec ecx + jnz voxbegslab1 + pop edi + pop ecx + pop ebx + pop eax + inc edi + +voxskipslab1: + push eax + test edi, 2 + jz voxskipslab2 + dec eax + cmp edi, eax + jge voxskipslab2 + + push ebx + push ecx + push edi +voxbegslab2: + mov ebp, ebx + add ebx, edx + shr ebp, 16 + xor eax, eax + mov al, byte [esi+ebp] +voxpal5: mov al, byte [eax+88888888h] + mov ah, al + mov word [edi], ax +voxbpl5: add edi, 88888888h + dec ecx + jnz voxbegslab2 + pop edi + pop ecx + pop ebx + add edi, 2 + +voxskipslab2: + mov eax, [esp] + + sub eax, 3 + cmp edi, eax + jge voxskipslab3 + +voxprebegslab3: + push ebx + push ecx + push edi +voxbegslab3: + mov ebp, ebx + add ebx, edx + shr ebp, 16 + xor eax, eax + mov al, byte [esi+ebp] +voxpal6: mov al, byte [eax+88888888h] + mov ah, al + shl eax, 8 + mov al, ah + shl eax, 8 + mov al, ah + mov dword [edi], eax +voxbpl6: add edi, 88888888h + dec ecx + jnz voxbegslab3 + pop edi + pop ecx + pop ebx + add edi, 4 + + mov eax, [esp] + + sub eax, 3 + cmp edi, eax + jl voxprebegslab3 + +voxskipslab3: + mov eax, [esp] + + dec eax + cmp edi, eax + jge voxskipslab4 + + push ebx + push ecx + push edi +voxbegslab4: + mov ebp, ebx + add ebx, edx + shr ebp, 16 + xor eax, eax + mov al, byte [esi+ebp] +voxpal7: mov al, byte [eax+88888888h] + mov ah, al + mov word [edi], ax +voxbpl7: add edi, 88888888h + dec ecx + jnz voxbegslab4 + pop edi + pop ecx + pop ebx + add edi, 2 + +voxskipslab4: + pop eax + + cmp edi, eax + je voxskipslab5 + +voxbegslab5: + mov ebp, ebx + add ebx, edx + shr ebp, 16 + xor eax, eax + mov al, byte [esi+ebp] +voxpal8: mov al, byte [eax+88888888h] + mov byte [edi], al +voxbpl8: add edi, 88888888h + dec ecx + jnz voxbegslab5 + +voxskipslab5: + pop edi + pop esi + pop ebp + pop ebx + ret + +align 16 + %ifdef M_TARGET_MACHO GLOBAL _rtext_a_end _rtext_a_end: diff --git a/src/asm_ia32/tmap.asm b/src/asm_ia32/tmap.asm index cbcd9f4f15..a32b1492e4 100644 --- a/src/asm_ia32/tmap.asm +++ b/src/asm_ia32/tmap.asm @@ -850,8 +850,8 @@ GLOBAL R_DrawColumnHorizP_ASM align 16 @R_DrawColumnHorizP_ASM@0: -R_DrawColumnHorizP_ASM: _R_DrawColumnHorizP_ASM: +R_DrawColumnHorizP_ASM: ; count = dc_yh - dc_yl; @@ -870,8 +870,10 @@ _R_DrawColumnHorizP_ASM: inc eax ; make 0 count mean 0 pixels and edx,3 push eax - mov esi,[dc_ctspan+edx*4] - lea eax,[dc_temp+ecx*4+edx] ; eax = top of column in buffer + mov eax,[dc_temp] + mov esi,[dc_ctspan+edx*4] + add eax,edx + lea eax,[eax+ecx*4] ; eax = top of column in buffer mov ebp,[dc_yh] mov [esi],ecx mov [esi+4],ebp @@ -1102,8 +1104,9 @@ _rt_copy1col_asm: lea esi,[eax*4] inc ebx ; ebx = count mov eax,edx - lea ecx,[dc_temp+ecx+esi] ; ecx = source + add ecx,esi mov edi,[ylookup+esi] + add ecx,[dc_temp] ; ecx = source mov esi,[dc_pitch] ; esi = pitch add eax,edi ; eax = dest add eax,[dc_destorg] @@ -1169,10 +1172,11 @@ _rt_copy4cols_asm: inc ebx ; ebx = count mov eax,ecx mov esi,[ylookup+edx*4] - lea ecx,[dc_temp+edx*4] ; ecx = source - mov edx,[dc_pitch] ; edx = pitch + mov ecx,[dc_temp] add eax,esi ; eax = dest add eax,[dc_destorg] + lea ecx,[ecx+edx*4] ; ecx = source + mov edx,[dc_pitch] ; edx = pitch shr ebx,1 jnc .even @@ -1241,7 +1245,8 @@ _rt_map1col_asm: mov esi,[dc_colormap] ; esi = colormap inc ebx ; ebx = count mov eax,edx - lea ebp,[dc_temp+ecx+edi] ; ebp = source + lea ebp,[ecx+edi] ; ebp = source + add ebp,[dc_temp] mov ecx,[ylookup+edi] mov edi,[dc_pitch] ; edi = pitch add eax,ecx ; eax = dest @@ -1320,7 +1325,8 @@ _rt_map4cols_asm1: mov eax,ecx inc ebx ; ebx = count mov edi,[ylookup+edx] - lea ebp,[dc_temp+edx] ; ebp = source + mov ebp,[dc_temp] + add ebp,edx ; ebp = source add eax,edi ; eax = dest mov edi,[dc_pitch] ; edi = pitch add eax,[dc_destorg] @@ -1414,7 +1420,8 @@ _rt_map4cols_asm2: mov eax,ecx inc ebx ; ebx = count mov edi,[ylookup+edx] - lea ebp,[dc_temp+edx] ; ebp = source + mov ebp,[dc_temp] + add ebp,edx ; ebp = source add eax,edi ; eax = dest mov edi,[dc_pitch] ; edi = pitch add eax,[dc_destorg] @@ -1493,10 +1500,11 @@ _rt_shaded4cols_asm: add eax,[dc_destorg] ; eax = destination push ebx push esi + mov esi,[dc_temp] inc ebp ; ebp = count add eax,[esp+16] push edi - lea esi,[dc_temp+ecx*4] ; esi = source + lea esi,[esi+ecx*4] ; esi = source align 16 @@ -1580,10 +1588,11 @@ _rt_add4cols_asm: add eax,[dc_destorg] push ebx push esi + mov esi,[dc_temp] push ebp inc edi add eax,[esp+20] - lea esi,[dc_temp+ecx*4] + lea esi,[esi+ecx*4] align 16 a4loop: @@ -1659,10 +1668,11 @@ _rt_addclamp4cols_asm: add eax,[dc_destorg] push ebx push esi + mov esi,[dc_temp] push ebp inc edi add eax,[esp+20] - lea esi,[dc_temp+ecx*4] + lea esi,[esi+ecx*4] push edi align 16 diff --git a/src/c_dispatch.h b/src/c_dispatch.h index ac8685855e..5aa3f98702 100644 --- a/src/c_dispatch.h +++ b/src/c_dispatch.h @@ -166,5 +166,6 @@ void ResetButtonStates (); // Same as above, but also clear bDown extern unsigned int MakeKey (const char *s); extern unsigned int MakeKey (const char *s, size_t len); +extern unsigned int SuperFastHash (const char *data, size_t len); #endif //__C_DISPATCH_H__ diff --git a/src/memarena.cpp b/src/memarena.cpp new file mode 100644 index 0000000000..c44586deef --- /dev/null +++ b/src/memarena.cpp @@ -0,0 +1,376 @@ +/* +** memarena.cpp +** Implements memory arenas. +** +**--------------------------------------------------------------------------- +** Copyright 2010 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +** A memory arena is used for efficient allocation of many small objects that +** will all be freed at once. Note that since individual destructors are not +** called, you must not use an arena to allocate any objects that use a +** destructor, either explicitly or implicitly (because they have members +** with destructors). +*/ + +#include "doomtype.h" +#include "m_alloc.h" +#include "memarena.h" +#include "c_dispatch.h" +#include "zstring.h" + +#define BLOCK_SIZE (10*1024) + +struct FMemArena::Block +{ + Block *NextBlock; + void *Limit; // End of this block + void *Avail; // Start of free space in this block + + void Reset(); + void *Alloc(size_t size); +}; + +//========================================================================== +// +// RoundPointer +// +// Rounds a pointer up to a pointer-sized boundary. +// +//========================================================================== + +static inline void *RoundPointer(void *ptr) +{ + return (void *)(((size_t)ptr + sizeof(void*) - 1) & ~(sizeof(void*) - 1)); +} + +//========================================================================== +// +// FMemArena Constructor +// +//========================================================================== + +FMemArena::FMemArena() +{ + TopBlock = NULL; + FreeBlocks = NULL; +} + +//========================================================================== +// +// FMemArena Destructor +// +//========================================================================== + +FMemArena::~FMemArena() +{ + FreeAllBlocks(); +} + +//========================================================================== +// +// FMemArena :: Alloc +// +//========================================================================== + +void *FMemArena::Alloc(size_t size) +{ + Block *block; + + for (block = TopBlock; block != NULL; block = block->NextBlock) + { + void *res = block->Alloc(size); + if (res != NULL) + { + return res; + } + } + block = AddBlock(size); + return block->Alloc(size); +} + +//========================================================================== +// +// FMemArena :: FreeAll +// +// Moves all blocks to the free list. No system-level deallocation occurs. +// +//========================================================================== + +void FMemArena::FreeAll() +{ + for (Block *next, *block = TopBlock; block != NULL; block = next) + { + next = block->NextBlock; + block->Reset(); + block->NextBlock = FreeBlocks; + FreeBlocks = block; + } + TopBlock = NULL; +} + +//========================================================================== +// +// FMemArena :: FreeAllBlocks +// +// Frees all blocks used by this arena. +// +//========================================================================== + +void FMemArena::FreeAllBlocks() +{ + FreeBlockChain(TopBlock); + FreeBlockChain(FreeBlocks); +} + +//========================================================================== +// +// FMemArena :: FreeBlockChain +// +// Frees a chain of blocks. +// +//========================================================================== + +void FMemArena::FreeBlockChain(Block *&top) +{ + for (Block *next, *block = top; block != NULL; block = next) + { + next = block->NextBlock; + M_Free(block); + } + top = NULL; +} + +//========================================================================== +// +// FMemArena :: AddBlock +// +// Allocates a block large enough to hold at least bytes and adds it +// to the TopBlock chain. +// +//========================================================================== + +FMemArena::Block *FMemArena::AddBlock(size_t size) +{ + Block *mem, **last; + size += sizeof(Block); // Account for header size + + // Search for a free block to use + for (last = &FreeBlocks, mem = FreeBlocks; mem != NULL; last = &mem->NextBlock, mem = mem->NextBlock) + { + if ((BYTE *)mem->Limit - (BYTE *)mem >= (ptrdiff_t)size) + { + *last = mem->NextBlock; + break; + } + } + if (mem == NULL) + { + // Allocate a new block + if (size < BLOCK_SIZE) + { + size = BLOCK_SIZE; + } + else + { // Stick some free space at the end so we can use this block for + // other things. + size += BLOCK_SIZE/2; + } + mem = (Block *)M_Malloc(size); + mem->Limit = (BYTE *)mem + size; + } + mem->Reset(); + mem->NextBlock = TopBlock; + TopBlock = mem; + return mem; +} + +//========================================================================== +// +// FMemArena :: Block :: Reset +// +// Resets this block's Avail pointer. +// +//========================================================================== + +void FMemArena::Block::Reset() +{ + Avail = RoundPointer(this + sizeof(*this)); +} + +//========================================================================== +// +// FMemArena :: Block :: Alloc +// +// Allocates memory from the block if it has space. Returns NULL if not. +// +//========================================================================== + +void *FMemArena::Block::Alloc(size_t size) +{ + if ((char *)Avail + size > Limit) + { + return NULL; + } + void *res = Avail; + Avail = RoundPointer((char *)Avail + size); + return res; +} + +//========================================================================== +// +// FSharedStringArena Constructor +// +//========================================================================== + +FSharedStringArena::FSharedStringArena() +{ + memset(Buckets, 0, sizeof(Buckets)); +} + +//========================================================================== +// +// FSharedStringArena Destructor +// +//========================================================================== + +FSharedStringArena::~FSharedStringArena() +{ + FreeAll(); + // FMemArena destructor will free the blocks. +} + +//========================================================================== +// +// FSharedStringArena :: Alloc +// +// Allocates a new string and initializes it with the passed string. This +// version takes an FString as a parameter, so it won't need to allocate any +// memory for the string text if it already exists in the arena. +// +//========================================================================== + +FString *FSharedStringArena::Alloc(const FString &source) +{ + unsigned int hash; + Node *strnode; + + strnode = FindString(source, source.Len(), hash); + if (strnode == NULL) + { + strnode = (Node *)FMemArena::Alloc(sizeof(Node)); + ::new(&strnode->String) FString(source); + strnode->Hash = hash; + hash %= countof(Buckets); + strnode->Next = Buckets[hash]; + Buckets[hash] = strnode; + } + return &strnode->String; +} + +//========================================================================== +// +// FSharedStringArena :: Alloc +// +//========================================================================== + +FString *FSharedStringArena::Alloc(const char *source) +{ + return Alloc(source, strlen(source)); +} + +//========================================================================== +// +// FSharedStringArena :: Alloc +// +//========================================================================== + +FString *FSharedStringArena::Alloc(const char *source, size_t strlen) +{ + unsigned int hash; + Node *strnode; + + strnode = FindString(source, strlen, hash); + if (strnode == NULL) + { + strnode = (Node *)FMemArena::Alloc(sizeof(Node)); + ::new(&strnode->String) FString(source, strlen); + strnode->Hash = hash; + hash %= countof(Buckets); + strnode->Next = Buckets[hash]; + Buckets[hash] = strnode; + } + return &strnode->String; +} + +//========================================================================== +// +// FSharedStringArena :: FindString +// +// Finds the string if it's already in the arena. Returns NULL if not. +// +//========================================================================== + +FSharedStringArena::Node *FSharedStringArena::FindString(const char *str, size_t strlen, unsigned int &hash) +{ + hash = SuperFastHash(str, strlen); + + for (Node *node = Buckets[hash % countof(Buckets)]; node != NULL; node = node->Next) + { + if (node->Hash == hash && node->String.Len() == strlen && memcmp(&node->String[0], str, strlen) == 0) + { + return node; + } + } + return NULL; +} + +//========================================================================== +// +// FSharedStringArena :: FreeAll +// +// In addition to moving all used blocks onto the free list, all FStrings +// they contain will have their destructors called. +// +//========================================================================== + +void FSharedStringArena::FreeAll() +{ + for (Block *next, *block = TopBlock; block != NULL; block = next) + { + next = block->NextBlock; + void *limit = block->Avail; + block->Reset(); + for (Node *string = (Node *)block->Avail; string < limit; ++string) + { + string->~Node(); + } + block->NextBlock = FreeBlocks; + FreeBlocks = block; + } + memset(Buckets, 0, sizeof(Buckets)); +} diff --git a/src/memarena.h b/src/memarena.h new file mode 100644 index 0000000000..e933fc0728 --- /dev/null +++ b/src/memarena.h @@ -0,0 +1,83 @@ +/* +** memarena.h +** +**--------------------------------------------------------------------------- +** Copyright 2010 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "zstring.h" + +// A general purpose arena. +class FMemArena +{ +public: + FMemArena(); + ~FMemArena(); + + void *Alloc(size_t size); + void FreeAll(); + void FreeAllBlocks(); + +protected: + struct Block; + + Block *AddBlock(size_t size); + void FreeBlockChain(Block *&top); + + Block *TopBlock; + Block *FreeBlocks; +}; + +// An arena specializing in storage of FStrings. It knows how to free them, +// but this means it also should never be used for allocating anything else. +// Identical strings all return the same pointer. +class FSharedStringArena : public FMemArena +{ +public: + FSharedStringArena(); + ~FSharedStringArena(); + void FreeAll(); + + class FString *Alloc(const FString &source); + class FString *Alloc(const char *source); + class FString *Alloc(const char *source, size_t strlen); + +protected: + struct Node + { + Node *Next; + FString String; + unsigned int Hash; + }; + Node *Buckets[256]; + + Node *FindString(const char *str, size_t strlen, unsigned int &hash); +private: + void *Alloc(size_t size) { return NULL; } // No access to FMemArena::Alloc for outsiders. +}; diff --git a/src/r_data.cpp b/src/r_data.cpp index a298cc03ff..8983d5e559 100644 --- a/src/r_data.cpp +++ b/src/r_data.cpp @@ -355,6 +355,304 @@ const BYTE *R_GetColumn (FTexture *tex, int col) return tex->GetColumn (col, NULL); } +//========================================================================== +// +// GetVoxelRemap +// +// Calculates a remap table for the voxel's palette. Results are cached so +// passing the same palette repeatedly will not require repeated +// recalculations. +// +//========================================================================== + +static BYTE *GetVoxelRemap(const BYTE *pal) +{ + static BYTE remap[256]; + static BYTE oldpal[768]; + static bool firsttime = true; + + if (firsttime || memcmp(oldpal, pal, 768) != 0) + { // Not the same palette as last time, so recalculate. + firsttime = false; + memcpy(oldpal, pal, 768); + for (int i = 0; i < 256; ++i) + { + // The voxel palette uses VGA colors, so we have to expand it + // from 6 to 8 bits per component. + remap[i] = BestColor((uint32 *)GPalette.BaseColors, + (oldpal[i*3 + 0] << 2) | (oldpal[i*3 + 0] >> 4), + (oldpal[i*3 + 1] << 2) | (oldpal[i*3 + 1] >> 4), + (oldpal[i*3 + 2] << 2) | (oldpal[i*3 + 2] >> 4)); + } + } + return remap; +} + +//========================================================================== +// +// CopyVoxelSlabs +// +// Copy all the slabs in a block of slabs. +// +//========================================================================== + +static bool CopyVoxelSlabs(kvxslab_t *dest, const kvxslab_t *src, int size) +{ + while (size >= 3) + { + int slabzleng = src->zleng; + + if (3 + slabzleng > size) + { // slab is too tall + return false; + } + + dest->ztop = src->ztop; + dest->zleng = src->zleng; + dest->backfacecull = src->backfacecull; + + for (int j = 0; j < slabzleng; ++j) + { + dest->col[j] = src->col[j]; + } + slabzleng += 3; + src = (kvxslab_t *)((BYTE *)src + slabzleng); + dest = (kvxslab_t *)((BYTE *)dest + slabzleng); + size -= slabzleng; + } + return true; +} + +//========================================================================== +// +// RemapVoxelSlabs +// +// Remaps all the slabs in a block of slabs. +// +//========================================================================== + +static void RemapVoxelSlabs(kvxslab_t *dest, int size, const BYTE *remap) +{ + while (size >= 3) + { + int slabzleng = dest->zleng; + + for (int j = 0; j < slabzleng; ++j) + { + dest->col[j] = remap[dest->col[j]]; + } + slabzleng += 3; + dest = (kvxslab_t *)((BYTE *)dest + slabzleng); + size -= slabzleng; + } +} + +//========================================================================== +// +// R_LoadKVX +// +//========================================================================== + +FVoxel *R_LoadKVX(int lumpnum) +{ + const kvxslab_t *slabs[MAXVOXMIPS]; + FVoxel *voxel = new FVoxel; + const BYTE *rawmip; + int mip, maxmipsize; + int i, j, n; + + FMemLump lump = Wads.ReadLump(lumpnum); // FMemLump adds an extra 0 byte to the end. + BYTE *rawvoxel = (BYTE *)lump.GetMem(); + int voxelsize = (int)(lump.GetSize()-1); + + // Oh, KVX, why couldn't you have a proper header? We'll just go through + // and collect each MIP level, doing lots of range checking, and if the + // last one doesn't end exactly 768 bytes before the end of the file, + // we'll reject it. + + for (mip = 0, rawmip = rawvoxel, maxmipsize = voxelsize - 768 - 4; + mip < MAXVOXMIPS; + mip++) + { + int numbytes = GetInt(rawmip); + if (numbytes > maxmipsize || numbytes < 24) + { + break; + } + rawmip += 4; + + FVoxelMipLevel *mipl = &voxel->Mips[mip]; + + // Load header data. + mipl->SizeX = GetInt(rawmip + 0); + mipl->SizeY = GetInt(rawmip + 4); + mipl->SizeZ = GetInt(rawmip + 8); + mipl->PivotX = GetInt(rawmip + 12); + mipl->PivotY = GetInt(rawmip + 16); + mipl->PivotZ = GetInt(rawmip + 20); + + // How much space do we have for voxdata? + int offsetsize = (mipl->SizeX + 1) * 4 + mipl->SizeX * (mipl->SizeY + 1) * 2; + int voxdatasize = numbytes - 24 - offsetsize; + if (voxdatasize < 0) + { // Clearly, not enough. + break; + } + if (voxdatasize == 0) + { // This mip level is empty. + goto nextmip; + } + + // Allocate slab data space. + mipl->OffsetX = new int[(numbytes - 24 + 3) / 4]; + mipl->OffsetXY = (short *)(mipl->OffsetX + mipl->SizeX + 1); + mipl->SlabData = (BYTE *)(mipl->OffsetXY + mipl->SizeX * (mipl->SizeY + 1)); + + // Load x offsets. + for (i = 0, n = mipl->SizeX; i <= n; ++i) + { + // The X offsets stored in the KVX file are relative to the start of the + // X offsets array. Make them relative to voxdata instead. + mipl->OffsetX[i] = GetInt(rawmip + 24 + i * 4) - offsetsize; + } + + // The first X offset must be 0 (since we subtracted offsetsize), according to the spec: + // NOTE: xoffset[0] = (xsiz+1)*4 + xsiz*(ysiz+1)*2 (ALWAYS) + if (mipl->OffsetX[0] != 0) + { + break; + } + // And the final X offset must point just past the end of the voxdata. + if (mipl->OffsetX[mipl->SizeX] != voxdatasize) + { + break; + } + + // Load xy offsets. + i = 24 + i * 4; + for (j = 0, n *= mipl->SizeY + 1; j < n; ++j) + { + mipl->OffsetXY[j] = GetShort(rawmip + i + j * 2); + } + + // Ensure all offsets are within bounds. + for (i = 0; i < mipl->SizeX; ++i) + { + int xoff = mipl->OffsetX[i]; + for (j = 0; j < mipl->SizeY; ++j) + { + int yoff = mipl->OffsetXY[(mipl->SizeY + 1) * i + j]; + if (unsigned(xoff + yoff) > unsigned(voxdatasize)) + { + goto bad; + } + } + } + + // Record slab location for the end. + slabs[mip] = (kvxslab_t *)(rawmip + 24 + offsetsize); + + // Time for the next mip Level. +nextmip: + rawmip += numbytes; + maxmipsize -= numbytes + 4; + } + // Did we get any mip levels, and if so, does the last one leave just + // enough room for the palette after it? + if (mip == 0 || rawmip != rawvoxel + voxelsize - 768) + { +bad: delete voxel; + return NULL; + } + + // Do not count empty mips at the end. + for (; mip > 0; --mip) + { + if (voxel->Mips[mip - 1].SlabData != NULL) + break; + } + voxel->NumMips = mip; + + for (i = 0; i < mip; ++i) + { + if (!CopyVoxelSlabs((kvxslab_t *)voxel->Mips[i].SlabData, slabs[i], voxel->Mips[i].OffsetX[voxel->Mips[i].SizeX])) + { // Invalid slabs encountered. Reject this voxel. + delete voxel; + return NULL; + } + } + + voxel->LumpNum = lumpnum; + voxel->Palette = new BYTE[768]; + memcpy(voxel->Palette, rawvoxel + voxelsize - 768, 768); + + return voxel; +} + +//========================================================================== +// +// FVoxelMipLevel Constructor +// +//========================================================================== + +FVoxelMipLevel::FVoxelMipLevel() +{ + SizeZ = SizeY = SizeX = 0; + PivotZ = PivotY = PivotX = 0; + OffsetX = NULL; + OffsetXY = NULL; + SlabData = NULL; +} + +//========================================================================== +// +// FVoxelMipLevel Destructor +// +//========================================================================== + +FVoxelMipLevel::~FVoxelMipLevel() +{ + if (OffsetX != NULL) + { + delete[] OffsetX; + } +} + +//========================================================================== +// +// FVoxel Constructor +// +//========================================================================== + +FVoxel::FVoxel() +{ + Palette = NULL; +} + +FVoxel::~FVoxel() +{ + if (Palette != NULL) delete [] Palette; +} + +//========================================================================== +// +// Remap the voxel to the game palette +// +//========================================================================== + +void FVoxel::Remap() +{ + if (Palette != NULL) + { + BYTE *remap = GetVoxelRemap(Palette); + for (int i = 0; i < NumMips; ++i) + { + RemapVoxelSlabs((kvxslab_t *)Mips[i].SlabData, Mips[i].OffsetX[Mips[i].SizeX], remap); + } + delete [] Palette; + Palette = NULL; + } +} //========================================================================== // @@ -389,10 +687,4 @@ CCMD (printspans) Printf ("\n"); } } - -CCMD (picnum) -{ - //int picnum = TexMan.GetTexture (argv[1], FTexture::TEX_Any); - //Printf ("%d: %s - %s\n", picnum, TexMan[picnum]->Name, TexMan(picnum)->Name); -} #endif diff --git a/src/r_data.h b/src/r_data.h index 487860e589..d2f25dfa97 100644 --- a/src/r_data.h +++ b/src/r_data.h @@ -144,4 +144,6 @@ extern size_t numfakecmaps; int R_FindSkin (const char *name, int pclass); // [RH] Find a skin +FVoxel *R_LoadKVX(int lumpnum); + #endif diff --git a/src/r_defs.h b/src/r_defs.h index 9d2822c33a..cf84c65ac1 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -25,6 +25,7 @@ #include "doomdef.h" #include "templates.h" +#include "memarena.h" // Some more or less basic data types // we depend on. @@ -1051,7 +1052,6 @@ struct column_t typedef BYTE lighttable_t; // This could be wider for >8 bit display. - // A vissprite_t is a thing // that will be drawn during a refresh. // I.e. a sprite object that is partly visible. @@ -1059,8 +1059,9 @@ struct vissprite_t { short x1, x2; fixed_t cx; // for line side calculation - fixed_t gx, gy; // for drawseg and fake floor clipping - fixed_t gz, gzt; // global bottom / top for silhouette clipping + fixed_t gx, gy, gz; // origin in world coordinates + angle_t angle; + fixed_t gzb, gzt; // global bottom / top for silhouette clipping fixed_t startfrac; // horizontal position of x1 fixed_t xscale, yscale; fixed_t xiscale; // negative if flipped @@ -1073,12 +1074,17 @@ struct vissprite_t sector_t *sector; // [RH] sector this sprite is in fixed_t alpha; fixed_t floorclip; - FTexture *pic; + union + { + FTexture *pic; + struct FVoxel *voxel; + }; + BYTE bIsVoxel:1; // [RH] Use voxel instead of pic + BYTE bSplitSprite:1; // [RH] Sprite was split by a drawseg + BYTE FakeFlatStat; // [RH] which side of fake/floor ceiling sprite is on short renderflags; DWORD Translation; // [RH] for color translation FRenderStyle RenderStyle; - BYTE FakeFlatStat; // [RH] which side of fake/floor ceiling sprite is on - BYTE bSplitSprite; // [RH] Sprite was split by a drawseg }; enum @@ -1100,14 +1106,16 @@ enum // struct spriteframe_t { + struct FVoxelDef *Voxel;// voxel to use for this frame FTextureID Texture[16]; // texture to use for view angles 0-15 - WORD Flip; // flip (1 = flip) to use for view angles 0-15. + WORD Flip; // flip (1 = flip) to use for view angles 0-15. }; // // A sprite definition: // a number of animation frames. // + struct spritedef_t { union @@ -1140,4 +1148,77 @@ public: int namespc; // namespace for this skin }; + +// [RH] Voxels from Build + +#define MAXVOXMIPS 5 + +struct kvxslab_t +{ + BYTE ztop; // starting z coordinate of top of slab + BYTE zleng; // # of bytes in the color array - slab height + BYTE backfacecull; // low 6 bits tell which of 6 faces are exposed + BYTE col[1/*zleng*/];// color data from top to bottom +}; + +struct FVoxelMipLevel +{ + FVoxelMipLevel(); + ~FVoxelMipLevel(); + + int SizeX; + int SizeY; + int SizeZ; + fixed_t PivotX; // 24.8 fixed point + fixed_t PivotY; // "" + fixed_t PivotZ; // "" + int *OffsetX; + short *OffsetXY; + BYTE *SlabData; +}; + +struct FVoxel +{ + int LumpNum; + int NumMips; + BYTE *Palette; + FVoxelMipLevel Mips[MAXVOXMIPS]; + + FVoxel(); + ~FVoxel(); + void Remap(); +}; + +struct FVoxelDef +{ + FVoxel *Voxel; + int PlacedSpin; // degrees/sec to spin actors without MF_DROPPED set + int DroppedSpin; // degrees/sec to spin actors with MF_DROPPED set + fixed_t Scale; + angle_t AngleOffset; // added to actor's angle to compensate for wrong-facing voxels +}; + +// [RH] A c-buffer. Used for keeping track of offscreen voxel spans. + +struct FCoverageBuffer +{ + struct Span + { + Span *NextSpan; + short Start, Stop; + }; + + FCoverageBuffer(int size); + ~FCoverageBuffer(); + + void Clear(); + void InsertSpan(int listnum, int start, int stop); + Span *AllocSpan(); + + FMemArena SpanArena; + Span **Spans; // [0..NumLists-1] span lists + Span *FreeSpans; + unsigned int NumLists; +}; + #endif diff --git a/src/r_draw.cpp b/src/r_draw.cpp index 8fb3efe364..36c87fa89e 100644 --- a/src/r_draw.cpp +++ b/src/r_draw.cpp @@ -1352,6 +1352,97 @@ void R_FillSpan (void) memset (ylookup[ds_y] + ds_x1 + dc_destorg, ds_color, ds_x2 - ds_x1 + 1); } +// Draw a voxel slab +// +// "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman +// Ken Silverman's official web site: "http://www.advsys.net/ken" +// See the included license file "BUILDLIC.TXT" for license info. + +// Actually, this is just R_DrawColumn with an extra width parameter. + +#ifndef X86_ASM +static const BYTE *slabcolormap; + +extern "C" void R_SetupDrawSlab(const BYTE *colormap) +{ + slabcolormap = colormap; +} + +extern "C" void STACK_ARGS R_DrawSlab(int dx, fixed_t v, int dy, fixed_t vi, const BYTE *vptr, BYTE *p) +{ + int x; + const BYTE *colormap = slabcolormap; + int pitch = dc_pitch; + + assert(dx > 0); + + if (dx == 1) + { + while (dy > 0) + { + *p = colormap[vptr[v >> FRACBITS]]; + p += pitch; + v += vi; + dy--; + } + } + else if (dx == 2) + { + while (dy > 0) + { + BYTE color = colormap[vptr[v >> FRACBITS]]; + p[0] = color; + p[1] = color; + p += pitch; + v += vi; + dy--; + } + } + else if (dx == 3) + { + while (dy > 0) + { + BYTE color = colormap[vptr[v >> FRACBITS]]; + p[0] = color; + p[1] = color; + p[2] = color; + p += pitch; + v += vi; + dy--; + } + } + else if (dx == 4) + { + while (dy > 0) + { + BYTE color = colormap[vptr[v >> FRACBITS]]; + p[0] = color; + p[1] = color; + p[2] = color; + p[3] = color; + p += pitch; + v += vi; + dy--; + } + } + else while (dy > 0) + { + BYTE color = colormap[vptr[v >> FRACBITS]]; + // The optimizer will probably turn this into a memset call. + // Since dx is not likely to be large, I'm not sure that's a good thing, + // hence the alternatives above. + for (x = 0; x < dx; x++) + { + p[x] = color; + } + p += pitch; + v += vi; + dy--; + } +} +#endif + + /****************************************************/ /****************************************************/ diff --git a/src/r_draw.h b/src/r_draw.h index 96e58bf8da..57dfd8712d 100644 --- a/src/r_draw.h +++ b/src/r_draw.h @@ -53,7 +53,7 @@ extern "C" BYTE* palookupoffse[4]; extern "C" const BYTE* bufplce[4]; // [RH] Temporary buffer for column drawing -extern "C" BYTE dc_temp[MAXHEIGHT*4]; +extern "C" BYTE *dc_temp; extern "C" unsigned int dc_tspans[4][MAXHEIGHT]; extern "C" unsigned int *dc_ctspan[4]; extern "C" unsigned int horizspans[4]; @@ -178,7 +178,7 @@ extern void (STACK_ARGS *rt_map4cols)(int sx, int yl, int yh); void rt_draw4cols (int sx); // [RH] Preps the temporary horizontal buffer. -void rt_initcols (void); +void rt_initcols (BYTE *buffer=NULL); void R_DrawFogBoundary (int x1, int x2, short *uclip, short *dclip); @@ -216,6 +216,9 @@ void R_FillColumnP (void); void R_FillColumnHorizP (void); void R_FillSpan (void); +extern "C" void R_SetupDrawSlab(const BYTE *colormap); +extern "C" void STACK_ARGS R_DrawSlab(int dx, fixed_t v, int dy, fixed_t vi, const BYTE *vptr, BYTE *p); + extern "C" int ds_y; extern "C" int ds_x1; extern "C" int ds_x2; diff --git a/src/r_drawt.cpp b/src/r_drawt.cpp index fbfaf6307d..c99bd3afbc 100644 --- a/src/r_drawt.cpp +++ b/src/r_drawt.cpp @@ -52,9 +52,13 @@ // dc_temp is the buffer R_DrawColumnHoriz writes into. // dc_tspans points into it. // dc_ctspan points into dc_tspans. -// But what is horizspan, and what is its relation with dc_ctspan? +// horizspan also points into dc_tspans. -BYTE dc_temp[MAXHEIGHT*4]; +// dc_ctspan is advanced while drawing into dc_temp. +// horizspan is advanced up to dc_ctspan when drawing from dc_temp to the screen. + +BYTE dc_tempbuff[MAXHEIGHT*4]; +BYTE *dc_temp; unsigned int dc_tspans[4][MAXHEIGHT]; unsigned int *dc_ctspan[4]; unsigned int *horizspan[4]; @@ -998,10 +1002,11 @@ void rt_draw4cols (int sx) // Before each pass through a rendering loop that uses these routines, // call this function to set up the span pointers. -void rt_initcols (void) +void rt_initcols (BYTE *buff) { int y; + dc_temp = buff == NULL ? dc_tempbuff : buff; for (y = 3; y >= 0; y--) horizspan[y] = dc_ctspan[y] = &dc_tspans[y][0]; } @@ -1035,26 +1040,18 @@ void R_DrawColumnHorizP_C (void) const BYTE *source = dc_source; if (count & 1) { - *dest = source[frac>>FRACBITS]; - dest += 4; - frac += fracstep; + *dest = source[frac>>FRACBITS]; dest += 4; frac += fracstep; } if (count & 2) { - dest[0] = source[frac>>FRACBITS]; - frac += fracstep; - dest[4] = source[frac>>FRACBITS]; - frac += fracstep; + dest[0] = source[frac>>FRACBITS]; frac += fracstep; + dest[4] = source[frac>>FRACBITS]; frac += fracstep; dest += 8; } if (count & 4) { - dest[0] = source[frac>>FRACBITS]; - frac += fracstep; - dest[4] = source[frac>>FRACBITS]; - frac += fracstep; - dest[8] = source[frac>>FRACBITS]; - frac += fracstep; - dest[12] = source[frac>>FRACBITS]; - frac += fracstep; + dest[0] = source[frac>>FRACBITS]; frac += fracstep; + dest[4] = source[frac>>FRACBITS]; frac += fracstep; + dest[8] = source[frac>>FRACBITS]; frac += fracstep; + dest[12]= source[frac>>FRACBITS]; frac += fracstep; dest += 16; } count >>= 3; @@ -1062,22 +1059,14 @@ void R_DrawColumnHorizP_C (void) do { - dest[0] = source[frac>>FRACBITS]; - frac += fracstep; - dest[4] = source[frac>>FRACBITS]; - frac += fracstep; - dest[8] = source[frac>>FRACBITS]; - frac += fracstep; - dest[12] = source[frac>>FRACBITS]; - frac += fracstep; - dest[16] = source[frac>>FRACBITS]; - frac += fracstep; - dest[20] = source[frac>>FRACBITS]; - frac += fracstep; - dest[24] = source[frac>>FRACBITS]; - frac += fracstep; - dest[28] = source[frac>>FRACBITS]; - frac += fracstep; + dest[0] = source[frac>>FRACBITS]; frac += fracstep; + dest[4] = source[frac>>FRACBITS]; frac += fracstep; + dest[8] = source[frac>>FRACBITS]; frac += fracstep; + dest[12]= source[frac>>FRACBITS]; frac += fracstep; + dest[16]= source[frac>>FRACBITS]; frac += fracstep; + dest[20]= source[frac>>FRACBITS]; frac += fracstep; + dest[24]= source[frac>>FRACBITS]; frac += fracstep; + dest[28]= source[frac>>FRACBITS]; frac += fracstep; dest += 32; } while (--count); } @@ -1110,8 +1099,7 @@ void R_FillColumnHorizP (void) if (!(count >>= 1)) return; do { - dest[0] = color; - dest[4] = color; + dest[0] = color; dest[4] = color; dest += 8; } while (--count); } diff --git a/src/r_main.cpp b/src/r_main.cpp index 88aab962c2..0ce3a54bd4 100644 --- a/src/r_main.cpp +++ b/src/r_main.cpp @@ -102,7 +102,6 @@ static fixed_t MaxVisForFloor; static FRandom pr_torchflicker ("TorchFlicker"); static FRandom pr_hom; static TArray PastViewers; -static int centerxwide; static bool polyclipped; static bool r_showviewer; bool r_dontmaplines; @@ -134,6 +133,7 @@ float LastFOV; int WidescreenRatio; fixed_t GlobVis; +fixed_t viewingrangerecip; fixed_t FocalTangent; fixed_t FocalLengthX; fixed_t FocalLengthY; @@ -150,6 +150,8 @@ float WallTMapScale2; extern "C" { int centerx; int centery; +int centerxwide; + } DCanvas *RenderTarget; // [RH] canvas to render to @@ -158,6 +160,7 @@ fixed_t globaluclip, globaldclip; fixed_t centerxfrac; fixed_t centeryfrac; fixed_t yaspectmul; +fixed_t baseyaspectmul; // yaspectmul without a forced aspect ratio float iyaspectmulfloat; fixed_t InvZtoScale; @@ -457,6 +460,9 @@ void R_InitTextureMapping () FocalLengthY = Scale (centerxfrac, yaspectmul, hitan); FocalLengthXfloat = (float)FocalLengthX / 65536.f; + // This is 1/FocalTangent before the widescreen extension of FOV. + viewingrangerecip = DivScale32(1, finetangent[FINEANGLES/4+(FieldOfView/2)]); + // Now generate xtoviewangle for sky texture mapping. // [RH] Do not generate viewangletox, because texture mapping is no // longer done with trig, so it's not needed. @@ -599,7 +605,7 @@ void R_SetViewSize (int blocks) void R_SetWindow (int windowSize, int fullWidth, int fullHeight, int stHeight) { - int virtheight, virtwidth; + int virtheight, virtwidth, trueratio, virtwidth2, virtheight2; if (windowSize >= 11) { @@ -620,7 +626,7 @@ void R_SetWindow (int windowSize, int fullWidth, int fullHeight, int stHeight) } // If the screen is approximately 16:9 or 16:10, consider it widescreen. - WidescreenRatio = CheckRatio (fullWidth, fullHeight); + WidescreenRatio = CheckRatio (fullWidth, fullHeight, &trueratio); DrawFSHUD = (windowSize == 11); @@ -643,8 +649,18 @@ void R_SetWindow (int windowSize, int fullWidth, int fullHeight, int stHeight) centerxfrac = centerx< @@ -53,7 +59,6 @@ #include "v_palette.h" #include "r_translate.h" - extern fixed_t globaluclip, globaldclip; @@ -94,6 +99,8 @@ short screenheightarray[MAXWIDTH]; CVAR (Bool, r_drawplayersprites, true, 0) // [RH] Draw player sprites? +CVAR (Bool, r_drawvoxels, true, 0) + // // INITIALIZATION FUNCTIONS // @@ -104,6 +111,13 @@ TArray sprites; TArray SpriteFrames; DWORD NumStdSprites; // The first x sprites that don't belong to skins. +TDeletingArray Voxels; // used only to auto-delete voxels on exit. +TDeletingArray VoxelDefs; + +int OffscreenBufferWidth, OffscreenBufferHeight; +BYTE *OffscreenColorBuffer; +FCoverageBuffer *OffscreenCoverageBuffer; + struct spriteframewithrotate : public spriteframe_t { int rotate; @@ -112,6 +126,18 @@ sprtemp[MAX_SPRITE_FRAMES]; int maxframe; char* spritename; +struct VoxelOptions +{ + VoxelOptions() + : DroppedSpin(0), PlacedSpin(0), Scale(FRACUNIT), AngleOffset(0) + {} + + int DroppedSpin; + int PlacedSpin; + fixed_t Scale; + angle_t AngleOffset; +}; + // [RH] skin globals FPlayerSkin *skins; size_t numskins; @@ -290,7 +316,9 @@ static void R_InstallSprite (int num) { if (sprtemp[frame].rotate == -1) { - memset (&sprtemp[frame], 0, sizeof(sprtemp[0])); + memset (&sprtemp[frame].Texture, 0, sizeof(sprtemp[0].Texture)); + sprtemp[frame].Flip = 0; + sprtemp[frame].rotate = 0; } } @@ -301,6 +329,7 @@ static void R_InstallSprite (int num) { memcpy (SpriteFrames[framestart+frame].Texture, sprtemp[frame].Texture, sizeof(sprtemp[frame].Texture)); SpriteFrames[framestart+frame].Flip = sprtemp[frame].Flip; + SpriteFrames[framestart+frame].Voxel = sprtemp[frame].Voxel; } // Let the textures know about the rotations @@ -338,41 +367,100 @@ void R_InitSpriteDefs () { int Head, Next; } *hashes; - unsigned int i, max; + struct VHasher + { + int Head, Next, Name, Spin; + char Frame; + } *vhashes; + unsigned int i, j, smax, vmax; DWORD intname; // Create a hash table to speed up the process - max = TexMan.NumTextures(); - hashes = (Hasher *)alloca (sizeof(Hasher) * max); - for (i = 0; i < max; ++i) - { - hashes[i].Head = -1; - } - for (i = 0; i < max; ++i) + smax = TexMan.NumTextures(); + hashes = new Hasher[smax]; + clearbuf(hashes, sizeof(Hasher)*smax/4, -1); + for (i = 0; i < smax; ++i) { FTexture *tex = TexMan.ByIndex(i); if (tex->UseType == FTexture::TEX_Sprite && strlen(tex->Name) >= 6) { - DWORD bucket = tex->dwName % max; + size_t bucket = tex->dwName % smax; hashes[i].Next = hashes[bucket].Head; hashes[bucket].Head = i; } } + // Repeat, for voxels + vmax = Wads.GetNumLumps(); + vhashes = new VHasher[vmax]; + clearbuf(vhashes, sizeof(VHasher)*vmax/4, -1); + for (i = 0; i < vmax; ++i) + { + if (Wads.GetLumpNamespace(i) == ns_voxels) + { + char name[9]; + size_t namelen; + int spin; + int sign; + + Wads.GetLumpName(name, i); + name[8] = 0; + namelen = strlen(name); + if (namelen < 4) + { // name is too short + continue; + } + if (name[4] != '\0' && name[4] != ' ' && (name[4] < 'A' || name[4] >= 'A' + MAX_SPRITE_FRAMES)) + { // frame char is invalid + continue; + } + spin = 0; + sign = 2; // 2 to convert from deg/halfsec to deg/sec + j = 5; + if (j < namelen && name[j] == '-') + { // a minus sign is okay, but only before any digits + j++; + sign = -2; + } + for (; j < namelen; ++j) + { // the remainder to the end of the name must be digits + if (name[j] >= '0' && name[j] <= '9') + { + spin = spin * 10 + name[j] - '0'; + } + else + { + break; + } + } + if (j < namelen) + { // the spin part is invalid + continue; + } + memcpy(&vhashes[i].Name, name, 4); + vhashes[i].Frame = name[4]; + vhashes[i].Spin = spin * sign; + size_t bucket = vhashes[i].Name % vmax; + vhashes[i].Next = vhashes[bucket].Head; + vhashes[bucket].Head = i; + } + } + // scan all the lump names for each of the names, noting the highest frame letter. for (i = 0; i < sprites.Size(); ++i) { memset (sprtemp, 0xFF, sizeof(sprtemp)); - for (int j = 0; j < MAX_SPRITE_FRAMES; ++j) + for (j = 0; j < MAX_SPRITE_FRAMES; ++j) { sprtemp[j].Flip = 0; + sprtemp[j].Voxel = NULL; } maxframe = -1; intname = sprites[i].dwName; // scan the lumps, filling in the frames for whatever is found - int hash = hashes[intname % max].Head; + int hash = hashes[intname % smax].Head; while (hash != -1) { FTexture *tex = TexMan[hash]; @@ -385,9 +473,345 @@ void R_InitSpriteDefs () } hash = hashes[hash].Next; } + + // repeat, for voxels + hash = vhashes[intname % vmax].Head; + while (hash != -1) + { + VHasher *vh = &vhashes[hash]; + if (vh->Name == intname) + { + FVoxel *vox = R_LoadKVX(hash); + if (vox == NULL) + { + Printf("%s is not a valid voxel file\n", Wads.GetLumpFullName(hash)); + } + else + { + FVoxelDef *voxdef = new FVoxelDef; + + voxdef->Voxel = vox; + voxdef->Scale = FRACUNIT; + voxdef->DroppedSpin = voxdef->PlacedSpin = vh->Spin; + voxdef->AngleOffset = 0; + + Voxels.Push(vox); + VoxelDefs.Push(voxdef); + + if (vh->Frame == ' ' || vh->Frame == '\0') + { // voxel applies to every sprite frame + for (j = 0; j < MAX_SPRITE_FRAMES; ++j) + { + if (sprtemp[j].Voxel == NULL) + { + sprtemp[j].Voxel = voxdef; + } + } + maxframe = MAX_SPRITE_FRAMES-1; + } + else + { // voxel applies to a specific frame + j = vh->Frame - 'A'; + sprtemp[j].Voxel = voxdef; + maxframe = MAX(maxframe, j); + } + } + } + hash = vh->Next; + } R_InstallSprite ((int)i); } + + delete[] hashes; + delete[] vhashes; +} + +//========================================================================== +// +// R_ExtendSpriteFrames +// +// Extends a sprite so that it can hold the desired frame. +// +//========================================================================== + +static void R_ExtendSpriteFrames(spritedef_t &spr, int frame) +{ + unsigned int i, newstart; + + if (spr.numframes >= ++frame) + { // The sprite already has enough frames, so do nothing. + return; + } + + if (spr.numframes == 0 || (spr.spriteframes + spr.numframes == SpriteFrames.Size())) + { // Sprite's frames are at the end of the array, or it has no frames + // at all, so we can tack the new frames directly on to the end + // of the SpriteFrames array. + newstart = SpriteFrames.Reserve(frame - spr.numframes); + } + else + { // We need to allocate space for all the sprite's frames and copy + // the existing ones over to the new space. The old space will be + // lost. + newstart = SpriteFrames.Reserve(frame); + for (i = 0; i < spr.numframes; ++i) + { + SpriteFrames[newstart + i] = SpriteFrames[spr.spriteframes + i]; + } + spr.spriteframes = WORD(newstart); + newstart += i; + } + // Initialize all new frames to 0. + memset(&SpriteFrames[newstart], 0, sizeof(spriteframe_t)*(frame - spr.numframes)); + spr.numframes = frame; +} + +//========================================================================== +// +// VOX_ReadSpriteNames +// +// Reads a list of sprite names from a VOXELDEF lump. +// +//========================================================================== + +static bool VOX_ReadSpriteNames(FScanner &sc, TArray &vsprites) +{ + unsigned int i; + + vsprites.Clear(); + while (sc.GetString()) + { + // A sprite name list is terminated by an '=' character. + if (sc.String[0] == '=') + { + if (vsprites.Size() == 0) + { + sc.ScriptMessage("No sprites specified for voxel.\n"); + } + return true; + } + if (sc.StringLen != 4 && sc.StringLen != 5) + { + sc.ScriptMessage("Sprite name \"%s\" is wrong size.\n", sc.String); + } + else if (sc.StringLen == 5 && (sc.String[4] = toupper(sc.String[4]), sc.String[4] < 'A' || sc.String[4] >= 'A' + MAX_SPRITE_FRAMES)) + { + sc.ScriptMessage("Sprite frame %s is invalid.\n", sc.String[4]); + } + else + { + int frame = (sc.StringLen == 4) ? 255 : sc.String[4] - 'A'; + int spritename; + + for (i = 0; i < 4; ++i) + { + sc.String[i] = toupper(sc.String[i]); + } + spritename = *(int *)sc.String; + for (i = 0; i < sprites.Size(); ++i) + { + if (sprites[i].dwName == spritename) + { + break; + } + } + if (i != sprites.Size()) + { + vsprites.Push((frame << 24) | i); + } + } + } + if (vsprites.Size() != 0) + { + sc.ScriptMessage("Unexpected end of file\n"); + } + return false; +} + +//========================================================================== +// +// VOX_ReadOptions +// +// Reads a list of options from a VOXELDEF lump, terminated with a '}' +// character. The leading '{' must already be consumed +// +//========================================================================== + +static void VOX_ReadOptions(FScanner &sc, VoxelOptions &opts) +{ + while (sc.GetToken()) + { + if (sc.TokenType == '}') + { + return; + } + sc.TokenMustBe(TK_Identifier); + if (sc.Compare("scale")) + { + sc.MustGetToken('='); + sc.MustGetToken(TK_FloatConst); + opts.Scale = FLOAT2FIXED(sc.Float); + } + else if (sc.Compare("spin")) + { + sc.MustGetToken('='); + sc.MustGetToken(TK_IntConst); + opts.DroppedSpin = opts.PlacedSpin = sc.Number; + } + else if (sc.Compare("placedspin")) + { + sc.MustGetToken('='); + sc.MustGetToken(TK_IntConst); + opts.PlacedSpin = sc.Number; + } + else if (sc.Compare("droppedspin")) + { + sc.MustGetToken('='); + sc.MustGetToken(TK_IntConst); + opts.DroppedSpin = sc.Number; + } + else if (sc.Compare("angleoffset")) + { + sc.MustGetToken('='); + sc.MustGetAnyToken(); + if (sc.TokenType == TK_IntConst) + { + sc.Float = sc.Number; + } + else + { + sc.TokenMustBe(TK_FloatConst); + } + opts.AngleOffset = angle_t(sc.Float * ANGLE_180 / 180.0); + } + else + { + sc.ScriptMessage("Unknown voxel option '%s'\n", sc.String); + if (sc.CheckToken('=')) + { + sc.MustGetAnyToken(); + } + } + } + sc.ScriptMessage("Unterminated voxel option block\n"); +} + +//========================================================================== +// +// VOX_GetVoxel +// +// Returns a voxel object for the given lump or NULL if it is not a valid +// voxel. If the voxel has already been loaded, it will be reused. +// +//========================================================================== + +static FVoxel *VOX_GetVoxel(int lumpnum) +{ + // Is this voxel already loaded? If so, return it. + for (unsigned i = 0; i < Voxels.Size(); ++i) + { + if (Voxels[i]->LumpNum == lumpnum) + { + return Voxels[i]; + } + } + FVoxel *vox = R_LoadKVX(lumpnum); + if (vox != NULL) + { + Voxels.Push(vox); + } + return vox; +} + +//========================================================================== +// +// VOX_AddVoxel +// +// Sets a voxel for a single sprite frame. +// +//========================================================================== + +static void VOX_AddVoxel(int sprnum, int frame, FVoxelDef *def) +{ + R_ExtendSpriteFrames(sprites[sprnum], frame); + SpriteFrames[sprites[sprnum].spriteframes + frame].Voxel = def; +} + +//========================================================================== +// +// R_InitVoxels +// +// Process VOXELDEF lumps for defining voxel options that cannot be +// condensed neatly into a sprite name format. +// +//========================================================================== + +void R_InitVoxels() +{ + int lump, lastlump = 0; + + while ((lump = Wads.FindLump("VOXELDEF", &lastlump)) != -1) + { + FScanner sc(lump); + TArray vsprites; + + while (VOX_ReadSpriteNames(sc, vsprites)) + { + FVoxel *voxeldata = NULL; + int voxelfile; + VoxelOptions opts; + + sc.SetCMode(true); + sc.MustGetToken(TK_StringConst); + voxelfile = Wads.CheckNumForFullName(sc.String, true, ns_voxels); + if (voxelfile < 0) + { + sc.ScriptMessage("Voxel \"%s\" not found.\n", sc.String); + } + else + { + voxeldata = VOX_GetVoxel(voxelfile); + if (voxeldata == NULL) + { + sc.ScriptMessage("\"%s\" is not a valid voxel file.\n", sc.String); + } + } + if (sc.CheckToken('{')) + { + VOX_ReadOptions(sc, opts); + } + sc.SetCMode(false); + if (voxeldata != NULL && vsprites.Size() != 0) + { + FVoxelDef *def = new FVoxelDef; + + def->Voxel = voxeldata; + def->Scale = opts.Scale; + def->DroppedSpin = opts.DroppedSpin; + def->PlacedSpin = opts.PlacedSpin; + def->AngleOffset = opts.AngleOffset; + VoxelDefs.Push(def); + + for (unsigned i = 0; i < vsprites.Size(); ++i) + { + int sprnum = int(vsprites[i] & 0xFFFFFF); + int frame = int(vsprites[i] >> 24); + if (frame == 255) + { // Apply voxel to all frames. + for (int j = MAX_SPRITE_FRAMES - 1; j >= 0; --j) + { + VOX_AddVoxel(sprnum, j, def); + } + } + else + { // Apply voxel to only one frame. + VOX_AddVoxel(sprnum, frame, def); + } + } + } + } + } } // [RH] @@ -882,6 +1306,7 @@ void R_InitSprites () } R_InitSpriteDefs (); + R_InitVoxels(); // [RH] Parse VOXELDEF NumStdSprites = sprites.Size(); R_InitSkins (); // [RH] Finish loading skin data @@ -956,6 +1381,19 @@ void R_DeinitSprites() spritesortersize = 0; spritesorter = NULL; } + + // Free offscreen buffer + if (OffscreenColorBuffer != NULL) + { + delete[] OffscreenColorBuffer; + OffscreenColorBuffer = NULL; + } + if (OffscreenCoverageBuffer != NULL) + { + delete OffscreenCoverageBuffer; + OffscreenCoverageBuffer = NULL; + } + OffscreenBufferHeight = OffscreenBufferWidth = 0; } // @@ -991,7 +1429,7 @@ vissprite_t *R_NewVisSprite (void) *p = new vissprite_t; } } - + vissprite_p++; return *(vissprite_p-1); } @@ -1171,6 +1609,75 @@ void R_DrawVisSprite (vissprite_t *vis) NetUpdate (); } +void R_DrawVisVoxel(vissprite_t *spr, int minslabz, int maxslabz, short *cliptop, short *clipbot) +{ + ESPSResult mode; + int flags = 0; + + // Do setup for blending. + dc_colormap = spr->colormap; + mode = R_SetPatchStyle(spr->RenderStyle, spr->alpha, spr->Translation, spr->FillColor); + + if (mode == DontDraw) + { + return; + } + if (colfunc == fuzzcolfunc || colfunc == R_FillColumnP) + { + flags = DVF_OFFSCREEN | DVF_SPANSONLY; + } + else if (colfunc != basecolfunc) + { + flags = DVF_OFFSCREEN; + } + if (flags != 0) + { + R_CheckOffscreenBuffer(RenderTarget->GetWidth(), RenderTarget->GetHeight(), !!(flags & DVF_SPANSONLY)); + } + + // Render the voxel, either directly to the screen or offscreen. + R_DrawVoxel(spr->gx, spr->gy, spr->gz, spr->angle, spr->xscale, spr->yscale, spr->voxel, spr->colormap, cliptop, clipbot, + minslabz, maxslabz, flags); + + // Blend the voxel, if that's what we need to do. + if (flags != 0) + { + for (int x = 0; x < viewwidth; ++x) + { + if (!(flags & DVF_SPANSONLY) && (x & 3) == 0) + { + rt_initcols(OffscreenColorBuffer + x * OffscreenBufferHeight); + } + for (FCoverageBuffer::Span *span = OffscreenCoverageBuffer->Spans[x]; span != NULL; span = span->NextSpan) + { + if (flags & DVF_SPANSONLY) + { + dc_x = x; + dc_yl = span->Start; + dc_yh = span->Stop - 1; + dc_count = span->Stop - span->Start; + dc_dest = ylookup[span->Start] + x + dc_destorg; + colfunc(); + } + else + { + unsigned int **tspan = &dc_ctspan[x & 3]; + (*tspan)[0] = span->Start; + (*tspan)[1] = span->Stop - 1; + *tspan += 2; + } + } + if (!(flags & DVF_SPANSONLY) && (x & 3) == 3) + { + rt_draw4cols(x - 3); + } + } + } + + R_FinishSetPatchStyle(); + NetUpdate(); +} + // // R_ProjectSprite // Generates a vissprite for a thing if it might be visible. @@ -1186,13 +1693,14 @@ void R_ProjectSprite (AActor *thing, int fakeside) fixed_t tx, tx2; fixed_t tz; - fixed_t xscale; + fixed_t xscale, yscale; int x1; int x2; FTextureID picnum; FTexture *tex; + FVoxelDef *voxel; WORD flip; @@ -1221,26 +1729,8 @@ void R_ProjectSprite (AActor *thing, int fakeside) tz = DMulScale20 (tr_x, viewtancos, tr_y, viewtansin); - // thing is behind view plane? - if (tz < MINZ) - return; - - tx = DMulScale16 (tr_x, viewsin, -tr_y, viewcos); - - // [RH] Flip for mirrors - if (MirrorFlags & RF_XFLIP) - { - tx = -tx; - } - tx2 = tx >> 4; - - // too far off the side? - if ((abs (tx) >> 6) > tz) - { - return; - } - - xscale = DivScale12 (centerxfrac, tz); + tex = NULL; + voxel = NULL; if (thing->picnum.isValid()) { @@ -1306,52 +1796,53 @@ void R_ProjectSprite (AActor *thing, int fakeside) picnum = sprframe->Texture[rot]; flip = sprframe->Flip & (1 << rot); tex = TexMan[picnum]; // Do not animate the rotation + if (r_drawvoxels) + { + voxel = sprframe->Voxel; + } } } - if (tex == NULL || tex->UseType == FTexture::TEX_Null) + if (voxel == NULL && (tex == NULL || tex->UseType == FTexture::TEX_Null)) { return; } - // [RH] Added scaling - int scaled_to = tex->GetScaledTopOffset(); - int scaled_bo = scaled_to - tex->GetScaledHeight(); - gzt = fz + thing->scaleY * scaled_to; - gzb = fz + thing->scaleY * scaled_bo; + // thing is behind view plane? + if (voxel == NULL && tz < MINZ) + return; - // [RH] Reject sprites that are off the top or bottom of the screen - if (MulScale12 (globaluclip, tz) > viewz - gzb || - MulScale12 (globaldclip, tz) < viewz - gzt) + tx = DMulScale16 (tr_x, viewsin, -tr_y, viewcos); + + // [RH] Flip for mirrors + if (MirrorFlags & RF_XFLIP) + { + tx = -tx; + } + tx2 = tx >> 4; + + // too far off the side? + if ((abs(tx) >> 6) > abs(tz)) { return; } - // [RH] Flip for mirrors and renderflags - if ((MirrorFlags ^ thing->renderflags) & RF_XFLIP) + if (voxel == NULL) { - flip = !flip; + // [RH] Added scaling + int scaled_to = tex->GetScaledTopOffset(); + int scaled_bo = scaled_to - tex->GetScaledHeight(); + gzt = fz + thing->scaleY * scaled_to; + gzb = fz + thing->scaleY * scaled_bo; + } + else + { + xscale = FixedMul(thing->scaleX, voxel->Scale); + yscale = FixedMul(thing->scaleY, voxel->Scale); + gzt = fz + MulScale8(yscale, voxel->Voxel->Mips[0].PivotZ) - thing->floorclip; + gzb = fz + MulScale8(yscale, voxel->Voxel->Mips[0].PivotZ - (voxel->Voxel->Mips[0].SizeZ << 8)); + if (gzt <= gzb) + return; } - - // calculate edges of the shape - const fixed_t thingxscalemul = DivScale16(thing->scaleX, tex->xScale); - - tx -= (flip ? (tex->GetWidth() - tex->LeftOffset - 1) : tex->LeftOffset) * thingxscalemul; - x1 = centerx + MulScale32 (tx, xscale); - - // off the right side? - if (x1 > WindowRight) - return; - - tx += tex->GetWidth() * thingxscalemul; - x2 = centerx + MulScale32 (tx, xscale); - - // off the left side or too small? - if (x2 < WindowLeft || x2 <= x1) - return; - - xscale = FixedDiv(FixedMul(thing->scaleX, xscale), tex->xScale); - iscale = (tex->GetWidth() << FRACBITS) / (x2 - x1); - x2--; // killough 3/27/98: exclude things totally separated // from the viewer, by either water or fake ceilings @@ -1380,50 +1871,129 @@ void R_ProjectSprite (AActor *thing, int fakeside) } } - // store information in a vissprite - vis = R_NewVisSprite (); + if (voxel == NULL) + { + xscale = DivScale12 (centerxfrac, tz); + + // [RH] Reject sprites that are off the top or bottom of the screen + if (MulScale12 (globaluclip, tz) > viewz - gzb || + MulScale12 (globaldclip, tz) < viewz - gzt) + { + return; + } + + // [RH] Flip for mirrors and renderflags + if ((MirrorFlags ^ thing->renderflags) & RF_XFLIP) + { + flip = !flip; + } + + // calculate edges of the shape + const fixed_t thingxscalemul = DivScale16(thing->scaleX, tex->xScale); + + tx -= (flip ? (tex->GetWidth() - tex->LeftOffset - 1) : tex->LeftOffset) * thingxscalemul; + x1 = centerx + MulScale32 (tx, xscale); + + // off the right side? + if (x1 > WindowRight) + return; + + tx += tex->GetWidth() * thingxscalemul; + x2 = centerx + MulScale32 (tx, xscale); + + // off the left side or too small? + if ((x2 < WindowLeft || x2 <= x1)) + return; + + xscale = FixedDiv(FixedMul(thing->scaleX, xscale), tex->xScale); + iscale = (tex->GetWidth() << FRACBITS) / (x2 - x1); + x2--; + + fixed_t yscale = SafeDivScale16(thing->scaleY, tex->yScale); + + // store information in a vissprite + vis = R_NewVisSprite(); + + vis->xscale = xscale; + vis->yscale = Scale(InvZtoScale, yscale, tz << 4); + vis->idepth = (unsigned)DivScale32(1, tz) >> 1; // tz is 20.12, so idepth ought to be 12.20, but signed math makes it 13.19 + vis->floorclip = FixedDiv (thing->floorclip, yscale); + vis->texturemid = (tex->TopOffset << FRACBITS) - FixedDiv (viewz - fz + thing->floorclip, yscale); + vis->x1 = x1 < WindowLeft ? WindowLeft : x1; + vis->x2 = x2 > WindowRight ? WindowRight : x2; + vis->angle = thing->angle; + + if (flip) + { + vis->startfrac = (tex->GetWidth() << FRACBITS) - 1; + vis->xiscale = -iscale; + } + else + { + vis->startfrac = 0; + vis->xiscale = iscale; + } + + if (vis->x1 > x1) + vis->startfrac += vis->xiscale * (vis->x1 - x1); + } + else + { + vis = R_NewVisSprite(); + + vis->xscale = xscale; + vis->yscale = yscale; + vis->x1 = WindowLeft; + vis->x2 = WindowRight; + vis->idepth = (unsigned)DivScale32(1, MAX(tz, MINZ)) >> 1; + vis->floorclip = thing->floorclip; + + fz -= thing->floorclip; + + vis->angle = thing->angle + voxel->AngleOffset; + + int voxelspin = (thing->flags & MF_DROPPED) ? voxel->DroppedSpin : voxel->PlacedSpin; + if (voxelspin != 0) + { + double ang = double(I_FPSTime()) * voxelspin / 1000; + vis->angle += angle_t(ang * (4294967296.f / 360)); + } + + // These are irrelevant for voxels. + vis->texturemid = 0x1CEDBEEF; + vis->startfrac = 0x1CEDBEEF; + vis->xiscale = 0x1CEDBEEF; + } // killough 3/27/98: save sector for special clipping later vis->heightsec = heightsec; vis->sector = thing->Sector; - fixed_t yscale = DivScale16(thing->scaleY, tex->yScale); + vis->cx = tx2; + vis->depth = tz; + vis->gx = fx; + vis->gy = fy; + vis->gz = fz; + vis->gzb = gzb; // [RH] use gzb, not thing->z + vis->gzt = gzt; // killough 3/27/98 vis->renderflags = thing->renderflags; vis->RenderStyle = thing->RenderStyle; vis->FillColor = thing->fillcolor; - vis->xscale = xscale; - vis->yscale = Scale (InvZtoScale, yscale, tz << 4); - vis->depth = tz; - vis->idepth = (DWORD)DivScale32 (1, tz) >> 1; // tz is 20.12, so idepth ought to be 12.20, but - vis->cx = tx2; // signed math makes it 13.19 - vis->gx = fx; - vis->gy = fy; - vis->gz = gzb; // [RH] use gzb, not thing->z - vis->gzt = gzt; // killough 3/27/98 - vis->floorclip = FixedDiv (thing->floorclip, yscale); - vis->texturemid = (tex->TopOffset << FRACBITS) - - FixedDiv (viewz-fz+thing->floorclip, yscale); - vis->x1 = x1 < WindowLeft ? WindowLeft : x1; - vis->x2 = x2 > WindowRight ? WindowRight : x2; vis->Translation = thing->Translation; // [RH] thing translation table vis->FakeFlatStat = fakeside; vis->alpha = thing->alpha; - vis->pic = tex; - if (flip) + if (voxel != NULL) { - vis->startfrac = (tex->GetWidth() << FRACBITS) - 1; - vis->xiscale = -iscale; + vis->voxel = voxel->Voxel; + vis->bIsVoxel = true; } else { - vis->startfrac = 0; - vis->xiscale = iscale; + vis->pic = tex; + vis->bIsVoxel = false; } - if (vis->x1 > x1) - vis->startfrac += vis->xiscale*(vis->x1-x1); - // The software renderer cannot invert the source without inverting the overlay // too. That means if the source is inverted, we need to do the reverse of what // the invert overlay flag says to do. @@ -1477,7 +2047,7 @@ void R_ProjectSprite (AActor *thing, int fakeside) else { // diminished light vis->colormap = mybasecolormap->Maps + (GETPALOOKUP ( - (fixed_t)DivScale12 (r_SpriteVisibility, tz), spriteshade) << COLORMAPSHIFT); + (fixed_t)DivScale12 (r_SpriteVisibility, MAX(tz, MINZ)), spriteshade) << COLORMAPSHIFT); } } } @@ -2081,19 +2651,23 @@ void R_DrawSprite (vissprite_t *spr) static short cliptop[MAXWIDTH]; drawseg_t *ds; int i; + int x1, x2; int r1, r2; short topclip, botclip; short *clip1, *clip2; // [RH] Check for particles - if (spr->pic == NULL) + if (!spr->bIsVoxel && spr->pic == NULL) { R_DrawParticle (spr); return; } + x1 = spr->x1; + x2 = spr->x2; + // [RH] Quickly reject sprites with bad x ranges. - if (spr->x1 > spr->x2) + if (x1 > x2) return; // [RH] Sprites split behind a one-sided line can also be discarded. @@ -2111,54 +2685,62 @@ void R_DrawSprite (vissprite_t *spr) // [RH] rewrote this to be based on which part of the sector is really visible fixed_t scale = MulScale19 (InvZtoScale, spr->idepth); + fixed_t hzb = FIXED_MIN, hzt = FIXED_MAX; - if (spr->heightsec && - !(spr->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC)) + if (spr->bIsVoxel && spr->floorclip != 0) + { + hzb = spr->gzb; + } + + if (spr->heightsec && !(spr->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC)) { // only things in specially marked sectors if (spr->FakeFlatStat != FAKED_AboveCeiling) { - fixed_t h = spr->heightsec->floorplane.ZatPoint (spr->gx, spr->gy); - //h = (centeryfrac - FixedMul (h-viewz, spr->yscale)) >> FRACBITS; - h = (centeryfrac - FixedMul (h-viewz, scale)) >> FRACBITS; + fixed_t hz = spr->heightsec->floorplane.ZatPoint (spr->gx, spr->gy); + fixed_t h = (centeryfrac - FixedMul (hz-viewz, scale)) >> FRACBITS; if (spr->FakeFlatStat == FAKED_BelowFloor) { // seen below floor: clip top - if (h > topclip) + if (!spr->bIsVoxel && h > topclip) { topclip = MIN (h, viewheight); } + hzt = MIN(hzt, hz); } else { // seen in the middle: clip bottom - if (h < botclip) + if (!spr->bIsVoxel && h < botclip) { botclip = MAX (0, h); } + hzb = MAX(hzb, hz); } } if (spr->FakeFlatStat != FAKED_BelowFloor) { - fixed_t h = spr->heightsec->ceilingplane.ZatPoint (spr->gx, spr->gy); - h = (centeryfrac - FixedMul (h-viewz, scale)) >> FRACBITS; + fixed_t hz = spr->heightsec->ceilingplane.ZatPoint (spr->gx, spr->gy); + fixed_t h = (centeryfrac - FixedMul (hz-viewz, scale)) >> FRACBITS; if (spr->FakeFlatStat == FAKED_AboveCeiling) { // seen above ceiling: clip bottom - if (h < botclip) + if (!spr->bIsVoxel && h < botclip) { botclip = MAX (0, h); } + hzb = MAX(hzb, hz); } else { // seen in the middle: clip top - if (h > topclip) + if (!spr->bIsVoxel && h > topclip) { topclip = MIN (h, viewheight); } + hzt = MIN(hzt, hz); } } } // killough 3/27/98: end special clipping for deep water / fake ceilings - else if (spr->floorclip) + else if (!spr->bIsVoxel && spr->floorclip) { // [RH] Move floorclip stuff from R_DrawVisSprite to here int clip = ((centeryfrac - FixedMul (spr->texturemid - (spr->pic->GetHeight() << FRACBITS) + @@ -2191,9 +2773,14 @@ void R_DrawSprite (vissprite_t *spr) } #endif - i = spr->x2 - spr->x1 + 1; - clip1 = clipbot + spr->x1; - clip2 = cliptop + spr->x1; + if (topclip >= botclip) + { + return; + } + + i = x2 - x1 + 1; + clip1 = clipbot + x1; + clip2 = cliptop + x1; do { *clip1++ = botclip; @@ -2212,7 +2799,7 @@ void R_DrawSprite (vissprite_t *spr) for (ds = ds_p; ds-- > firstdrawseg; ) // new -- killough { // determine if the drawseg obscures the sprite - if (ds->x1 > spr->x2 || ds->x2 < spr->x1 || + if (ds->x1 > x2 || ds->x2 < x1 || (!(ds->silhouette & SIL_BOTH) && ds->maskedtexturecol == -1 && !ds->bFogBoundary) ) { @@ -2220,8 +2807,8 @@ void R_DrawSprite (vissprite_t *spr) continue; } - r1 = MAX (ds->x1, spr->x1); - r2 = MIN (ds->x2, spr->x2); + r1 = MAX (ds->x1, x1); + r2 = MIN (ds->x2, x2); fixed_t neardepth, fardepth; if (ds->sz1 < ds->sz2) @@ -2279,9 +2866,33 @@ void R_DrawSprite (vissprite_t *spr) // all clipping has been performed, so draw the sprite - mfloorclip = clipbot; - mceilingclip = cliptop; - R_DrawVisSprite (spr); + if (!spr->bIsVoxel) + { + mfloorclip = clipbot; + mceilingclip = cliptop; + R_DrawVisSprite (spr); + } + else + { + // If it is completely clipped away, don't bother drawing it. + if (cliptop[x2] >= clipbot[x2]) + { + for (i = x1; i < x2; ++i) + { + if (cliptop[i] < clipbot[i]) + { + break; + } + } + if (i == x2) + { + return; + } + } + int minvoxely = spr->gzt <= hzt ? 0 : (spr->gzt - hzt) / spr->yscale; + int maxvoxely = spr->gzb > hzb ? INT_MAX : (spr->gzt - hzb) / spr->yscale; + R_DrawVisVoxel(spr, minvoxely, maxvoxely, cliptop, clipbot); + } } // @@ -2538,13 +3149,14 @@ void R_ProjectParticle (particle_t *particle, const sector_t *sector, int shade, vis->cx = tx; vis->gx = particle->x; vis->gy = particle->y; - vis->gz = y1; + vis->gzb = y1; vis->gzt = y2; vis->x1 = x1; vis->x2 = x2; vis->Translation = 0; vis->startfrac = particle->color; vis->pic = NULL; + vis->bIsVoxel = false; vis->renderflags = particle->trans; vis->FakeFlatStat = fakeside; vis->floorclip = 0; @@ -2595,7 +3207,7 @@ void R_DrawParticle (vissprite_t *vis) BYTE *dest; DWORD fg; BYTE color = vis->colormap[vis->startfrac]; - int yl = vis->gz; + int yl = vis->gzb; int ycount = vis->gzt - yl + 1; int x1 = vis->x1; int countbase = vis->x2 - x1 + 1; @@ -2629,3 +3241,480 @@ void R_DrawParticle (vissprite_t *vis) dest += spacing; } while (--ycount); } + +static fixed_t distrecip(fixed_t y) +{ + y >>= 3; + return y == 0 ? 0 : SafeDivScale32(centerxwide, y); +} + +extern fixed_t baseyaspectmul; + +void R_DrawVoxel(fixed_t dasprx, fixed_t daspry, fixed_t dasprz, angle_t dasprang, + fixed_t daxscale, fixed_t dayscale, FVoxel *voxobj, + lighttable_t *colormap, short *daumost, short *dadmost, int minslabz, int maxslabz, int flags) +{ + int i, j, k, x, y, syoff, ggxstart, ggystart, nxoff; + fixed_t cosang, sinang, sprcosang, sprsinang; + int backx, backy, gxinc, gyinc; + int daxscalerecip, dayscalerecip, cnt, gxstart, gystart, dazscale; + int lx, rx, nx, ny, x1=0, y1=0, x2=0, y2=0, yplc, yinc=0; + int yoff, xs=0, ys=0, xe, ye, xi=0, yi=0, cbackx, cbacky, dagxinc, dagyinc; + kvxslab_t *voxptr, *voxend; + FVoxelMipLevel *mip; + + const int nytooclose = centerxwide * 2100, nytoofar = 32768*32768 - 1048576; + const int xdimenscale = Scale(centerxwide, yaspectmul, 160); + const fixed_t globalposx = viewx >> 12; + const fixed_t globalposy = -viewy >> 12; + const fixed_t globalposz = -viewz >> 8; + + dasprx = dasprx >> 12; + daspry = -daspry >> 12; + dasprz = -dasprz >> 8; + daxscale >>= 10; + dayscale >>= 10; + + cosang = viewcos >> 2; + sinang = -viewsin >> 2; + sprcosang = finecosine[dasprang >> ANGLETOFINESHIFT] >> 2; + sprsinang = -finesine[dasprang >> ANGLETOFINESHIFT] >> 2; + + R_SetupDrawSlab(colormap); + + // Select mip level + i = abs(DMulScale8(dasprx - globalposx, viewcos, daspry - globalposy, -viewsin)); + i = DivScale6(i, MIN(daxscale, dayscale)); + j = FocalLengthX >> 3; + for (k = 0; k < voxobj->NumMips; ++k) + { + if (i < j) { break; } + i >>= 1; + } + if (k >= voxobj->NumMips) k = voxobj->NumMips - 1; + + mip = &voxobj->Mips[k]; if (mip->SlabData == NULL) return; + + minslabz >>= k; + maxslabz >>= k; + + daxscale <<= (k+8); dayscale <<= (k+8); + dazscale = FixedDiv(dayscale, baseyaspectmul); + daxscale = FixedDiv(daxscale, yaspectmul); + daxscale = Scale(daxscale, xdimenscale, centerxwide << 9); + dayscale = Scale(dayscale, FixedMul(xdimenscale, viewingrangerecip), centerxwide << 9); + + daxscalerecip = (1<<30) / daxscale; + dayscalerecip = (1<<30) / dayscale; + + x = FixedMul(globalposx - dasprx, daxscalerecip); + y = FixedMul(globalposy - daspry, daxscalerecip); + backx = (DMulScale10(x, sprcosang, y, sprsinang) + mip->PivotX) >> 8; + backy = (DMulScale10(y, sprcosang, x, -sprsinang) + mip->PivotY) >> 8; + cbackx = clamp(backx, 0, mip->SizeX - 1); + cbacky = clamp(backy, 0, mip->SizeY - 1); + + sprcosang = MulScale14(daxscale, sprcosang); + sprsinang = MulScale14(daxscale, sprsinang); + + x = (dasprx - globalposx) - DMulScale18(mip->PivotX, sprcosang, mip->PivotY, -sprsinang); + y = (daspry - globalposy) - DMulScale18(mip->PivotY, sprcosang, mip->PivotX, sprsinang); + + cosang = FixedMul(cosang, dayscalerecip); + sinang = FixedMul(sinang, dayscalerecip); + + gxstart = y*cosang - x*sinang; + gystart = x*cosang + y*sinang; + gxinc = DMulScale10(sprsinang, cosang, sprcosang, -sinang); + gyinc = DMulScale10(sprcosang, cosang, sprsinang, sinang); + if ((abs(globalposz - dasprz) >> 10) >= abs(dazscale)) return; + + x = 0; y = 0; j = MAX(mip->SizeX, mip->SizeY); + fixed_t *ggxinc = (fixed_t *)alloca((j + 1) * sizeof(fixed_t) * 2); + fixed_t *ggyinc = ggxinc + (j + 1); + for (i = 0; i <= j; i++) + { + ggxinc[i] = x; x += gxinc; + ggyinc[i] = y; y += gyinc; + } + + syoff = DivScale21(globalposz - dasprz, dazscale) + (mip->PivotZ << 7); + yoff = (abs(gxinc) + abs(gyinc)) >> 1; + + for (cnt = 0; cnt < 8; cnt++) + { + switch (cnt) + { + case 0: xs = 0; ys = 0; xi = 1; yi = 1; break; + case 1: xs = mip->SizeX-1; ys = 0; xi = -1; yi = 1; break; + case 2: xs = 0; ys = mip->SizeY-1; xi = 1; yi = -1; break; + case 3: xs = mip->SizeX-1; ys = mip->SizeY-1; xi = -1; yi = -1; break; + case 4: xs = 0; ys = cbacky; xi = 1; yi = 2; break; + case 5: xs = mip->SizeX-1; ys = cbacky; xi = -1; yi = 2; break; + case 6: xs = cbackx; ys = 0; xi = 2; yi = 1; break; + case 7: xs = cbackx; ys = mip->SizeY-1; xi = 2; yi = -1; break; + } + xe = cbackx; ye = cbacky; + if (cnt < 4) + { + if ((xi < 0) && (xe >= xs)) continue; + if ((xi > 0) && (xe <= xs)) continue; + if ((yi < 0) && (ye >= ys)) continue; + if ((yi > 0) && (ye <= ys)) continue; + } + else + { + if ((xi < 0) && (xe > xs)) continue; + if ((xi > 0) && (xe < xs)) continue; + if ((yi < 0) && (ye > ys)) continue; + if ((yi > 0) && (ye < ys)) continue; + xe += xi; ye += yi; + } + + i = ksgn(ys-backy)+ksgn(xs-backx)*3+4; + switch(i) + { + case 6: case 7: x1 = 0; y1 = 0; break; + case 8: case 5: x1 = gxinc; y1 = gyinc; break; + case 0: case 3: x1 = gyinc; y1 = -gxinc; break; + case 2: case 1: x1 = gxinc+gyinc; y1 = gyinc-gxinc; break; + } + switch(i) + { + case 2: case 5: x2 = 0; y2 = 0; break; + case 0: case 1: x2 = gxinc; y2 = gyinc; break; + case 8: case 7: x2 = gyinc; y2 = -gxinc; break; + case 6: case 3: x2 = gxinc+gyinc; y2 = gyinc-gxinc; break; + } + BYTE oand = (1 << int(xs 0) { dagxinc = gxinc; dagyinc = FixedMul(gyinc, viewingrangerecip); } + else { dagxinc = -gxinc; dagyinc = -FixedMul(gyinc, viewingrangerecip); } + + /* Fix for non 90 degree viewing ranges */ + nxoff = FixedMul(x2 - x1, viewingrangerecip); + x1 = FixedMul(x1, viewingrangerecip); + + ggxstart = gxstart + ggyinc[ys]; + ggystart = gystart - ggxinc[ys]; + + for (x = xs; x != xe; x += xi) + { + BYTE *slabxoffs = &mip->SlabData[mip->OffsetX[x]]; + short *xyoffs = &mip->OffsetXY[x * (mip->SizeY + 1)]; + + nx = FixedMul(ggxstart + ggxinc[x], viewingrangerecip) + x1; + ny = ggystart + ggyinc[x]; + for (y = ys; y != ye; y += yi, nx += dagyinc, ny -= dagxinc) + { + if ((ny <= nytooclose) || (ny >= nytoofar)) continue; + voxptr = (kvxslab_t *)(slabxoffs + xyoffs[y]); + voxend = (kvxslab_t *)(slabxoffs + xyoffs[y+1]); + if (voxptr >= voxend) continue; + + lx = MulScale32(nx >> 3, distrecip(ny+y1)) + centerx; + if (lx < 0) lx = 0; + rx = MulScale32((nx + nxoff) >> 3, distrecip(ny+y2)) + centerx; + if (rx > viewwidth) rx = viewwidth; + if (rx <= lx) continue; + rx -= lx; + + fixed_t l1 = distrecip(ny-yoff); + fixed_t l2 = distrecip(ny+yoff); + for (; voxptr < voxend; voxptr = (kvxslab_t *)((BYTE *)voxptr + voxptr->zleng + 3)) + { + const BYTE *col = voxptr->col; + int zleng = voxptr->zleng; + int ztop = voxptr->ztop; + fixed_t z1, z2; + + if (ztop < minslabz) + { + int diff = minslabz - ztop; + ztop = minslabz; + col += diff; + zleng -= diff; + } + if (ztop + zleng > maxslabz) + { + int diff = ztop + zleng - maxslabz; + zleng -= diff; + } + if (zleng <= 0) continue; + + j = (ztop << 15) - syoff; + if (j < 0) + { + k = j + (zleng << 15); + if (k < 0) + { + if ((voxptr->backfacecull & oand32) == 0) continue; + z2 = MulScale32(l2, k) + centery; /* Below slab */ + } + else + { + if ((voxptr->backfacecull & oand) == 0) continue; /* Middle of slab */ + z2 = MulScale32(l1, k) + centery; + } + z1 = MulScale32(l1, j) + centery; + } + else + { + if ((voxptr->backfacecull & oand16) == 0) continue; + z1 = MulScale32(l2, j) + centery; /* Above slab */ + z2 = MulScale32(l1, j + (zleng << 15)) + centery; + } + + if (zleng == 1) + { + yplc = 0; yinc = 0; + if (z1 < daumost[lx]) z1 = daumost[lx]; + } + else + { + if (z2-z1 >= 1024) yinc = FixedDiv(zleng, z2 - z1); + else if (z2 > z1) yinc = (((1 << 24) - 1) / (z2 - z1)) * zleng >> 8; + if (z1 < daumost[lx]) { yplc = yinc*(daumost[lx]-z1); z1 = daumost[lx]; } else yplc = 0; + } + if (z2 > dadmost[lx]) z2 = dadmost[lx]; + z2 -= z1; if (z2 <= 0) continue; + + if (!(flags & DVF_OFFSCREEN)) + { + // Draw directly to the screen. + R_DrawSlab(rx, yplc, z2, yinc, col, ylookup[z1] + lx + dc_destorg); + } + else + { + // Record the area covered and possibly draw to an offscreen buffer. + dc_yl = z1; + dc_yh = z1 + z2 - 1; + dc_count = z2; + dc_iscale = yinc; + for (int x = 0; x < rx; ++x) + { + OffscreenCoverageBuffer->InsertSpan(lx + x, z1, z1 + z2); + if (!(flags & DVF_SPANSONLY)) + { + dc_x = lx + x; + rt_initcols(OffscreenColorBuffer + (dc_x & ~3) * OffscreenBufferHeight); + dc_source = col; + dc_texturefrac = yplc; + hcolfunc_pre(); + } + } + } + } + } + } + } +} + +//========================================================================== +// +// FCoverageBuffer Constructor +// +//========================================================================== + +FCoverageBuffer::FCoverageBuffer(int lists) + : Spans(NULL), FreeSpans(NULL) +{ + NumLists = lists; + Spans = new Span *[lists]; + memset(Spans, 0, sizeof(Span*)*lists); +} + +//========================================================================== +// +// FCoverageBuffer Destructor +// +//========================================================================== + +FCoverageBuffer::~FCoverageBuffer() +{ + if (Spans != NULL) + { + delete[] Spans; + } +} + +//========================================================================== +// +// FCoverageBuffer :: Clear +// +//========================================================================== + +void FCoverageBuffer::Clear() +{ + SpanArena.FreeAll(); + memset(Spans, 0, sizeof(Span*)*NumLists); + FreeSpans = NULL; +} + +//========================================================================== +// +// FCoverageBuffer :: InsertSpan +// +// start is inclusive. +// stop is exclusive. +// +//========================================================================== + +void FCoverageBuffer::InsertSpan(int listnum, int start, int stop) +{ + assert(unsigned(listnum) < NumLists); + assert(start < stop); + + Span **span_p = &Spans[listnum]; + Span *span; + + if (*span_p == NULL || (*span_p)->Start > stop) + { // This list is empty or the first entry is after this one, so we can just insert the span. + goto addspan; + } + + // Insert the new span in order, merging with existing ones. + while (*span_p != NULL) + { + if ((*span_p)->Stop < start) // ===== (existing span) + { // Span ends before this one starts. // ++++ (new span) + span_p = &(*span_p)->NextSpan; + continue; + } + + // Does the new span overlap or abut the existing one? + if ((*span_p)->Start <= start) + { + if ((*span_p)->Stop >= stop) // ============= + { // The existing span completely covers this one. // +++++ + return; + } + // Extend the existing span with the new one. // ====== + span = *span_p; // +++++++ + span->Stop = stop; // (or) +++++ + + // Free up any spans we just covered up. + span_p = &(*span_p)->NextSpan; + while (*span_p != NULL && (*span_p)->Start <= stop && (*span_p)->Stop <= stop) + { + Span *span = *span_p; // ====== ====== + *span_p = span->NextSpan; // +++++++++++++ + span->NextSpan = FreeSpans; + FreeSpans = span; + } + if (*span_p != NULL && (*span_p)->Start <= stop) // ======= ======== + { // Our new span connects two existing spans. // ++++++++++++++ + // They should all be collapsed into a single span. + span->Stop = (*span_p)->Stop; + span = *span_p; + *span_p = span->NextSpan; + span->NextSpan = FreeSpans; + FreeSpans = span; + } + goto check; + } + else if ((*span_p)->Start <= stop) // ===== + { // The new span extends the existing span from // ++++ + // the beginning. // (or) ++++ + (*span_p)->Start = start; + goto check; + } + else // ====== + { // No overlap, so insert a new span. // +++++ + goto addspan; + } + } + // Append a new span to the end of the list. +addspan: + span = AllocSpan(); + span->NextSpan = *span_p; + span->Start = start; + span->Stop = stop; + *span_p = span; +check: +#ifdef _DEBUG + // Validate the span list: Spans must be in order, and there must be + // at least one pixel between spans. + for (span = Spans[listnum]; span != NULL; span = span->NextSpan) + { + assert(span->Start < span->Stop); + if (span->NextSpan != NULL) + { + assert(span->Stop < span->NextSpan->Start); + } + } +#endif + ; +} + +//========================================================================== +// +// FCoverageBuffer :: AllocSpan +// +//========================================================================== + +FCoverageBuffer::Span *FCoverageBuffer::AllocSpan() +{ + Span *span; + + if (FreeSpans != NULL) + { + span = FreeSpans; + FreeSpans = span->NextSpan; + } + else + { + span = (Span *)SpanArena.Alloc(sizeof(Span)); + } + return span; +} + +//========================================================================== +// +// R_CheckOffscreenBuffer +// +// Allocates the offscreen coverage buffer and optionally the offscreen +// color buffer. If they already exist but are the wrong size, they will +// be reallocated. +// +//========================================================================== + +void R_CheckOffscreenBuffer(int width, int height, bool spansonly) +{ + if (OffscreenCoverageBuffer == NULL) + { + assert(OffscreenColorBuffer == NULL && "The color buffer cannot exist without the coverage buffer"); + OffscreenCoverageBuffer = new FCoverageBuffer(width); + } + else if (OffscreenCoverageBuffer->NumLists != width) + { + delete OffscreenCoverageBuffer; + OffscreenCoverageBuffer = new FCoverageBuffer(width); + if (OffscreenColorBuffer != NULL) + { + delete[] OffscreenColorBuffer; + OffscreenColorBuffer = NULL; + } + } + else + { + OffscreenCoverageBuffer->Clear(); + } + + if (!spansonly) + { + if (OffscreenColorBuffer == NULL) + { + OffscreenColorBuffer = new BYTE[width * height]; + } + else if (OffscreenBufferWidth != width || OffscreenBufferHeight != height) + { + delete[] OffscreenColorBuffer; + OffscreenColorBuffer = new BYTE[width * height]; + } + } + OffscreenBufferWidth = width; + OffscreenBufferHeight = height; +} diff --git a/src/r_things.h b/src/r_things.h index 9ae796ce90..da31daf79d 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -104,6 +104,14 @@ void R_ClearSprites (); void R_DrawMasked (); void R_DrawRemainingPlayerSprites (); +void R_CheckOffscreenBuffer(int width, int height, bool spansonly); + +enum { DVF_OFFSCREEN = 1, DVF_SPANSONLY = 2 }; + +void R_DrawVoxel(fixed_t dasprx, fixed_t daspry, fixed_t dasprz, angle_t dasprang, + fixed_t daxscale, fixed_t dayscale, FVoxel *voxobj, + lighttable_t *colormap, short *daumost, short *dadmost, int minslabz, int maxslabz, int flags); + void R_ClipVisSprite (vissprite_t *vis, int xl, int xh); diff --git a/src/resourcefiles/file_wad.cpp b/src/resourcefiles/file_wad.cpp index ffa9c85e86..3a6de70eab 100644 --- a/src/resourcefiles/file_wad.cpp +++ b/src/resourcefiles/file_wad.cpp @@ -113,6 +113,7 @@ bool FWadFile::Open(bool quiet) SetNamespace("TX_START", "TX_END", ns_newtextures); SetNamespace("V_START", "V_END", ns_strifevoices); SetNamespace("HI_START", "HI_END", ns_hires); + SetNamespace("VX_START", "VX_END", ns_voxels); SkinHack(); } delete [] fileinfo; diff --git a/src/resourcefiles/resourcefile.cpp b/src/resourcefiles/resourcefile.cpp index c5bde5712b..866605d14d 100644 --- a/src/resourcefiles/resourcefile.cpp +++ b/src/resourcefiles/resourcefile.cpp @@ -116,6 +116,7 @@ void FResourceLump::LumpNameSetup(const char *iname) !strncmp(iname, "textures/", 9) ? ns_newtextures : !strncmp(iname, "hires/", 6) ? ns_hires : !strncmp(iname, "sprites/", 8) ? ns_sprites : + !strncmp(iname, "voxels/", 7) ? ns_voxels : !strncmp(iname, "colormaps/", 10) ? ns_colormaps : !strncmp(iname, "acs/", 4) ? ns_acslibrary : !strncmp(iname, "voices/", 7) ? ns_strifevoices : @@ -137,7 +138,7 @@ void FResourceLump::LumpNameSetup(const char *iname) // Since '\' can't be used as a file name's part inside a ZIP // we have to work around this for sprites because it is a valid // frame character. - else if (Namespace == ns_sprites) + else if (Namespace == ns_sprites || Namespace == ns_voxels) { char *c; diff --git a/src/tables.h b/src/tables.h index 72a79383dd..aeb2c6991d 100644 --- a/src/tables.h +++ b/src/tables.h @@ -60,7 +60,7 @@ extern fixed_t finesine[5*FINEANGLES/4]; // (encapsulated in a struct so that we can still use array accesses). struct cosine_inline { - fixed_t operator[] (unsigned int x) + fixed_t operator[] (unsigned int x) const { return finesine[x+FINEANGLES/4]; } diff --git a/src/textures/buildtexture.cpp b/src/textures/buildtexture.cpp index 2a3d1809f9..836cc2daa7 100644 --- a/src/textures/buildtexture.cpp +++ b/src/textures/buildtexture.cpp @@ -234,6 +234,7 @@ void FTextureManager::AddTiles (void *tiles) rot.Texture[8] = rot.Texture[9] = texnum.GetIndex() + 4; rot.Flip = 0x00FC; + rot.Voxel = NULL; tex->Rotations = SpriteFrames.Push (rot); } else if (rotType == 2) @@ -247,6 +248,7 @@ void FTextureManager::AddTiles (void *tiles) rot.Texture[17-j*2] = texnum.GetIndex() + j; } rot.Flip = 0; + rot.Voxel = NULL; tex->Rotations = SpriteFrames.Push (rot); } } diff --git a/src/v_video.cpp b/src/v_video.cpp index 450650f53b..1986a2a971 100644 --- a/src/v_video.cpp +++ b/src/v_video.cpp @@ -1313,6 +1313,21 @@ void DFrameBuffer::RenderView(player_t *player) FCanvasTextureInfo::UpdateAll (); } +//========================================================================== +// +// +// +//========================================================================== +extern TDeletingArray Voxels; + +void DFrameBuffer::RemapVoxels() +{ + for (unsigned i=0; iRemap(); + } +} + //=========================================================================== // // Render the view to a savegame picture @@ -1689,6 +1704,7 @@ void V_Init2() Printf ("Resolution: %d x %d\n", SCREENWIDTH, SCREENHEIGHT); screen->SetGamma (gamma); + screen->RemapVoxels(); FBaseCVar::ResetColors (); C_NewModeAdjust(); M_InitVideoModesMenu(); @@ -1733,43 +1749,61 @@ CUSTOM_CVAR (Int, vid_aspect, 0, CVAR_GLOBALCONFIG|CVAR_ARCHIVE) // 1: 16:9 // 2: 16:10 // 4: 5:4 -int CheckRatio (int width, int height) +int CheckRatio (int width, int height, int *trueratio) { + int fakeratio = -1; + int ratio; + if ((vid_aspect >=1) && (vid_aspect <=4)) { // [SP] User wants to force aspect ratio; let them. - return vid_aspect == 3? 0: int(vid_aspect); + fakeratio = vid_aspect == 3? 0: int(vid_aspect); } if (vid_nowidescreen) { if (!vid_tft) { - return 0; + fakeratio = 0; + } + else + { + fakeratio = (height * 5/4 == width) ? 4 : 0; } - return (height * 5/4 == width) ? 4 : 0; } // If the size is approximately 16:9, consider it so. if (abs (height * 16/9 - width) < 10) { - return 1; + ratio = 1; } // 16:10 has more variance in the pixel dimensions. Grr. - if (abs (height * 16/10 - width) < 60) + else if (abs (height * 16/10 - width) < 60) { // 320x200 and 640x400 are always 4:3, not 16:10 if ((width == 320 && height == 200) || (width == 640 && height == 400)) { - return 0; + ratio = 0; + } + else + { + ratio = 2; } - return 2; } // Unless vid_tft is set, 1280x1024 is 4:3, not 5:4. - if (height * 5/4 == width && vid_tft) + else if (height * 5/4 == width && vid_tft) { - return 4; + ratio = 4; } // Assume anything else is 4:3. - return 0; + else + { + ratio = 0; + } + + if (trueratio != NULL) + { + *trueratio = ratio; + } + return (fakeratio >= 0) ? fakeratio : ratio; } // First column: Base width (unused) diff --git a/src/v_video.h b/src/v_video.h index 0fb9eaf8aa..b56a4d2895 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -364,6 +364,9 @@ public: // Set the rect defining the area affected by blending. virtual void SetBlendingRect (int x1, int y1, int x2, int y2); + // Remap voxel palette + virtual void RemapVoxels(); + // render 3D view virtual void RenderView(player_t *player); @@ -500,7 +503,7 @@ void V_DrawFrame (int left, int top, int width, int height); extern "C" void ASM_PatchPitch (void); #endif -int CheckRatio (int width, int height); +int CheckRatio (int width, int height, int *trueratio=NULL); static inline int CheckRatio (double width, double height) { return CheckRatio(int(width), int(height)); } extern const int BaseRatioSizes[5][4]; diff --git a/src/w_wad.h b/src/w_wad.h index dc1ea7794c..76d94959d6 100644 --- a/src/w_wad.h +++ b/src/w_wad.h @@ -62,6 +62,7 @@ typedef enum { ns_bloodmisc, ns_strifevoices, ns_hires, + ns_voxels, // These namespaces are only used to mark lumps in special subdirectories // so that their contents doesn't interfere with the global namespace. diff --git a/zdoom.vcproj b/zdoom.vcproj index c96f7e246c..29801cd05f 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -704,6 +704,10 @@ RelativePath=".\src\md5.cpp" > + + @@ -1353,6 +1357,10 @@ RelativePath=".\src\md5.h" > + +