summaryrefslogtreecommitdiffstats
path: root/src/rw
diff options
context:
space:
mode:
Diffstat (limited to 'src/rw')
-rw-r--r--src/rw/Lights.cpp41
-rw-r--r--src/rw/Lights.h2
-rw-r--r--src/rw/MemoryHeap.cpp497
-rw-r--r--src/rw/MemoryHeap.h225
-rw-r--r--src/rw/MemoryMgr.cpp130
-rw-r--r--src/rw/MemoryMgr.h12
-rw-r--r--src/rw/RwHelper.cpp123
-rw-r--r--src/rw/RwHelper.h7
-rw-r--r--src/rw/TexRead.cpp108
-rw-r--r--src/rw/TexturePools.cpp221
-rw-r--r--src/rw/TexturePools.h42
-rw-r--r--src/rw/VisibilityPlugins.cpp172
12 files changed, 1499 insertions, 81 deletions
diff --git a/src/rw/Lights.cpp b/src/rw/Lights.cpp
index 5a253854..b3cef6d4 100644
--- a/src/rw/Lights.cpp
+++ b/src/rw/Lights.cpp
@@ -3,6 +3,7 @@
#include <rpworld.h>
#include "Lights.h"
+#include "Timer.h"
#include "Timecycle.h"
#include "Coronas.h"
#include "Weather.h"
@@ -248,6 +249,46 @@ SetAmbientAndDirectionalColours(float f)
RpLightSetColor(pDirect, &DirectionalLightColour);
}
+// unused
+void
+SetFlashyColours(float f)
+{
+ if(CTimer::GetTimeInMilliseconds() & 0x100){
+ AmbientLightColour.red = 1.0f;
+ AmbientLightColour.green = 1.0f;
+ AmbientLightColour.blue = 1.0f;
+
+ DirectionalLightColour.red = DirectionalLightColourForFrame.red;
+ DirectionalLightColour.green = DirectionalLightColourForFrame.green;
+ DirectionalLightColour.blue = DirectionalLightColourForFrame.blue;
+
+ RpLightSetColor(pAmbient, &AmbientLightColour);
+ RpLightSetColor(pDirect, &DirectionalLightColour);
+ }else{
+ SetAmbientAndDirectionalColours(f * 0.75f);
+ }
+}
+
+// unused
+void
+SetFlashyColours_Mild(float f)
+{
+ if(CTimer::GetTimeInMilliseconds() & 0x100){
+ AmbientLightColour.red = 0.65f;
+ AmbientLightColour.green = 0.65f;
+ AmbientLightColour.blue = 0.65f;
+
+ DirectionalLightColour.red = DirectionalLightColourForFrame.red;
+ DirectionalLightColour.green = DirectionalLightColourForFrame.green;
+ DirectionalLightColour.blue = DirectionalLightColourForFrame.blue;
+
+ RpLightSetColor(pAmbient, &AmbientLightColour);
+ RpLightSetColor(pDirect, &DirectionalLightColour);
+ }else{
+ SetAmbientAndDirectionalColours(f * 0.9f);
+ }
+}
+
void
SetBrightMarkerColours(float f)
{
diff --git a/src/rw/Lights.h b/src/rw/Lights.h
index b296816b..5057f1d0 100644
--- a/src/rw/Lights.h
+++ b/src/rw/Lights.h
@@ -14,6 +14,8 @@ void WorldReplaceScorchedLightsWithNormal(RpWorld *world);
void AddAnExtraDirectionalLight(RpWorld *world, float dirx, float diry, float dirz, float red, float green, float blue);
void RemoveExtraDirectionalLights(RpWorld *world);
void SetAmbientAndDirectionalColours(float f);
+void SetFlashyColours(float f);
+void SetFlashyColours_Mild(float f);
void SetBrightMarkerColours(float f);
void ReSetAmbientAndDirectionalColours(void);
void DeActivateDirectional(void);
diff --git a/src/rw/MemoryHeap.cpp b/src/rw/MemoryHeap.cpp
new file mode 100644
index 00000000..469262d3
--- /dev/null
+++ b/src/rw/MemoryHeap.cpp
@@ -0,0 +1,497 @@
+#include "common.h"
+#include "main.h"
+#include "FileMgr.h"
+#include "Timer.h"
+#include "ModelInfo.h"
+#include "Streaming.h"
+#include "FileLoader.h"
+#include "MemoryHeap.h"
+
+#ifdef USE_CUSTOM_ALLOCATOR
+
+//#define MEMORYHEAP_ASSERT(cond) { if (!(cond)) { printf("ASSERT File:%s Line:%d\n", __FILE__, __LINE__); exit(1); } }
+//#define MEMORYHEAP_ASSERT_MESSAGE(cond, message) { if (!(cond)) { printf("ASSERT File:%s Line:%d:\n\t%s\n", __FILE__, __LINE__, message); exit(1); } }
+
+#define MEMORYHEAP_ASSERT(cond) assert(cond)
+#define MEMORYHEAP_ASSERT_MESSAGE(cond, message) assert(cond)
+
+// registered pointers that we keep track of
+void **gPtrList[4000];
+int32 numPtrs;
+int32 gPosnInList;
+// indices into the ptr list in here are free
+CStack<int32, 4000> m_ptrListIndexStack;
+// how much memory we've moved
+uint32 memMoved;
+
+CMemoryHeap gMainHeap;
+
+void
+CMemoryHeap::Init(uint32 total)
+{
+ MEMORYHEAP_ASSERT((total != 0xF) != 0);
+
+ m_totalMemUsed = 0;
+ m_memUsed = nil;
+ m_currentMemID = MEMID_FREE;
+ m_blocksUsed = nil;
+ m_totalBlocksUsed = 0;
+ m_unkMemId = -1;
+
+ uint8 *mem = (uint8*)malloc(total);
+ assert(((uintptr)mem & 0xF) == 0);
+ m_start = (HeapBlockDesc*)mem;
+ m_end = (HeapBlockDesc*)(mem + total - sizeof(HeapBlockDesc));
+ m_start->m_memId = MEMID_FREE;
+ m_start->m_size = total - 2*sizeof(HeapBlockDesc);
+ m_end->m_memId = MEMID_GAME;
+ m_end->m_size = 0;
+
+ m_freeList.m_last.m_size = INT_MAX;
+ m_freeList.Init();
+ m_freeList.Insert(m_start);
+
+ // TODO: figure out what these are and use sizeof
+ m_fixedSize[0].Init(0x10);
+ m_fixedSize[1].Init(0x20);
+ m_fixedSize[2].Init(0xE0);
+ m_fixedSize[3].Init(0x60);
+ m_fixedSize[4].Init(0x1C0);
+ m_fixedSize[5].Init(0x50);
+
+ m_currentMemID = MEMID_FREE; // disable registration
+ m_memUsed = (uint32*)Malloc(NUM_MEMIDS * sizeof(uint32));
+ m_blocksUsed = (uint32*)Malloc(NUM_MEMIDS * sizeof(uint32));
+ RegisterMalloc(GetDescFromHeapPointer(m_memUsed));
+ RegisterMalloc(GetDescFromHeapPointer(m_blocksUsed));
+
+ m_currentMemID = MEMID_GAME;
+ for(int i = 0; i < NUM_MEMIDS; i++){
+ m_memUsed[i] = 0;
+ m_blocksUsed[i] = 0;
+ }
+}
+
+void
+CMemoryHeap::RegisterMalloc(HeapBlockDesc *block)
+{
+ block->m_memId = m_currentMemID;
+ if(m_currentMemID == MEMID_FREE)
+ return;
+ m_totalMemUsed += block->m_size + sizeof(HeapBlockDesc);
+ m_memUsed[m_currentMemID] += block->m_size + sizeof(HeapBlockDesc);
+ m_blocksUsed[m_currentMemID]++;
+ m_totalBlocksUsed++;
+}
+
+void
+CMemoryHeap::RegisterFree(HeapBlockDesc *block)
+{
+ if(block->m_memId == MEMID_FREE)
+ return;
+ m_totalMemUsed -= block->m_size + sizeof(HeapBlockDesc);
+ m_memUsed[block->m_memId] -= block->m_size + sizeof(HeapBlockDesc);
+ m_blocksUsed[block->m_memId]--;
+ m_totalBlocksUsed--;
+}
+
+void*
+CMemoryHeap::Malloc(uint32 size)
+{
+ static int recursion = 0;
+
+ // weird way to round up
+ if((size & 0xF) != 0)
+ size = (size&~0xF) + 0x10;
+
+ recursion++;
+
+ // See if we can allocate from one of the fixed-size lists
+ for(int i = 0; i < NUM_FIXED_MEMBLOCKS; i++){
+ CommonSize *list = &m_fixedSize[i];
+ if(m_fixedSize[i].m_size == size){
+ HeapBlockDesc *block = list->Malloc();
+ if(block){
+ RegisterMalloc(block);
+ recursion--;
+ return block->GetDataPointer();
+ }
+ break;
+ }
+ }
+
+ // now try the normal free list
+ HeapBlockDesc *next;
+ for(HeapBlockDesc *block = m_freeList.m_first.m_next;
+ block != &m_freeList.m_last;
+ block = next){
+ MEMORYHEAP_ASSERT(block->m_memId == MEMID_FREE);
+ MEMORYHEAP_ASSERT_MESSAGE(block >= m_start && block <= m_end, "Block outside of memory");
+
+ // make sure block has maximum size
+ uint32 initialsize = block->m_size;
+ uint32 blocksize = CombineFreeBlocks(block);
+#ifdef FIX_BUGS
+ // has to be done here because block can be moved
+ next = block->m_next;
+#endif
+ if(initialsize != blocksize){
+ block->RemoveHeapFreeBlock();
+ HeapBlockDesc *pos = block->m_prev->FindSmallestFreeBlock(block->m_size);
+ block->InsertHeapFreeBlock(pos->m_prev);
+ }
+ if(block->m_size >= size){
+ // got space to allocate from!
+ block->RemoveHeapFreeBlock();
+ FillInBlockData(block, block->GetNextConsecutive(), size);
+ recursion--;
+ return block->GetDataPointer();
+ }
+#ifndef FIX_BUGS
+ next = block->m_next;
+#endif
+ }
+
+ // oh no, we're losing, try to free some stuff
+ static bool removeCollision = false;
+ static bool removeIslands = false;
+ static bool removeBigBuildings = false;
+ size_t initialMemoryUsed = CStreaming::ms_memoryUsed;
+ CStreaming::MakeSpaceFor(0xCFE800 - CStreaming::ms_memoryUsed);
+ if (recursion > 10)
+ CGame::TidyUpMemory(true, false);
+ else if (recursion > 6)
+ CGame::TidyUpMemory(false, true);
+ if (initialMemoryUsed == CStreaming::ms_memoryUsed && recursion > 11) {
+ if (!removeCollision && !CGame::playingIntro) {
+ CModelInfo::RemoveColModelsFromOtherLevels(LEVEL_GENERIC);
+ removeCollision = true;
+ }
+ else if (!removeIslands && !CGame::playingIntro) {
+ CStreaming::RemoveIslandsNotUsed(LEVEL_INDUSTRIAL);
+ CStreaming::RemoveIslandsNotUsed(LEVEL_COMMERCIAL);
+ CStreaming::RemoveIslandsNotUsed(LEVEL_SUBURBAN);
+ removeIslands = true;
+ }
+ else if (!removeBigBuildings) {
+ CStreaming::RemoveBigBuildings(LEVEL_INDUSTRIAL);
+ CStreaming::RemoveBigBuildings(LEVEL_COMMERCIAL);
+ CStreaming::RemoveBigBuildings(LEVEL_SUBURBAN);
+ }
+ else {
+ LoadingScreen("NO MORE MEMORY", nil, nil);
+ LoadingScreen("NO MORE MEMORY", nil, nil);
+ }
+ CGame::TidyUpMemory(true, false);
+ }
+ void *mem = Malloc(size);
+ if (removeCollision) {
+ CTimer::Stop();
+ // TODO: different on PS2
+ CFileLoader::LoadCollisionFromDatFile(CCollision::ms_collisionInMemory);
+ removeCollision = false;
+ CTimer::Update();
+ }
+ if (removeBigBuildings || removeIslands) {
+ CTimer::Stop();
+ if (!CGame::playingIntro)
+ CStreaming::RequestBigBuildings(CGame::currLevel);
+ CStreaming::LoadAllRequestedModels(true);
+ removeBigBuildings = false;
+ removeIslands = false;
+ CTimer::Update();
+ }
+ recursion--;
+ return mem;
+}
+
+void*
+CMemoryHeap::Realloc(void *ptr, uint32 size)
+{
+ if(ptr == nil)
+ return Malloc(size);
+
+ // weird way to round up
+ if((size & 0xF) != 0)
+ size = (size&~0xF) + 0x10;
+
+ HeapBlockDesc *block = GetDescFromHeapPointer(ptr);
+
+#ifdef FIX_BUGS
+ // better handling of size < block->m_size
+ if(size == 0){
+ Free(ptr);
+ return nil;
+ }
+ if(block->m_size >= size){
+ // shrink allocated block
+ RegisterFree(block);
+ PushMemId(block->m_memId);
+ FillInBlockData(block, block->GetNextConsecutive(), size);
+ PopMemId();
+ return ptr;
+ }
+#else
+ // not growing. just returning here is a bit cheap though
+ if(block->m_size >= size)
+ return ptr;
+#endif
+
+ // have to grow allocated block
+ HeapBlockDesc *next = block->GetNextConsecutive();
+ MEMORYHEAP_ASSERT_MESSAGE(next >= m_start && next <= m_end, "Block outside of memory");
+ if(next->m_memId == MEMID_FREE){
+ // try to grow the current block
+ // make sure the next free block has maximum size
+ uint32 freespace = CombineFreeBlocks(next);
+ HeapBlockDesc *end = next->GetNextConsecutive();
+ MEMORYHEAP_ASSERT_MESSAGE(end >= m_start && end <= m_end, "Block outside of memory");
+ // why the sizeof here?
+ if(block->m_size + next->m_size + sizeof(HeapBlockDesc) >= size){
+ // enough space to grow
+ next->RemoveHeapFreeBlock();
+ RegisterFree(block);
+ PushMemId(block->m_memId);
+ FillInBlockData(block, next->GetNextConsecutive(), size);
+ PopMemId();
+ return ptr;
+ }
+ }
+
+ // can't grow the existing block, have to get a new one and copy
+ PushMemId(block->m_memId);
+ void *dst = Malloc(size);
+ PopMemId();
+ memcpy(dst, ptr, block->m_size);
+ Free(ptr);
+ return dst;
+}
+
+void
+CMemoryHeap::Free(void *ptr)
+{
+ HeapBlockDesc *block = GetDescFromHeapPointer(ptr);
+ MEMORYHEAP_ASSERT_MESSAGE(block->m_memId != MEMID_FREE, "MemoryHeap corrupt");
+ MEMORYHEAP_ASSERT(m_unkMemId == -1 || m_unkMemId == block->m_memId);
+
+ RegisterFree(block);
+ block->m_memId = MEMID_FREE;
+ CombineFreeBlocks(block);
+ FreeBlock(block);
+ if(block->m_ptrListIndex != -1){
+ int32 idx = block->m_ptrListIndex;
+ gPtrList[idx] = nil;
+ m_ptrListIndexStack.push(idx);
+ }
+ block->m_ptrListIndex = -1;
+}
+
+// allocate 'size' bytes from 'block'
+void
+CMemoryHeap::FillInBlockData(HeapBlockDesc *block, HeapBlockDesc *end, uint32 size)
+{
+ block->m_size = size;
+ block->m_ptrListIndex = -1;
+ HeapBlockDesc *remainder = block->GetNextConsecutive();
+ MEMORYHEAP_ASSERT(remainder <= end);
+
+ if(remainder < end-1){
+ RegisterMalloc(block);
+
+ // can fit another block in the remaining space
+ remainder->m_size = GetSizeBetweenBlocks(remainder, end);
+ remainder->m_memId = MEMID_FREE;
+ MEMORYHEAP_ASSERT(remainder->m_size != 0);
+ FreeBlock(remainder);
+ }else{
+ // fully allocate this one
+ if(remainder < end)
+ // no gaps allowed
+ block->m_size = GetSizeBetweenBlocks(block, end);
+ RegisterMalloc(block);
+ }
+}
+
+// Make sure free block has no other free blocks after it
+uint32
+CMemoryHeap::CombineFreeBlocks(HeapBlockDesc *block)
+{
+ HeapBlockDesc *next = block->GetNextConsecutive();
+ if(next->m_memId != MEMID_FREE)
+ return block->m_size;
+ // get rid of free blocks after this one and adjust size
+ for(; next->m_memId == MEMID_FREE; next = next->GetNextConsecutive())
+ next->RemoveHeapFreeBlock();
+ block->m_size = GetSizeBetweenBlocks(block, next);
+ return block->m_size;
+}
+
+// Try to move all registered memory blocks into more optimal location
+void
+CMemoryHeap::TidyHeap(void)
+{
+ for(int i = 0; i < numPtrs; i++){
+ if(gPtrList[i] == nil || *gPtrList[i] == nil)
+ continue;
+ HeapBlockDesc *newblock = WhereShouldMemoryMove(*gPtrList[i]);
+ if(newblock)
+ *gPtrList[i] = MoveHeapBlock(newblock, GetDescFromHeapPointer(*gPtrList[i]));
+ }
+}
+
+//
+void
+CMemoryHeap::RegisterMemPointer(void *ptr)
+{
+ HeapBlockDesc *block = GetDescFromHeapPointer(*(void**)ptr);
+
+ if(block->m_ptrListIndex != -1)
+ return; // already registered
+
+ int index;
+ if(m_ptrListIndexStack.sp > 0){
+ // re-use a previously free'd index
+ index = m_ptrListIndexStack.pop();
+ }else{
+ // have to find a new index
+ index = gPosnInList;
+
+ void **pp = gPtrList[index];
+ // we're replacing an old pointer here??
+ if(pp && *pp && *pp != (void*)0xDDDDDDDD)
+ GetDescFromHeapPointer(*pp)->m_ptrListIndex = -1;
+
+ gPosnInList++;
+ if(gPosnInList == 4000)
+ gPosnInList = 0;
+ if(numPtrs < 4000)
+ numPtrs++;
+ }
+ gPtrList[index] = (void**)ptr;
+ block->m_ptrListIndex = index;
+}
+
+void*
+CMemoryHeap::MoveMemory(void *ptr)
+{
+ HeapBlockDesc *newblock = WhereShouldMemoryMove(ptr);
+ if(newblock)
+ return MoveHeapBlock(newblock, GetDescFromHeapPointer(ptr));
+ else
+ return ptr;
+}
+
+HeapBlockDesc*
+CMemoryHeap::WhereShouldMemoryMove(void *ptr)
+{
+ HeapBlockDesc *block = GetDescFromHeapPointer(ptr);
+ MEMORYHEAP_ASSERT(block->m_memId != MEMID_FREE);
+
+ HeapBlockDesc *next = block->GetNextConsecutive();
+ if(next->m_memId != MEMID_FREE)
+ return nil;
+
+ // we want to move the block into another block
+ // such that the free space between this and the next block can be minimized
+ HeapBlockDesc *newblock = m_freeList.m_first.FindSmallestFreeBlock(block->m_size);
+ // size of free space wouldn't decrease, so return
+ if(newblock->m_size >= block->m_size + next->m_size)
+ return nil;
+ // size of free space wouldn't decrease enough
+ if(newblock->m_size >= 16 + 1.125f*block->m_size) // what are 16 and 1.125 here? sizeof(HeapBlockDesc)?
+ return nil;
+ return newblock;
+}
+
+void*
+CMemoryHeap::MoveHeapBlock(HeapBlockDesc *dst, HeapBlockDesc *src)
+{
+ PushMemId(src->m_memId);
+ dst->RemoveHeapFreeBlock();
+ FillInBlockData(dst, dst->GetNextConsecutive(), src->m_size);
+ PopMemId();
+ memcpy(dst->GetDataPointer(), src->GetDataPointer(), src->m_size);
+ memMoved += src->m_size;
+ dst->m_ptrListIndex = src->m_ptrListIndex;
+ src->m_ptrListIndex = -1;
+ Free(src->GetDataPointer());
+ return dst->GetDataPointer();
+}
+
+uint32
+CMemoryHeap::GetMemoryUsed(int32 id)
+{
+ return m_memUsed[id];
+}
+
+uint32
+CMemoryHeap::GetBlocksUsed(int32 id)
+{
+ return m_blocksUsed[id];
+}
+
+void
+CMemoryHeap::PopMemId(void)
+{
+ assert(m_idStack.sp > 0);
+ m_currentMemID = m_idStack.pop();
+ assert(m_currentMemID != MEMID_FREE);
+}
+
+void
+CMemoryHeap::PushMemId(int32 id)
+{
+ MEMORYHEAP_ASSERT(id != MEMID_FREE);
+ assert(m_idStack.sp < 16);
+ m_idStack.push(m_currentMemID);
+ m_currentMemID = id;
+}
+
+void
+CMemoryHeap::ParseHeap(void)
+{
+ char tmp[16];
+ int fd = CFileMgr::OpenFileForWriting("heap.txt");
+ CTimer::Stop();
+
+ // CMemoryHeap::IntegrityCheck();
+
+ uint32 addrQW = 0;
+ for(HeapBlockDesc *block = m_start; block < m_end; block = block->GetNextConsecutive()){
+ char chr = '*'; // free
+ if(block->m_memId != MEMID_FREE)
+ chr = block->m_memId-1 + 'A';
+ int numQW = block->m_size>>4;
+
+ if((addrQW & 0x3F) == 0){
+ sprintf(tmp, "\n%5dK:", addrQW>>6);
+ CFileMgr::Write(fd, tmp, 8);
+ }
+ CFileMgr::Write(fd, "#", 1); // the descriptor, has to be 16 bytes!!!!
+ addrQW++;
+
+ while(numQW--){
+ if((addrQW & 0x3F) == 0){
+ sprintf(tmp, "\n%5dK:", addrQW>>6);
+ CFileMgr::Write(fd, tmp, 8);
+ }
+ CFileMgr::Write(fd, &chr, 1);
+ addrQW++;
+ }
+ }
+
+ CTimer::Update();
+ CFileMgr::CloseFile(fd);
+}
+
+
+void
+CommonSize::Init(uint32 size)
+{
+ m_freeList.Init();
+ m_size = size;
+ m_failed = 0;
+ m_remaining = 0;
+}
+
+#endif
diff --git a/src/rw/MemoryHeap.h b/src/rw/MemoryHeap.h
new file mode 100644
index 00000000..23163c1c
--- /dev/null
+++ b/src/rw/MemoryHeap.h
@@ -0,0 +1,225 @@
+#pragma once
+
+// some windows shit
+#ifdef MoveMemory
+#undef MoveMemory
+#endif
+
+#ifdef USE_CUSTOM_ALLOCATOR
+#define PUSH_MEMID(id) gMainHeap.PushMemId(id)
+#define POP_MEMID() gMainHeap.PopMemId()
+#define REGISTER_MEMPTR(ptr) gMainHeap.RegisterMemPointer(ptr)
+#else
+#define PUSH_MEMID(id)
+#define POP_MEMID()
+#define REGISTER_MEMPTR(ptr)
+#endif
+
+enum {
+ MEMID_FREE,
+ // IDs from LCS:
+/*
+ MEMID_GAME = 1, // "Game"
+ MEMID_WORLD = 2, // "World"
+ MEMID_ANIMATION = 3, // "Animation"
+ MEMID_POOLS = 4, // "Pools"
+ MEMID_DEF_MODELS = 5, // "Default Models"
+ MEMID_STREAM = 6, // "Streaming"
+ MEMID_STREAM_MODELS = 7, // "Streamed Models"
+ MEMID_STREAM_LODS = 8, // "Streamed LODs"
+ MEMID_STREAM_TEXUTRES = 9, // "Streamed Textures"
+ MEMID_STREAM_COLLISION = 10, // "Streamed Collision"
+ MEMID_STREAM_ANIMATION = 11, // "Streamed Animation"
+ MEMID_TEXTURES = 12, // "Textures"
+ MEMID_COLLISION = 13, // "Collision"
+ MEMID_PRE_ALLOC = 14, // "PreAlloc"
+ MEMID_GAME_PROCESS = 15, // "Game Process"
+ MEMID_SCRIPT = 16, // "Script"
+ MEMID_CARS = 17, // "Cars"
+ MEMID_RENDER = 18, // "Render"
+ MEMID_PED_ATTR = 19, // "Ped Attr"
+*/
+ // III:
+ MEMID_GAME = 1, // "Game"
+ MEMID_WORLD = 2, // "World"
+ MEMID_ANIMATION = 3, // "Animation"
+ MEMID_POOLS = 4, // "Pools"
+ MEMID_DEF_MODELS = 5, // "Default Models"
+ MEMID_STREAM = 6, // "Streaming"
+ MEMID_STREAM_MODELS = 7, // "Streamed Models" (instance)
+ MEMID_STREAM_TEXUTRES = 8, // "Streamed Textures"
+ MEMID_TEXTURES = 9, // "Textures"
+ MEMID_COLLISION = 10, // "Collision"
+ MEMID_RENDERLIST = 11, // ?
+ MEMID_GAME_PROCESS = 12, // "Game Process"
+ MEMID_SCRIPT = 13, // "Script"
+ MEMID_CARS = 14, // "Cars"
+ MEMID_RENDER = 15, // "Render"
+ MEMID_FRONTEND = 17, // ?
+
+ NUM_MEMIDS,
+
+ NUM_FIXED_MEMBLOCKS = 6
+};
+
+template<typename T, uint32 N>
+class CStack
+{
+public:
+ T values[N];
+ uint32 sp;
+
+ CStack() : sp(0) {}
+ void push(const T& val) { values[sp++] = val; }
+ T& pop() { return values[--sp]; }
+};
+
+
+struct HeapBlockDesc
+{
+ uint32 m_size;
+ int16 m_memId;
+ int16 m_ptrListIndex;
+ HeapBlockDesc *m_next;
+ HeapBlockDesc *m_prev;
+
+ HeapBlockDesc *GetNextConsecutive(void)
+ {
+ return (HeapBlockDesc*)((uintptr)this + sizeof(HeapBlockDesc) + m_size);
+ }
+
+ void *GetDataPointer(void)
+ {
+ return (void*)((uintptr)this + sizeof(HeapBlockDesc));
+ }
+
+ void RemoveHeapFreeBlock(void)
+ {
+ m_next->m_prev = m_prev;
+ m_prev->m_next = m_next;
+ }
+
+ // after node
+ void InsertHeapFreeBlock(HeapBlockDesc *node)
+ {
+ m_next = node->m_next;
+ node->m_next->m_prev = this;
+ m_prev = node;
+ node->m_next = this;
+ }
+
+ HeapBlockDesc *FindSmallestFreeBlock(uint32 size)
+ {
+ HeapBlockDesc *b;
+ for(b = m_next; b->m_size < size; b = b->m_next);
+ return b;
+ }
+};
+
+#ifdef USE_CUSTOM_ALLOCATOR
+// TODO: figure something out for 64 bit pointers
+static_assert(sizeof(HeapBlockDesc) == 0x10, "HeapBlockDesc must have 0x10 size otherwise most of assumptions don't make sense");
+#endif
+
+struct HeapBlockList
+{
+ HeapBlockDesc m_first;
+ HeapBlockDesc m_last;
+
+ void Init(void)
+ {
+ m_first.m_next = &m_last;
+ m_last.m_prev = &m_first;
+ }
+
+ void Insert(HeapBlockDesc *node)
+ {
+ node->InsertHeapFreeBlock(&m_first);
+ }
+};
+
+struct CommonSize
+{
+ HeapBlockList m_freeList;
+ uint32 m_size;
+ uint32 m_failed;
+ uint32 m_remaining;
+
+ void Init(uint32 size);
+ void Free(HeapBlockDesc *node)
+ {
+ m_freeList.Insert(node);
+ m_remaining++;
+ }
+ HeapBlockDesc *Malloc(void)
+ {
+ if(m_freeList.m_first.m_next == &m_freeList.m_last){
+ m_failed++;
+ return nil;
+ }
+ HeapBlockDesc *block = m_freeList.m_first.m_next;
+ m_remaining--;
+ block->RemoveHeapFreeBlock();
+ block->m_ptrListIndex = -1;
+ return block;
+ }
+};
+
+class CMemoryHeap
+{
+public:
+ HeapBlockDesc *m_start;
+ HeapBlockDesc *m_end;
+ HeapBlockList m_freeList;
+ CommonSize m_fixedSize[NUM_FIXED_MEMBLOCKS];
+ uint32 m_totalMemUsed;
+ CStack<int32, 16> m_idStack;
+ uint32 m_currentMemID;
+ uint32 *m_memUsed;
+ uint32 m_totalBlocksUsed;
+ uint32 *m_blocksUsed;
+ uint32 m_unkMemId;
+
+ CMemoryHeap(void) : m_start(nil) {}
+ void Init(uint32 total);
+ void RegisterMalloc(HeapBlockDesc *block);
+ void RegisterFree(HeapBlockDesc *block);
+ void *Malloc(uint32 size);
+ void *Realloc(void *ptr, uint32 size);
+ void Free(void *ptr);
+ void FillInBlockData(HeapBlockDesc *block, HeapBlockDesc *end, uint32 size);
+ uint32 CombineFreeBlocks(HeapBlockDesc *block);
+ void *MoveMemory(void *ptr);
+ HeapBlockDesc *WhereShouldMemoryMove(void *ptr);
+ void *MoveHeapBlock(HeapBlockDesc *dst, HeapBlockDesc *src);
+ void PopMemId(void);
+ void PushMemId(int32 id);
+ void RegisterMemPointer(void *ptr);
+ void TidyHeap(void);
+ uint32 GetMemoryUsed(int32 id);
+ uint32 GetBlocksUsed(int32 id);
+ int32 GetLargestFreeBlock(void) { return m_freeList.m_last.m_prev->m_size; }
+
+ void ParseHeap(void);
+
+ HeapBlockDesc *GetDescFromHeapPointer(void *block)
+ {
+ return (HeapBlockDesc*)((uintptr)block - sizeof(HeapBlockDesc));
+ }
+ uint32 GetSizeBetweenBlocks(HeapBlockDesc *first, HeapBlockDesc *second)
+ {
+ return (uintptr)second - (uintptr)first - sizeof(HeapBlockDesc);
+ }
+ void FreeBlock(HeapBlockDesc *block){
+ for(int i = 0; i < NUM_FIXED_MEMBLOCKS; i++){
+ if(m_fixedSize[i].m_size == block->m_size){
+ m_fixedSize[i].Free(block);
+ return;
+ }
+ }
+ HeapBlockDesc *b = m_freeList.m_first.FindSmallestFreeBlock(block->m_size);
+ block->InsertHeapFreeBlock(b->m_prev);
+ }
+};
+
+extern CMemoryHeap gMainHeap;
diff --git a/src/rw/MemoryMgr.cpp b/src/rw/MemoryMgr.cpp
new file mode 100644
index 00000000..e2f6f144
--- /dev/null
+++ b/src/rw/MemoryMgr.cpp
@@ -0,0 +1,130 @@
+#include "common.h"
+#include "MemoryHeap.h"
+#include "MemoryMgr.h"
+
+
+uint8 *pMemoryTop;
+
+void
+InitMemoryMgr(void)
+{
+#ifdef USE_CUSTOM_ALLOCATOR
+#ifdef GTA_PS2
+#error "finish this"
+#else
+ // randomly allocate 128mb
+ gMainHeap.Init(128*1024*1024);
+#endif
+#endif
+}
+
+
+RwMemoryFunctions memFuncs = {
+ MemoryMgrMalloc,
+ MemoryMgrFree,
+ MemoryMgrRealloc,
+ MemoryMgrCalloc
+};
+
+#ifdef USE_CUSTOM_ALLOCATOR
+// game seems to be using heap directly here, but this is nicer
+void *operator new(size_t sz) { return MemoryMgrMalloc(sz); }
+void *operator new[](size_t sz) { return MemoryMgrMalloc(sz); }
+void operator delete(void *ptr) noexcept { MemoryMgrFree(ptr); }
+void operator delete[](void *ptr) noexcept { MemoryMgrFree(ptr); }
+#endif
+
+void*
+MemoryMgrMalloc(size_t size)
+{
+#ifdef USE_CUSTOM_ALLOCATOR
+ void *mem = gMainHeap.Malloc(size);
+#else
+ void *mem = malloc(size);
+#endif
+ if((uint8*)mem + size > pMemoryTop)
+ pMemoryTop = (uint8*)mem + size ;
+ return mem;
+}
+
+void*
+MemoryMgrRealloc(void *ptr, size_t size)
+{
+#ifdef USE_CUSTOM_ALLOCATOR
+ void *mem = gMainHeap.Realloc(ptr, size);
+#else
+ void *mem = realloc(ptr, size);
+#endif
+ if((uint8*)mem + size > pMemoryTop)
+ pMemoryTop = (uint8*)mem + size ;
+ return mem;
+}
+
+void*
+MemoryMgrCalloc(size_t num, size_t size)
+{
+#ifdef USE_CUSTOM_ALLOCATOR
+ void *mem = gMainHeap.Malloc(num*size);
+#else
+ void *mem = calloc(num, size);
+#endif
+ if((uint8*)mem + size > pMemoryTop)
+ pMemoryTop = (uint8*)mem + size ;
+#ifdef FIX_BUGS
+ memset(mem, 0, num*size);
+#endif
+ return mem;
+}
+
+void
+MemoryMgrFree(void *ptr)
+{
+#ifdef USE_CUSTOM_ALLOCATOR
+#ifdef FIX_BUGS
+ // i don't suppose this is handled by RW?
+ if(ptr == nil) return;
+#endif
+ gMainHeap.Free(ptr);
+#else
+ free(ptr);
+#endif
+}
+
+void *
+RwMallocAlign(RwUInt32 size, RwUInt32 align)
+{
+#ifdef FIX_BUGS
+ uintptr ptralign = align-1;
+ void *mem = (void *)MemoryMgrMalloc(size + sizeof(uintptr) + ptralign);
+
+ ASSERT(mem != nil);
+
+ void *addr = (void *)((((uintptr)mem) + sizeof(uintptr) + ptralign) & ~ptralign);
+
+ ASSERT(addr != nil);
+#else
+ void *mem = (void *)MemoryMgrMalloc(size + align);
+
+ ASSERT(mem != nil);
+
+ void *addr = (void *)((((uintptr)mem) + align) & ~(align - 1));
+
+ ASSERT(addr != nil);
+#endif
+
+ *(((void **)addr) - 1) = mem;
+
+ return addr;
+}
+
+void
+RwFreeAlign(void *mem)
+{
+ ASSERT(mem != nil);
+
+ void *addr = *(((void **)mem) - 1);
+
+ ASSERT(addr != nil);
+
+ MemoryMgrFree(addr);
+}
diff --git a/src/rw/MemoryMgr.h b/src/rw/MemoryMgr.h
new file mode 100644
index 00000000..e2962806
--- /dev/null
+++ b/src/rw/MemoryMgr.h
@@ -0,0 +1,12 @@
+#pragma once
+
+extern RwMemoryFunctions memFuncs;
+void InitMemoryMgr(void);
+
+void *MemoryMgrMalloc(size_t size);
+void *MemoryMgrRealloc(void *ptr, size_t size);
+void *MemoryMgrCalloc(size_t num, size_t size);
+void MemoryMgrFree(void *ptr);
+
+void *RwMallocAlign(RwUInt32 size, RwUInt32 align);
+void RwFreeAlign(void *mem);
diff --git a/src/rw/RwHelper.cpp b/src/rw/RwHelper.cpp
index 6a7010e2..e0133985 100644
--- a/src/rw/RwHelper.cpp
+++ b/src/rw/RwHelper.cpp
@@ -7,8 +7,10 @@
#include "Timecycle.h"
#include "skeleton.h"
#include "Debug.h"
-#ifndef FINAL
+#if !defined(FINAL) || defined(DEBUGMENU)
#include "rtcharse.h"
+#endif
+#ifndef FINAL
RtCharset *debugCharset;
#endif
@@ -19,7 +21,7 @@ bool gPS2alphaTest = false;
#endif
bool gBackfaceCulling = true;
-#ifndef FINAL
+#if !defined(FINAL) || defined(DEBUGMENU)
static bool charsetOpen;
void OpenCharsetSafe()
{
@@ -62,45 +64,6 @@ void FlushObrsPrintfs()
#endif
}
-void *
-RwMallocAlign(RwUInt32 size, RwUInt32 align)
-{
-#ifdef FIX_BUGS
- uintptr ptralign = align-1;
- void *mem = (void *)malloc(size + sizeof(uintptr) + ptralign);
-
- ASSERT(mem != nil);
-
- void *addr = (void *)((((uintptr)mem) + sizeof(uintptr) + ptralign) & ~ptralign);
-
- ASSERT(addr != nil);
-#else
- void *mem = (void *)malloc(size + align);
-
- ASSERT(mem != nil);
-
- void *addr = (void *)((((uintptr)mem) + align) & ~(align - 1));
-
- ASSERT(addr != nil);
-#endif
-
- *(((void **)addr) - 1) = mem;
-
- return addr;
-}
-
-void
-RwFreeAlign(void *mem)
-{
- ASSERT(mem != nil);
-
- void *addr = *(((void **)mem) - 1);
-
- ASSERT(addr != nil);
-
- free(addr);
-}
-
void
DefinedState(void)
{
@@ -642,9 +605,81 @@ CameraCreate(RwInt32 width, RwInt32 height, RwBool zBuffer)
return (nil);
}
-#ifdef USE_TEXTURE_POOL
-WRAPPER void _TexturePoolsInitialise() { EAXJMP(0x598B10); }
-WRAPPER void _TexturePoolsShutdown() { EAXJMP(0x598B30); }
+#ifdef LIBRW
+#include <rpmatfx.h>
+#include "VehicleModelInfo.h"
+
+int32
+findPlatform(rw::Atomic *a)
+{
+ rw::Geometry *g = a->geometry;
+ if(g->instData)
+ return g->instData->platform;
+ return 0;
+}
+
+// in CVehicleModelInfo in VC
+static RpMaterial*
+GetMatFXEffectMaterialCB(RpMaterial *material, void *data)
+{
+ if(RpMatFXMaterialGetEffects(material) == rpMATFXEFFECTNULL)
+ return material;
+ *(int*)data = RpMatFXMaterialGetEffects(material);
+ return nil;
+}
+
+// Game doesn't read atomic extensions so we never get any other than the default pipe,
+// but we need it for uninstancing
+void
+attachPipe(rw::Atomic *atomic)
+{
+ if(RpSkinGeometryGetSkin(RpAtomicGetGeometry(atomic)))
+ atomic->pipeline = rw::skinGlobals.pipelines[rw::platform];
+ else{
+ int fx = rpMATFXEFFECTNULL;
+ RpGeometryForAllMaterials(RpAtomicGetGeometry(atomic), GetMatFXEffectMaterialCB, &fx);
+ if(fx != rpMATFXEFFECTNULL)
+ RpMatFXAtomicEnableEffects(atomic);
+ }
+}
+
+// Attach pipes for the platform we have native data for so we can uninstance
+void
+switchPipes(rw::Atomic *a, int32 platform)
+{
+ if(a->pipeline && a->pipeline->platform != platform){
+ uint32 plgid = a->pipeline->pluginID;
+ switch(plgid){
+ // assume default pipe won't be attached explicitly
+ case rw::ID_SKIN:
+ a->pipeline = rw::skinGlobals.pipelines[platform];
+ break;
+ case rw::ID_MATFX:
+ a->pipeline = rw::matFXGlobals.pipelines[platform];
+ break;
+ }
+ }
+}
+
+RpAtomic*
+ConvertPlatformAtomic(RpAtomic *atomic, void *data)
+{
+ int32 driver = rw::platform;
+ int32 platform = findPlatform(atomic);
+ if(platform != 0 && platform != driver){
+ attachPipe(atomic); // kludge
+ rw::ObjPipeline *origPipe = atomic->pipeline;
+ rw::platform = platform;
+ switchPipes(atomic, rw::platform);
+ if(atomic->geometry->flags & rw::Geometry::NATIVE)
+ atomic->uninstance();
+ // no ADC in this game
+ //rw::ps2::unconvertADC(atomic->geometry);
+ rw::platform = driver;
+ atomic->pipeline = origPipe;
+ }
+ return atomic;
+}
#endif
#if defined(FIX_BUGS) && defined(GTA_PC)
diff --git a/src/rw/RwHelper.h b/src/rw/RwHelper.h
index 130eb636..1a5f64b1 100644
--- a/src/rw/RwHelper.h
+++ b/src/rw/RwHelper.h
@@ -2,9 +2,6 @@
extern bool gPS2alphaTest;
-void *RwMallocAlign(RwUInt32 size, RwUInt32 align);
-void RwFreeAlign(void *mem);
-
void OpenCharsetSafe();
void CreateDebugFont();
void DestroyDebugFont();
@@ -53,8 +50,8 @@ RwCamera *CameraCreate(RwInt32 width,
RwBool zBuffer);
-void _TexturePoolsInitialise();
-void _TexturePoolsShutdown();
+
+RpAtomic *ConvertPlatformAtomic(RpAtomic *atomic, void *data);
#if defined(FIX_BUGS) && defined (GTA_PC)
void SetAlphaTest(RwUInt32 alpharef);
diff --git a/src/rw/TexRead.cpp b/src/rw/TexRead.cpp
index 5dde20dd..d0addcca 100644
--- a/src/rw/TexRead.cpp
+++ b/src/rw/TexRead.cpp
@@ -18,6 +18,7 @@
#include "Sprite2d.h"
#include "Text.h"
#include "RwHelper.h"
+#include "Frontend.h"
#endif //GTA_PC
float texLoadTime;
@@ -150,11 +151,80 @@ RwTexDictionaryGtaStreamRead2(RwStream *stream, RwTexDictionary *texDict)
}
#ifdef GTA_PC
-#ifdef RWLIBS
-extern "C" RwInt32 _rwD3D8FindCorrectRasterFormat(RwRasterType type, RwInt32 flags);
-#else
-RwInt32 _rwD3D8FindCorrectRasterFormat(RwRasterType type, RwInt32 flags);
+
+#ifdef LIBRW
+
+#define CAPSVERSION 0
+
+struct GPUcaps
+{
+ uint32 version; // so we can force regeneration easily
+ uint32 platform;
+ uint32 subplatform;
+ uint32 dxtSupport;
+};
+
+static void
+GetGPUcaps(GPUcaps *caps)
+{
+ caps->version = CAPSVERSION;
+ caps->platform = rw::platform;
+ caps->subplatform = 0;
+ caps->dxtSupport = 0;
+ // TODO: more later
+#ifdef RW_GL3
+ caps->subplatform = rw::gl3::gl3Caps.gles;
+ caps->dxtSupport = rw::gl3::gl3Caps.dxtSupported;
+#endif
+#ifdef RW_D3D9
+ caps->dxtSupport = 1; // TODO, probably
#endif
+}
+
+void
+ReadVideoCardCapsFile(GPUcaps *caps)
+{
+ memset(caps, 0, sizeof(GPUcaps));
+
+ int32 file = CFileMgr::OpenFile("DATA\\CAPS.DAT", "rb");
+ if (file != 0) {
+ CFileMgr::Read(file, (char*)&caps->version, 4);
+ CFileMgr::Read(file, (char*)&caps->platform, 4);
+ CFileMgr::Read(file, (char*)&caps->subplatform, 4);
+ CFileMgr::Read(file, (char*)&caps->dxtSupport, 4);
+ CFileMgr::CloseFile(file);
+ }
+}
+
+bool
+CheckVideoCardCaps(void)
+{
+ GPUcaps caps, fcaps;
+ GetGPUcaps(&caps);
+ ReadVideoCardCapsFile(&fcaps);
+ return caps.version != fcaps.version ||
+ caps.platform != fcaps.platform ||
+ caps.subplatform != fcaps.subplatform ||
+ caps.dxtSupport != fcaps.dxtSupport;
+}
+
+void
+WriteVideoCardCapsFile(void)
+{
+ GPUcaps caps;
+ GetGPUcaps(&caps);
+ int32 file = CFileMgr::OpenFile("DATA\\CAPS.DAT", "wb");
+ if (file != 0) {
+ CFileMgr::Write(file, (char*)&caps.version, 4);
+ CFileMgr::Write(file, (char*)&caps.platform, 4);
+ CFileMgr::Write(file, (char*)&caps.subplatform, 4);
+ CFileMgr::Write(file, (char*)&caps.dxtSupport, 4);
+ CFileMgr::CloseFile(file);
+ }
+}
+
+#else
+extern "C" RwInt32 _rwD3D8FindCorrectRasterFormat(RwRasterType type, RwInt32 flags);
void
ReadVideoCardCapsFile(uint32 &cap32, uint32 &cap24, uint32 &cap16, uint32 &cap8)
{
@@ -201,6 +271,7 @@ WriteVideoCardCapsFile(void)
CFileMgr::CloseFile(file);
}
}
+#endif
void
ConvertingTexturesScreen(uint32 num, uint32 count, const char *text)
@@ -282,6 +353,22 @@ CreateTxdImageForVideoCard()
return false;
}
+#ifdef RW_GL3
+ // so we can read back DXT with GLES
+ // only works for textures that are not yet loaded
+ // so let's hope that is the case for all
+ rw::gl3::needToReadBackTextures = true;
+#endif
+
+#ifdef DISABLE_VSYNC_ON_TEXTURE_CONVERSION
+ // let's disable vsync and frame limiter to speed up texture conversion
+ // (actually we probably don't need to disable frame limiter in here, but let's do it just in case =P)
+ int8 vsyncState = CMenuManager::m_PrefsVsync;
+ int8 frameLimiterState = CMenuManager::m_PrefsFrameLimiter;
+ CMenuManager::m_PrefsVsync = 0;
+ CMenuManager::m_PrefsFrameLimiter = 0;
+#endif
+
int32 i;
for (i = 0; i < TXDSTORESIZE; i++) {
ConvertingTexturesScreen(i, TXDSTORESIZE, "CVT_MSG");
@@ -309,6 +396,9 @@ CreateTxdImageForVideoCard()
delete []buf;
delete pDir;
CStreaming::RemoveTxd(i);
+#ifdef RW_GL3
+ rw::gl3::needToReadBackTextures = false;
+#endif
return false;
}
@@ -332,9 +422,19 @@ CreateTxdImageForVideoCard()
}
}
+#ifdef DISABLE_VSYNC_ON_TEXTURE_CONVERSION
+ // restore vsync and frame limiter states
+ CMenuManager::m_PrefsVsync = vsyncState;
+ CMenuManager::m_PrefsFrameLimiter = frameLimiterState;
+#endif
+
RwStreamClose(img, nil);
delete []buf;
+#ifdef RW_GL3
+ rw::gl3::needToReadBackTextures = false;
+#endif
+
if (!pDir->WriteDirFile("models\\txd.dir")) {
DealWithTxdWriteError(i, TXDSTORESIZE, "CVT_ERR");
delete pDir;
diff --git a/src/rw/TexturePools.cpp b/src/rw/TexturePools.cpp
new file mode 100644
index 00000000..c2ba6cf9
--- /dev/null
+++ b/src/rw/TexturePools.cpp
@@ -0,0 +1,221 @@
+#ifndef LIBRW
+
+#include <d3d8.h>
+#define WITHD3D
+#include "common.h"
+#include "TexturePools.h"
+
+// TODO: this needs to be integrated into RW
+
+extern "C" LPDIRECT3DDEVICE8 _RwD3DDevice;
+
+CTexturePool aTexturePools[12];
+CPaletteList PaletteList;
+int numTexturePools;
+int MaxPaletteIndex;
+bool bUsePaletteIndex = true;
+
+
+void
+CTexturePool::Create(D3DFORMAT _Format, int _size, uint32 mipmapLevels, int32 numTextures)
+{
+ Format = _Format;
+ size = _size;
+ levels = mipmapLevels;
+ pTextures = new IDirect3DTexture8 *[numTextures];
+ texturesMax = numTextures;
+ texturesNum = 0;
+ texturesUsed = 0;
+}
+
+void
+CTexturePool::Release()
+{
+ int i = 0;
+ while (i < texturesNum) {
+ pTextures[i]->Release();
+ i++;
+ }
+
+ delete[] pTextures;
+
+ pTextures = nil;
+ texturesNum = 0;
+ texturesUsed = 0;
+}
+
+IDirect3DTexture8 *
+CTexturePool::FindTexture()
+{
+ if (texturesNum == 0)
+ return nil;
+ texturesUsed--;
+ return pTextures[--texturesNum];
+}
+
+bool
+CTexturePool::AddTexture(IDirect3DTexture8 *texture)
+{
+ ++texturesUsed;
+ if (texturesNum >= texturesMax)
+ return false;
+ pTextures[texturesNum] = texture;
+ ++texturesNum;
+ return true;
+}
+
+void
+CTexturePool::Resize(int numTextures)
+{
+ if (numTextures == texturesMax)
+ return;
+
+ IDirect3DTexture8 **newTextures = new IDirect3DTexture8 *[numTextures];
+
+ for (int i = 0; i < texturesNum && i < numTextures; i++)
+ newTextures[i] = pTextures[i];
+
+ if (numTextures < texturesNum) {
+ for (int i = numTextures; i < texturesNum; i++)
+ pTextures[i]->Release();
+ }
+ delete[] pTextures;
+ pTextures = newTextures;
+ texturesMax = numTextures;
+}
+
+void
+CPaletteList::Alloc(int max)
+{
+ Data = new int[max];
+ Max = max;
+ Num = 0;
+}
+
+void
+CPaletteList::Free()
+{
+ delete[] Data;
+ Data = nil;
+ Num = 0;
+}
+
+int
+CPaletteList::Find()
+{
+ if (Num == 0)
+ return -1;
+ return Data[--Num];
+}
+
+void
+CPaletteList::Add(int item)
+{
+ if (Num < Max)
+ Data[Num++] = item;
+ else {
+ Resize(2 * Max);
+ Add(item);
+ }
+}
+
+void
+CPaletteList::Resize(int max)
+{
+ if (max == Max)
+ return;
+
+ int *newData = new int[4 * max];
+ for (int i = 0; i < Num && i < max; i++)
+ newData[i] = Data[i];
+ delete[] Data;
+ Data = newData;
+ Max = max;
+}
+
+HRESULT
+CreateTexture(int width, int height, int levels, D3DFORMAT Format, IDirect3DTexture8 **texture)
+{
+ if (width == height) {
+ for (int i = 0; i < numTexturePools; i++) {
+ if (width != aTexturePools[i].GetSize() && levels == aTexturePools[i].levels && Format == aTexturePools[i].Format)
+ *texture = aTexturePools[i].FindTexture();
+ }
+ }
+ if (*texture)
+ return D3D_OK;
+ else
+ return _RwD3DDevice->CreateTexture(width, height, levels, 0, Format, D3DPOOL_MANAGED, texture);
+}
+
+void
+ReleaseTexture(IDirect3DTexture8 *texture)
+{
+ int levels = 1;
+ if (texture->GetLevelCount() > 1)
+ levels = 0;
+
+ D3DSURFACE_DESC SURFACE_DESC;
+
+ texture->GetLevelDesc(0, &SURFACE_DESC);
+
+ if (SURFACE_DESC.Width == SURFACE_DESC.Height) {
+ for (int i = 0; i < numTexturePools; i++) {
+ if (SURFACE_DESC.Width == aTexturePools[i].GetSize() && SURFACE_DESC.Format == aTexturePools[i].Format && levels == aTexturePools[i].levels) {
+ if (!aTexturePools[i].AddTexture(texture)) {
+ if (aTexturePools[i].texturesUsed > 3 * aTexturePools[i].texturesMax / 2) {
+ aTexturePools[i].Resize(2 * aTexturePools[i].texturesMax);
+ aTexturePools[i].texturesUsed--;
+ aTexturePools[i].AddTexture(texture);
+ } else {
+ texture->Release();
+ }
+ }
+ return;
+ }
+ }
+ }
+ if (numTexturePools < 12 && bUsePaletteIndex && levels != 0 && SURFACE_DESC.Width == SURFACE_DESC.Height &&
+ (SURFACE_DESC.Width == 64 || SURFACE_DESC.Width == 128 || SURFACE_DESC.Width == 256)) {
+ aTexturePools[numTexturePools].Create(SURFACE_DESC.Format, SURFACE_DESC.Width, 1, 16);
+ aTexturePools[numTexturePools].AddTexture(texture);
+ numTexturePools++;
+ } else
+ texture->Release();
+}
+
+int
+FindAvailablePaletteIndex()
+{
+ int index = PaletteList.Find();
+ if (index == -1)
+ index = MaxPaletteIndex++;
+ return index;
+}
+
+void
+AddAvailablePaletteIndex(int index)
+{
+ if (bUsePaletteIndex)
+ PaletteList.Add(index);
+}
+
+void
+_TexturePoolsInitialise()
+{
+ PaletteList.Alloc(100);
+ MaxPaletteIndex = 0;
+}
+
+void
+_TexturePoolsShutdown()
+{
+ for (int i = 0; i < numTexturePools; i++)
+ aTexturePools[i].Release();
+
+ numTexturePools = 0;
+ bUsePaletteIndex = false;
+ PaletteList.Free();
+}
+
+#endif // !LIBRW \ No newline at end of file
diff --git a/src/rw/TexturePools.h b/src/rw/TexturePools.h
new file mode 100644
index 00000000..75187432
--- /dev/null
+++ b/src/rw/TexturePools.h
@@ -0,0 +1,42 @@
+#pragma once
+
+class CTexturePool
+{
+public:
+ D3DFORMAT Format;
+ int size;
+ uint32 levels;
+ int32 texturesMax;
+ int32 texturesUsed;
+ int32 texturesNum;
+ IDirect3DTexture8 **pTextures;
+
+public:
+ CTexturePool() {}
+ void Create(D3DFORMAT _Format, int size, uint32 mipmapLevels, int32 numTextures);
+ void Release();
+ IDirect3DTexture8 *FindTexture();
+ bool AddTexture(IDirect3DTexture8 *texture);
+ void Resize(int numTextures);
+#ifdef FIX_BUGS
+ int GetSize() { return size; }
+#else
+ float GetSize() { return size; }
+#endif
+};
+
+class CPaletteList
+{
+ int Max;
+ int Num;
+ int *Data;
+public:
+ void Alloc(int max);
+ void Free();
+ int Find();
+ void Add(int item);
+ void Resize(int max);
+};
+
+void _TexturePoolsInitialise();
+void _TexturePoolsShutdown(); \ No newline at end of file
diff --git a/src/rw/VisibilityPlugins.cpp b/src/rw/VisibilityPlugins.cpp
index 8878a26a..b27d96c8 100644
--- a/src/rw/VisibilityPlugins.cpp
+++ b/src/rw/VisibilityPlugins.cpp
@@ -10,8 +10,7 @@
#include "VisibilityPlugins.h"
#include "World.h"
#include "custompipes.h"
-
-#define FADE_DISTANCE 20.0f
+#include "MemoryHeap.h"
CLinkList<CVisibilityPlugins::AlphaObjectInfo> CVisibilityPlugins::m_alphaList;
CLinkList<CVisibilityPlugins::AlphaObjectInfo> CVisibilityPlugins::m_alphaEntityList;
@@ -32,6 +31,119 @@ float CVisibilityPlugins::ms_pedLod0Dist;
float CVisibilityPlugins::ms_pedLod1Dist;
float CVisibilityPlugins::ms_pedFadeDist;
+#ifdef GTA_PS2 // maybe something else?
+// if wanted, delete the original geometry data after rendering
+// and only keep the instanced data
+bool
+rpDefaultGeometryInstance(RpGeometry *geo, void *atomic, int del)
+{
+#if THIS_IS_COMPATIBLE_WITH_GTA3_RW31
+ if(RpGeometryGetNumMorphTargets(geo) != 1)
+ return false;
+
+ // this needs R*'s modification that geometry data is
+ // allocated separately from the geometry itself
+ geo->instanceFlags = rpGEOMETRYINSTANCE;
+ AtomicDefaultRenderCallBack((RpAtomic*)atomic);
+
+ if(!del)
+ return true;
+
+ // New mesh without indices
+ RpMeshHeader *newheader = _rpMeshHeaderCreate(sizeof(RpMesh)*geo->mesh->numMeshes + sizeof(RpMeshHeader));
+ newheader->numMeshes = geo->mesh->numMeshes;
+ newheader->serialNum = 1;
+ newheader->totalIndicesInMesh = 0;
+ newheader->firstMeshOffset = 0;
+ RpMesh *oldmesh = (RpMesh*)(geo->mesh+1);
+ RpMesh *newmesh = (RpMesh*)(newheader+1);
+ for(int i = 0; i < geo->mesh->numMeshes; i++){
+ newmesh[i].indices = nil;
+ newmesh[i].numIndices = 0;
+ newmesh[i].material = oldmesh[i].material;
+ }
+
+ geo->refCount++;
+ RpGeometryLock(geo, rpGEOMETRYLOCKPOLYGONS | rpGEOMETRYLOCKVERTICES |
+ rpGEOMETRYLOCKNORMALS | rpGEOMETRYLOCKPRELIGHT |
+ rpGEOMETRYLOCKTEXCOORDS1 | rpGEOMETRYLOCKTEXCOORDS2);
+
+ // vertices and normals
+ RpMorphTarget *mt = RpGeometryGetMorphTarget(geo, 0);
+ if(mt->verts){
+ RwFree(mt->verts);
+ mt->verts = nil;
+ mt->normals = nil;
+ }
+ geo->numVertices = 0;
+
+ // triangles
+ for(int i = 0; i < RpGeometryGetNumTriangles(geo); i++){
+ if(RpGeometryGetTriangles(geo)->matIndex == -1)
+ continue;
+ RpMaterialDestroy(_rpMaterialListGetMaterial(&geo->matList, RpGeometryGetTriangles(geo)->matIndex));
+ }
+ if(RpGeometryGetTriangles(geo)){
+ RwFree(RpGeometryGetTriangles(geo));
+ geo->triangles = nil;
+ geo->numTriangles = 0;
+ }
+
+ // tex coords
+ if(RpGeometryGetVertexTexCoords(geo, 1)){
+ RwFree(RpGeometryGetVertexTexCoords(geo, 1));
+ geo->texCoords[1] = nil;
+ }
+ if(RpGeometryGetVertexTexCoords(geo, 0)){
+ RwFree(RpGeometryGetVertexTexCoords(geo, 0));
+ geo->texCoords[0] = nil;
+ }
+
+ // vertex colors
+ if(RpGeometryGetPreLightColors(geo)){
+ RwFree(RpGeometryGetPreLightColors(geo));
+ geo->preLitLum = nil;
+ }
+
+ RpGeometryUnlock(geo);
+
+ geo->instanceFlags = rpGEOMETRYPERSISTENT;
+ // BUG? don't we have to free the old mesh?
+ geo->mesh = newheader;
+ geo->refCount--;
+#else
+ // We can do something for librw here actually, maybe later
+ AtomicDefaultRenderCallBack((RpAtomic*)atomic);
+#endif
+
+ return true;
+}
+
+RpAtomic*
+PreInstanceRenderCB(RpAtomic *atomic)
+{
+ RpGeometry *geo = RpAtomicGetGeometry(atomic);
+ if(RpGeometryGetTriangles(geo)){
+ PUSH_MEMID(MEMID_STREAM_MODELS);
+ rpDefaultGeometryInstance(geo, atomic, 1);
+ POP_MEMID();
+ }else
+ AtomicDefaultRenderCallBack(atomic);
+ return atomic;
+}
+#define RENDERCALLBACK PreInstanceRenderCB
+#else
+RpAtomic*
+DefaultRenderCB_pushid(RpAtomic *atomic)
+{
+ PUSH_MEMID(MEMID_STREAM_MODELS);
+ AtomicDefaultRenderCallBack(atomic);
+ POP_MEMID();
+ return atomic;
+}
+#define RENDERCALLBACK DefaultRenderCB_pushid
+#endif
+
void
CVisibilityPlugins::Initialise(void)
{
@@ -134,7 +246,7 @@ CVisibilityPlugins::RenderAlphaAtomics(void)
for(node = m_alphaList.tail.prev;
node != &m_alphaList.head;
node = node->prev)
- AtomicDefaultRenderCallBack(node->item.atomic);
+ RENDERCALLBACK(node->item.atomic);
}
void
@@ -203,7 +315,7 @@ CVisibilityPlugins::RenderWheelAtomicCB(RpAtomic *atomic)
if(lodatm){
if(RpAtomicGetGeometry(lodatm) != RpAtomicGetGeometry(atomic))
RpAtomicSetGeometry(atomic, RpAtomicGetGeometry(lodatm), rpATOMICSAMEBOUNDINGSPHERE);
- AtomicDefaultRenderCallBack(atomic);
+ RENDERCALLBACK(atomic);
}
return atomic;
}
@@ -220,7 +332,7 @@ CVisibilityPlugins::RenderObjNormalAtomic(RpAtomic *atomic)
len = RwV3dLength(&view);
if(RwV3dDotProduct(&view, RwMatrixGetUp(m)) < -0.3f*len && len > 8.0f)
return atomic;
- AtomicDefaultRenderCallBack(atomic);
+ RENDERCALLBACK(atomic);
return atomic;
}
@@ -234,7 +346,7 @@ CVisibilityPlugins::RenderAlphaAtomic(RpAtomic *atomic, int alpha)
flags = RpGeometryGetFlags(geo);
RpGeometrySetFlags(geo, flags | rpGEOMETRYMODULATEMATERIALCOLOR);
RpGeometryForAllMaterials(geo, SetAlphaCB, (void*)alpha);
- AtomicDefaultRenderCallBack(atomic);
+ RENDERCALLBACK(atomic);
RpGeometryForAllMaterials(geo, SetAlphaCB, (void*)255);
RpGeometrySetFlags(geo, flags);
return atomic;
@@ -252,7 +364,7 @@ CVisibilityPlugins::RenderFadingAtomic(RpAtomic *atomic, float camdist)
lodatm = mi->GetAtomicFromDistance(camdist - FADE_DISTANCE);
if(mi->m_additive){
RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE);
- AtomicDefaultRenderCallBack(atomic);
+ RENDERCALLBACK(atomic);
RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);
}else{
fadefactor = (mi->GetLargestLodDistance() - (camdist - FADE_DISTANCE))/FADE_DISTANCE;
@@ -260,7 +372,7 @@ CVisibilityPlugins::RenderFadingAtomic(RpAtomic *atomic, float camdist)
fadefactor = 1.0f;
alpha = mi->m_alpha * fadefactor;
if(alpha == 255)
- AtomicDefaultRenderCallBack(atomic);
+ RENDERCALLBACK(atomic);
else{
RpGeometry *geo = RpAtomicGetGeometry(lodatm);
uint32 flags = RpGeometryGetFlags(geo);
@@ -268,7 +380,7 @@ CVisibilityPlugins::RenderFadingAtomic(RpAtomic *atomic, float camdist)
RpGeometryForAllMaterials(geo, SetAlphaCB, (void*)alpha);
if(geo != RpAtomicGetGeometry(atomic))
RpAtomicSetGeometry(atomic, geo, rpATOMICSAMEBOUNDINGSPHERE); // originally 5 (mistake?)
- AtomicDefaultRenderCallBack(atomic);
+ RENDERCALLBACK(atomic);
RpGeometryForAllMaterials(geo, SetAlphaCB, (void*)255);
RpGeometrySetFlags(geo, flags);
}
@@ -295,7 +407,7 @@ CVisibilityPlugins::RenderVehicleHiDetailCB(RpAtomic *atomic)
if(dot > 0.0f && ((flags & ATOMIC_FLAG_ANGLECULL) || 0.1f*distsq < dot*dot))
return atomic;
}
- AtomicDefaultRenderCallBack(atomic);
+ RENDERCALLBACK(atomic);
}
return atomic;
}
@@ -320,10 +432,10 @@ CVisibilityPlugins::RenderVehicleHiDetailAlphaCB(RpAtomic *atomic)
if(flags & ATOMIC_FLAG_DRAWLAST){
// sort before clump
if(!InsertAtomicIntoSortedList(atomic, distsq - 0.0001f))
- AtomicDefaultRenderCallBack(atomic);
+ RENDERCALLBACK(atomic);
}else{
if(!InsertAtomicIntoSortedList(atomic, distsq + dot))
- AtomicDefaultRenderCallBack(atomic);
+ RENDERCALLBACK(atomic);
}
}
return atomic;
@@ -346,7 +458,7 @@ CVisibilityPlugins::RenderVehicleHiDetailCB_BigVehicle(RpAtomic *atomic)
if(dot > 0.0f)
return atomic;
}
- AtomicDefaultRenderCallBack(atomic);
+ RENDERCALLBACK(atomic);
}
return atomic;
}
@@ -369,7 +481,7 @@ CVisibilityPlugins::RenderVehicleHiDetailAlphaCB_BigVehicle(RpAtomic *atomic)
return atomic;
if(!InsertAtomicIntoSortedList(atomic, distsq + dot))
- AtomicDefaultRenderCallBack(atomic);
+ RENDERCALLBACK(atomic);
}
return atomic;
}
@@ -383,7 +495,7 @@ CVisibilityPlugins::RenderVehicleHiDetailCB_Boat(RpAtomic *atomic)
clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic));
distsq = GetDistanceSquaredFromCamera(clumpframe);
if(distsq < ms_bigVehicleLod1Dist)
- AtomicDefaultRenderCallBack(atomic);
+ RENDERCALLBACK(atomic);
return atomic;
}
@@ -405,7 +517,7 @@ CVisibilityPlugins::RenderVehicleLowDetailCB_BigVehicle(RpAtomic *atomic)
if(dot > 0.0f)
return atomic;
}
- AtomicDefaultRenderCallBack(atomic);
+ RENDERCALLBACK(atomic);
}
return atomic;
}
@@ -429,7 +541,7 @@ CVisibilityPlugins::RenderVehicleLowDetailAlphaCB_BigVehicle(RpAtomic *atomic)
return atomic;
if(!InsertAtomicIntoSortedList(atomic, distsq + dot))
- AtomicDefaultRenderCallBack(atomic);
+ RENDERCALLBACK(atomic);
}
return atomic;
}
@@ -446,7 +558,7 @@ CVisibilityPlugins::RenderVehicleReallyLowDetailCB(RpAtomic *atomic)
if(dist >= ms_vehicleLod0Dist){
alpha = GetClumpAlpha(clump);
if(alpha == 255)
- AtomicDefaultRenderCallBack(atomic);
+ RENDERCALLBACK(atomic);
else
RenderAlphaAtomic(atomic, alpha);
}
@@ -463,7 +575,7 @@ CVisibilityPlugins::RenderVehicleReallyLowDetailCB_BigVehicle(RpAtomic *atomic)
clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic));
distsq = GetDistanceSquaredFromCamera(clumpframe);
if(distsq >= ms_bigVehicleLod1Dist)
- AtomicDefaultRenderCallBack(atomic);
+ RENDERCALLBACK(atomic);
return atomic;
}
@@ -484,7 +596,7 @@ CVisibilityPlugins::RenderTrainHiDetailCB(RpAtomic *atomic)
if(dot > 0.0f && ((flags & ATOMIC_FLAG_ANGLECULL) || 0.1f*distsq < dot*dot))
return atomic;
}
- AtomicDefaultRenderCallBack(atomic);
+ RENDERCALLBACK(atomic);
}
return atomic;
}
@@ -509,10 +621,10 @@ CVisibilityPlugins::RenderTrainHiDetailAlphaCB(RpAtomic *atomic)
if(flags & ATOMIC_FLAG_DRAWLAST){
// sort before clump
if(!InsertAtomicIntoSortedList(atomic, distsq - 0.0001f))
- AtomicDefaultRenderCallBack(atomic);
+ RENDERCALLBACK(atomic);
}else{
if(!InsertAtomicIntoSortedList(atomic, distsq + dot))
- AtomicDefaultRenderCallBack(atomic);
+ RENDERCALLBACK(atomic);
}
}
return atomic;
@@ -523,7 +635,7 @@ CVisibilityPlugins::RenderPlayerCB(RpAtomic *atomic)
{
if(CWorld::Players[0].m_pSkinTexture)
RpGeometryForAllMaterials(RpAtomicGetGeometry(atomic), SetTextureCB, CWorld::Players[0].m_pSkinTexture);
- AtomicDefaultRenderCallBack(atomic);
+ RENDERCALLBACK(atomic);
return atomic;
}
@@ -539,7 +651,7 @@ CVisibilityPlugins::RenderPedLowDetailCB(RpAtomic *atomic)
if(dist >= ms_pedLod0Dist){
alpha = GetClumpAlpha(clump);
if(alpha == 255)
- AtomicDefaultRenderCallBack(atomic);
+ RENDERCALLBACK(atomic);
else
RenderAlphaAtomic(atomic, alpha);
}
@@ -558,7 +670,7 @@ CVisibilityPlugins::RenderPedHiDetailCB(RpAtomic *atomic)
if(dist < ms_pedLod0Dist){
alpha = GetClumpAlpha(clump);
if(alpha == 255)
- AtomicDefaultRenderCallBack(atomic);
+ RENDERCALLBACK(atomic);
else
RenderAlphaAtomic(atomic, alpha);
}
@@ -577,7 +689,7 @@ CVisibilityPlugins::RenderPedCB(RpAtomic *atomic)
if(RwV3dDotProduct(&cam2atm, &cam2atm) < ms_pedLod1Dist){
alpha = GetClumpAlpha(RpAtomicGetClump(atomic));
if(alpha == 255)
- AtomicDefaultRenderCallBack(atomic);
+ RENDERCALLBACK(atomic);
else
RenderAlphaAtomic(atomic, alpha);
}
@@ -707,6 +819,11 @@ CVisibilityPlugins::PluginAttach(void)
ms_clumpPluginOffset = RpClumpRegisterPlugin(sizeof(ClumpExt),
ID_VISIBILITYCLUMP,
ClumpConstructor, ClumpDestructor, ClumpCopyConstructor);
+
+#if GTA_VERSION <= GTA3_PS2_160
+ Initialise();
+#endif
+
return ms_atomicPluginOffset != -1 && ms_clumpPluginOffset != -1;
}
@@ -777,12 +894,11 @@ CVisibilityPlugins::GetAtomicId(RpAtomic *atomic)
return ATOMICEXT(atomic)->flags;
}
-// This is rather useless, but whatever
void
CVisibilityPlugins::SetAtomicRenderCallback(RpAtomic *atomic, RpAtomicCallBackRender cb)
{
if(cb == nil)
- cb = AtomicDefaultRenderCallBack; // not necessary
+ cb = RENDERCALLBACK;
RpAtomicSetRenderCallBack(atomic, cb);
}