Complete disaster-recovery snapshot: engine/game source, game data assets, VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive. Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false, no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
539 lines
13 KiB
C++
539 lines
13 KiB
C++
#include "ProxyHeaders.hpp"
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
ArrangeMegatexturesProcess::ArrangeMegatexturesProcess(NotationFile *mega_file):
|
|
megaFile(mega_file)
|
|
{
|
|
Check_Pointer(this);
|
|
Check_Object(megaFile);
|
|
Check_Object(this);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
ArrangeMegatexturesProcess::ArrangeMegatexturesProcess(
|
|
NotationFile *config_file,
|
|
NotationFile *mega_file
|
|
):
|
|
Process(config_file),
|
|
megaFile(mega_file)
|
|
{
|
|
Check_Pointer(this);
|
|
Check_Object(config_file);
|
|
Check_Object(megaFile);
|
|
Check_Object(this);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
ArrangeMegatexturesProcess::ProcessCallback(
|
|
int error,
|
|
const char* megatexture,
|
|
const char* texture
|
|
)
|
|
{
|
|
Check_Object(this);
|
|
Check_Pointer(megatexture);
|
|
|
|
switch (error)
|
|
{
|
|
case BadPageSize:
|
|
PAUSE(("[%s] has a bad PageSize!", megatexture));
|
|
break;
|
|
|
|
case BadTextureLine:
|
|
Check_Pointer(texture);
|
|
PAUSE(("[%s] has a bad Texture= specification!", megatexture));
|
|
break;
|
|
|
|
case BadTexture:
|
|
Check_Pointer(texture);
|
|
PAUSE(("[%s]%s is a bad texture!", megatexture, texture));
|
|
break;
|
|
|
|
case NotEnoughRoom:
|
|
Check_Pointer(texture);
|
|
PAUSE(("[%s] doesn't have enough room to hold %s!", megatexture, texture));
|
|
break;
|
|
|
|
case EmptyMegatexture:
|
|
PAUSE(("[%s] is empty!", megatexture));
|
|
break;
|
|
}
|
|
}
|
|
|
|
const int Offsets[32]=
|
|
{
|
|
0, 1, 4, 5,
|
|
16, 17, 20, 21,
|
|
64, 65, 68, 69,
|
|
80, 81, 84, 85,
|
|
256, 257, 260, 261,
|
|
272, 273, 276, 277,
|
|
320, 321, 324, 325,
|
|
336, 337, 340, 341
|
|
};
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
TextureProxy::ArrangeMegatextures(ArrangeMegatexturesProcess *process)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(process);
|
|
|
|
//
|
|
//--------------------------
|
|
// Get the mega texture data
|
|
//--------------------------
|
|
//
|
|
NotationFile *mega_file = process->megaFile;
|
|
Check_Object(mega_file);
|
|
MString mega_name;
|
|
GetName(&mega_name);
|
|
Check_Object(&mega_name);
|
|
Vector2DOf<int> mega_size;
|
|
GetImageSize(&mega_size);
|
|
Verify(
|
|
mega_size.x >= 16 && mega_size.x <= 512
|
|
&& mega_size.y >= 16 && mega_size.y <= 512
|
|
&& mega_size.x == (mega_size.x & -mega_size.x)
|
|
&& mega_size.y == (mega_size.y & -mega_size.y)
|
|
&& mega_size.x == mega_size.y
|
|
);
|
|
TextureLibrary *library = GetTextureLibrary();
|
|
Check_Object(library);
|
|
|
|
//
|
|
//-------------------------------
|
|
// Create the various size arrays
|
|
//-------------------------------
|
|
//
|
|
Vector2DOf<int> cells(mega_size.x>>4, mega_size.y>>4);
|
|
DynamicArrayOf<bool> used(false, cells.x*cells.y);
|
|
|
|
//
|
|
//-------------------------------------------------
|
|
// Tell the application if the megatexture is empty
|
|
//-------------------------------------------------
|
|
//
|
|
Page *texture_page = mega_file->FindPage(mega_name);
|
|
ChainOf<Note*> *textures = NULL;
|
|
int texture_count = 0;
|
|
if (texture_page)
|
|
{
|
|
textures = texture_page->MakeNoteChain("Texture");
|
|
Register_Object(textures);
|
|
Page::NoteIterator texture_itr(textures);
|
|
texture_count = texture_itr.GetSize();
|
|
}
|
|
if (!texture_count)
|
|
{
|
|
process->ProcessCallback(
|
|
ArrangeMegatexturesProcess::EmptyMegatexture,
|
|
mega_name,
|
|
NULL
|
|
);
|
|
if (!process->continueProcess)
|
|
{
|
|
Unwind_1:
|
|
if (textures)
|
|
{
|
|
Unregister_Object(textures);
|
|
delete textures;
|
|
}
|
|
return;
|
|
#undef UNWIND
|
|
#define UNWIND() goto Unwind_1
|
|
}
|
|
}
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Now start stepping through the entries for the page and skip the page
|
|
// size entry
|
|
//----------------------------------------------------------------------
|
|
//
|
|
DynamicArrayOf<Vector2DOf<int> >
|
|
offsets(texture_count),
|
|
sizes(texture_count);
|
|
DynamicArrayOf<TextureProxy*>
|
|
proxies(texture_count);
|
|
int errors = 0;
|
|
texture_count = -1;
|
|
Page::NoteIterator texture_itr(textures);
|
|
Note *entry;
|
|
while ((entry = texture_itr.ReadAndNext()) != NULL)
|
|
{
|
|
Check_Object(entry);
|
|
++texture_count;
|
|
Verify(texture_count >= 0);
|
|
|
|
//
|
|
//--------------------------------------------
|
|
// Extract the texture data from the tron file
|
|
//--------------------------------------------
|
|
//
|
|
const char* data;
|
|
entry->GetEntry(&data);
|
|
Check_Pointer(data);
|
|
char texture_name[80];
|
|
int param_count = sscanf(data, "%s", texture_name);
|
|
if (param_count != 1 || !strlen(texture_name))
|
|
{
|
|
process->ProcessCallback(
|
|
ArrangeMegatexturesProcess::BadTextureLine,
|
|
mega_name,
|
|
texture_name
|
|
);
|
|
if (!process->continueProcess)
|
|
UNWIND();
|
|
else
|
|
{
|
|
++errors;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
//--------------------------
|
|
// Do the status check again
|
|
//--------------------------
|
|
//
|
|
process->ProcessCallback(
|
|
ArrangeMegatexturesProcess::StatusCheck,
|
|
mega_name,
|
|
texture_name
|
|
);
|
|
if (!process->continueProcess)
|
|
UNWIND();
|
|
|
|
//
|
|
//-----------------------------------------------------------------
|
|
// We now have a texture name, so get the actual texture proxy from
|
|
// the library
|
|
//-----------------------------------------------------------------
|
|
//
|
|
proxies[texture_count] = library->UseTextureProxy(texture_name);
|
|
Check_Object(proxies[texture_count]);
|
|
|
|
//
|
|
//---------------------------
|
|
// Make sure the size is good
|
|
//---------------------------
|
|
//
|
|
proxies[texture_count]->GetImageSize(&sizes[texture_count]);
|
|
if (!sizes[texture_count].x && !sizes[texture_count].y)
|
|
{
|
|
process->ProcessCallback(
|
|
ArrangeMegatexturesProcess::BadTexture,
|
|
mega_name,
|
|
texture_name
|
|
);
|
|
if (!process->continueProcess)
|
|
{
|
|
Unwind_2:
|
|
for (int i=0; i<=texture_count; ++i)
|
|
proxies[i]->DetachReference();
|
|
UNWIND();
|
|
#undef UNWIND
|
|
#define UNWIND() goto Unwind_2
|
|
}
|
|
else
|
|
{
|
|
++errors;
|
|
continue;
|
|
}
|
|
}
|
|
Verify(
|
|
sizes[texture_count].x >= 16 && sizes[texture_count].x <= 512
|
|
&& sizes[texture_count].y >= 16 && sizes[texture_count].y <= 512
|
|
&& sizes[texture_count].x == (sizes[texture_count].x & -sizes[texture_count].x)
|
|
&& sizes[texture_count].y == (sizes[texture_count].y & -sizes[texture_count].y)
|
|
);
|
|
|
|
//
|
|
//------------------------------------------------------------------------
|
|
// Find the parameters for the search, which is based upon an interleaving
|
|
// scheme using a bit-wise mix of the x and y index values
|
|
//------------------------------------------------------------------------
|
|
//
|
|
Vector2DOf<int>
|
|
units(sizes[texture_count].x>>4, sizes[texture_count].y>>4);
|
|
Verify(units.x>0 && units.y>0);
|
|
int
|
|
increment,
|
|
run,
|
|
scale;
|
|
if (units.x < units.y)
|
|
{
|
|
run = units.y / units.x;
|
|
scale = units.x*units.x;
|
|
increment = scale * run*run;
|
|
}
|
|
else
|
|
{
|
|
run = units.x / units.y;
|
|
scale = units.y*units.y;
|
|
increment = scale * run*run;
|
|
scale *= 2;
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// We search the array associated with the step, looking for the first
|
|
// empty slot that matches
|
|
//--------------------------------------------------------------------
|
|
//
|
|
int index;
|
|
int i;
|
|
for (index=0; index<used.GetLength(); index += increment)
|
|
{
|
|
for (i=0; i<run; ++i)
|
|
{
|
|
int sub_index = scale*Offsets[i];
|
|
if (!used[index + sub_index])
|
|
{
|
|
index += sub_index;
|
|
break;
|
|
}
|
|
}
|
|
if (i!=run)
|
|
break;
|
|
}
|
|
|
|
//
|
|
//-----------------------------------------------------
|
|
// If we don't find any empty slots, do the error thing
|
|
//-----------------------------------------------------
|
|
//
|
|
if (index == used.GetLength())
|
|
{
|
|
process->ProcessCallback(
|
|
ArrangeMegatexturesProcess::NotEnoughRoom,
|
|
mega_name,
|
|
texture_name
|
|
);
|
|
if (!process->continueProcess)
|
|
UNWIND();
|
|
else
|
|
{
|
|
++errors;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
//--------------------
|
|
// Mark the used cells
|
|
//--------------------
|
|
//
|
|
int x,y;
|
|
for (y=0; y<units.y; ++y)
|
|
for (x=0; x<units.x; ++x)
|
|
{
|
|
Verify(!used[index + Offsets[y]*2 + Offsets[x]]);
|
|
used[index + Offsets[y]*2 + Offsets[x]] = true;
|
|
}
|
|
|
|
//
|
|
//----------------------------------------
|
|
// Extract the x,y location from the index
|
|
//----------------------------------------
|
|
//
|
|
int mask=1;
|
|
x = y = 0;
|
|
while (index)
|
|
{
|
|
if (index&1)
|
|
x |= mask;
|
|
index >>= 1;
|
|
if (index&1)
|
|
y |= mask;
|
|
index >>= 1;
|
|
mask <<= 1;
|
|
}
|
|
offsets[texture_count].x = x<<4;
|
|
offsets[texture_count].y = y<<4;
|
|
}
|
|
#undef UNWIND
|
|
#define UNWIND() goto Unwind_1
|
|
|
|
//
|
|
//------------------------------------------------------------------------
|
|
// If we don't have any errors, go ahead and save our data back out to the
|
|
// notation file. We first will have to erase the old texture entries
|
|
//------------------------------------------------------------------------
|
|
//
|
|
if (!errors)
|
|
{
|
|
Note* dummy;
|
|
texture_itr.First();
|
|
while ((dummy = texture_itr.ReadAndNext()) != NULL)
|
|
texture_page->DeleteNote(dummy->GetName());
|
|
int i;
|
|
for (i=0; i<=texture_count; ++i)
|
|
{
|
|
char buffer[80];
|
|
MString texture_name;
|
|
Check_Object(proxies[i]);
|
|
proxies[i]->GetName(&texture_name);
|
|
sprintf(
|
|
buffer,
|
|
"%s %d %d %d %d",
|
|
static_cast<const char*>(texture_name),
|
|
offsets[i].x,
|
|
offsets[i].y,
|
|
sizes[i].x,
|
|
sizes[i].y
|
|
);
|
|
texture_page->AppendEntry("Texture", buffer);
|
|
}
|
|
|
|
//
|
|
//-----------------------------------------------
|
|
// Now tell the debug stream about the space left
|
|
//-----------------------------------------------
|
|
//
|
|
DynamicArrayOf<int> room(5);
|
|
int total=0;
|
|
for (i=0; i<5; ++i)
|
|
{
|
|
total <<= 2;
|
|
int shift = 8 - 2*i;
|
|
int range = 4 << (2*i);
|
|
int count=0;
|
|
for (int j=0; j<range; ++j)
|
|
if (!used[j<<shift])
|
|
++count;
|
|
room[i] = count - total;
|
|
total = count;
|
|
}
|
|
if (total)
|
|
{
|
|
SPEW(("ArrangeMegatextures", "\n[%s] has room for:", mega_name));
|
|
for (i=0; i<5; ++i)
|
|
{
|
|
if (room[i])
|
|
SPEW((
|
|
"ArrangeMegatextures",
|
|
"\t%d %dx%d textures",
|
|
room[i],
|
|
256 >> i,
|
|
256 >> i
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
//---------
|
|
// Clean up
|
|
//---------
|
|
//
|
|
for (int i=0; i<=texture_count; ++i)
|
|
proxies[i]->DetachReference();
|
|
UNWIND();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
TextureLibrary::ArrangeMegatextures(ArrangeMegatexturesProcess *process)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(process);
|
|
|
|
//
|
|
//------------------------------------------------------------------------
|
|
// Get the page list from the mega file and process each page after making
|
|
// sure the app says it's OK
|
|
//------------------------------------------------------------------------
|
|
//
|
|
NotationFile *mega_file = process->megaFile;
|
|
Check_Object(mega_file);
|
|
NotationFile::PageIterator *pages = mega_file->MakePageIterator();
|
|
Register_Object(pages);
|
|
Page *page;
|
|
while ((page = pages->ReadAndNext()) != NULL)
|
|
{
|
|
const char* mega_name = page->GetName();
|
|
Check_Pointer(mega_name);
|
|
process->ProcessCallback(
|
|
ArrangeMegatexturesProcess::StatusCheck,
|
|
mega_name,
|
|
NULL
|
|
);
|
|
if (!process->continueProcess)
|
|
break;
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// For each page, get a list of all the entries. If the page is missing
|
|
// its PageSize page, it is a final uncoalesced texture, so skip it
|
|
//----------------------------------------------------------------------
|
|
//
|
|
const char* size_string;
|
|
if (!page->GetEntry("PageSize", &size_string))
|
|
continue;
|
|
|
|
//
|
|
//------------------------------
|
|
// Make sure the page size is OK
|
|
//------------------------------
|
|
//
|
|
Vector2DOf<int> mega_size;
|
|
#if defined(_ARMOR)
|
|
int param_count =
|
|
#endif
|
|
sscanf(size_string, "%d %d", &mega_size.x, &mega_size.y);
|
|
Verify(param_count);
|
|
if (
|
|
mega_size.x < 16 || mega_size.x > 512
|
|
|| mega_size.y < 16 || mega_size.y > 512
|
|
|| mega_size.x != (mega_size.x & -mega_size.x)
|
|
|| mega_size.y != (mega_size.y & -mega_size.y)
|
|
)
|
|
{
|
|
process->ProcessCallback(
|
|
ArrangeMegatexturesProcess::BadPageSize,
|
|
mega_name,
|
|
NULL
|
|
);
|
|
if (!process->continueProcess)
|
|
break;
|
|
else
|
|
continue;
|
|
}
|
|
|
|
//
|
|
//---------------------------------------------------
|
|
// Create the megatexture proxy run the process on it
|
|
//---------------------------------------------------
|
|
//
|
|
TextureProxy *mega_texture =
|
|
TextureProxy::MakeProxy(mega_name, mega_size, this);
|
|
Register_Object(mega_texture);
|
|
mega_texture->ArrangeMegatextures(process);
|
|
mega_texture->DetachReference();
|
|
|
|
//
|
|
//---------
|
|
// Clean up
|
|
//---------
|
|
//
|
|
if (!process->continueProcess)
|
|
break;
|
|
}
|
|
|
|
//
|
|
//-----------------------
|
|
// Clean up the page list
|
|
//-----------------------
|
|
//
|
|
Unregister_Object(pages);
|
|
delete pages;
|
|
}
|