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.
563 lines
13 KiB
C++
563 lines
13 KiB
C++
#include "ProxyHeaders.hpp"
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
BuildMegatexturesProcess::BuildMegatexturesProcess(NotationFile *mega_file):
|
|
megaFile(mega_file)
|
|
{
|
|
Check_Pointer(this);
|
|
Check_Object(megaFile);
|
|
Check_Object(this);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
BuildMegatexturesProcess::BuildMegatexturesProcess(
|
|
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
|
|
BuildMegatexturesProcess::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 SizesDontMatch:
|
|
Check_Pointer(texture);
|
|
PAUSE(("[%s]%s does not match its stated size!", megatexture, texture));
|
|
break;
|
|
|
|
case BadTexture:
|
|
Check_Pointer(texture);
|
|
PAUSE(("[%s]%s is a bad texture!", megatexture, texture));
|
|
break;
|
|
|
|
case TextureDoesntFit:
|
|
Check_Pointer(texture);
|
|
PAUSE(("[%s]%s doesn't fit!", megatexture, texture));
|
|
break;
|
|
|
|
case TextureOverlap:
|
|
Check_Pointer(texture);
|
|
PAUSE(("[%s]%s overlaps a previous texture!", megatexture, texture));
|
|
break;
|
|
|
|
case EmptyMegatexture:
|
|
PAUSE(("[%s] is empty!", megatexture));
|
|
break;
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
TextureProxy::BuildMegatextures(BuildMegatexturesProcess *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)
|
|
);
|
|
TextureLibrary *library = GetTextureLibrary();
|
|
Check_Object(library);
|
|
|
|
//
|
|
//-----------------------------------------------------------------
|
|
// Create the megatexture proxy and clear it out to invisible black
|
|
//-----------------------------------------------------------------
|
|
//
|
|
unsigned channel_size = mega_size.x*mega_size.y;
|
|
DynamicArrayOf<BYTE>
|
|
red(channel_size),
|
|
green(channel_size),
|
|
blue(channel_size),
|
|
alpha(channel_size);
|
|
memset(red.GetData(), 0, channel_size);
|
|
memset(green.GetData(), 0, channel_size);
|
|
memset(blue.GetData(), 0, channel_size);
|
|
memset(alpha.GetData(), 0, channel_size);
|
|
DynamicArrayOf<bool>
|
|
used(false, channel_size);
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Now start stepping through the entries for the page and skip the page
|
|
// size entry
|
|
//----------------------------------------------------------------------
|
|
//
|
|
bool alpha_used = false;
|
|
int texture_count = 0;
|
|
ChainOf<Note*> *textures = NULL;
|
|
Note *entry = NULL;
|
|
unsigned errors = 0;
|
|
|
|
Page *mega_page = mega_file->FindPage(mega_name);
|
|
if (mega_page)
|
|
{
|
|
textures = mega_page->MakeNoteChain("Texture");
|
|
Check_Object(textures);
|
|
Page::NoteIterator texture_itr(textures);
|
|
while ((entry = texture_itr.ReadAndNext()) != NULL)
|
|
{
|
|
Check_Object(entry);
|
|
|
|
//
|
|
//--------------------------------------------
|
|
// Extract the texture data from the tron file
|
|
//--------------------------------------------
|
|
//
|
|
const char* data;
|
|
entry->GetEntry(&data);
|
|
Check_Pointer(data);
|
|
Vector2DOf<int> offset, size(0,0);
|
|
char texture_name[80];
|
|
int param_count =
|
|
sscanf(
|
|
data,
|
|
"%s %d %d %d %d",
|
|
texture_name,
|
|
&offset.x,
|
|
&offset.y,
|
|
&size.x,
|
|
&size.y
|
|
);
|
|
if (param_count != 5 || !strlen(texture_name))
|
|
{
|
|
process->ProcessCallback(
|
|
BuildMegatexturesProcess::BadTextureLine,
|
|
mega_name,
|
|
texture_name
|
|
);
|
|
if (!process->continueProcess)
|
|
{
|
|
Unwind_1:
|
|
Check_Object(textures);
|
|
delete textures;
|
|
return;
|
|
#undef UNWIND
|
|
#define UNWIND() goto Unwind_1
|
|
}
|
|
else
|
|
{
|
|
++errors;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
//--------------------------
|
|
// Do the status check again
|
|
//--------------------------
|
|
//
|
|
process->ProcessCallback(
|
|
BuildMegatexturesProcess::StatusCheck,
|
|
mega_name,
|
|
texture_name
|
|
);
|
|
if (!process->continueProcess)
|
|
UNWIND();
|
|
|
|
//
|
|
//-----------------------------------------------------------------
|
|
// We now have a texture name, so get the actual texture proxy from
|
|
// the library
|
|
//-----------------------------------------------------------------
|
|
//
|
|
TextureProxy *texture = library->UseTextureProxy(texture_name);
|
|
Check_Object(texture);
|
|
|
|
//
|
|
//---------------------------
|
|
// Make sure the size is good
|
|
//---------------------------
|
|
//
|
|
Vector2DOf<int> texture_size;
|
|
texture->GetImageSize(&texture_size);
|
|
if (!texture_size.x && !texture_size.y)
|
|
{
|
|
process->ProcessCallback(
|
|
BuildMegatexturesProcess::BadTexture,
|
|
mega_name,
|
|
texture_name
|
|
);
|
|
if (!process->continueProcess)
|
|
{
|
|
Unwind_2:
|
|
texture->DetachReference();
|
|
UNWIND();
|
|
#undef UNWIND
|
|
#define UNWIND() goto Unwind_2
|
|
}
|
|
else
|
|
{
|
|
texture->DetachReference();
|
|
++errors;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
//-----------------------------------------------
|
|
// Make sure the size matches what is in the file
|
|
//-----------------------------------------------
|
|
//
|
|
if (size != texture_size)
|
|
{
|
|
process->ProcessCallback(
|
|
BuildMegatexturesProcess::SizesDontMatch,
|
|
mega_name,
|
|
texture_name
|
|
);
|
|
if (!process->continueProcess)
|
|
UNWIND();
|
|
else
|
|
{
|
|
texture->DetachReference();
|
|
++errors;
|
|
continue;
|
|
}
|
|
}
|
|
++texture_count;
|
|
|
|
//
|
|
//--------------------------------------------------------------
|
|
// Figure out where this texture wishes to be in the megatexture
|
|
//--------------------------------------------------------------
|
|
//
|
|
if (
|
|
offset.x < 0 || offset.x+size.x > mega_size.x
|
|
|| offset.y < 0 || offset.y+size.y > mega_size.y
|
|
)
|
|
{
|
|
process->ProcessCallback(
|
|
BuildMegatexturesProcess::TextureDoesntFit,
|
|
mega_name,
|
|
texture_name
|
|
);
|
|
if (!process->continueProcess)
|
|
UNWIND();
|
|
else
|
|
{
|
|
++errors;
|
|
texture->DetachReference();
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
//-----------------------
|
|
// Get the texture depths
|
|
//-----------------------
|
|
//
|
|
unsigned
|
|
red_depth,
|
|
green_depth,
|
|
blue_depth,
|
|
alpha_depth;
|
|
texture->GetChannelDepth(
|
|
&red_depth,
|
|
&green_depth,
|
|
&blue_depth,
|
|
&alpha_depth
|
|
);
|
|
if (alpha_depth)
|
|
alpha_used = true;
|
|
|
|
//
|
|
//---------------------
|
|
// Get the texture data
|
|
//---------------------
|
|
//
|
|
DynamicArrayOf<BYTE>
|
|
r,
|
|
g,
|
|
b,
|
|
a;
|
|
|
|
if (alpha_depth)
|
|
texture->GetChannels(&r, &g, &b, &a);
|
|
else
|
|
{
|
|
texture->GetChannels(&r, &g, &b, NULL);
|
|
alpha_depth = 0;
|
|
}
|
|
|
|
//
|
|
//-------------------------
|
|
// Copy the data row by row
|
|
//-------------------------
|
|
//
|
|
Vector2DOf<int> source(0,0);
|
|
for (source.y=0; source.y<size.y; ++source.y)
|
|
{
|
|
Vector2DOf<int> dest;
|
|
dest.Add(offset, source);
|
|
unsigned source_pixel = texture->GetPixelIndex(source);
|
|
unsigned dest_pixel = GetPixelIndex(dest);
|
|
|
|
//
|
|
//--------------------
|
|
// Look for an overlap
|
|
//--------------------
|
|
//
|
|
void *overlap = memchr(&used[dest_pixel], true, size.x);
|
|
if (overlap)
|
|
{
|
|
process->ProcessCallback(
|
|
BuildMegatexturesProcess::TextureOverlap,
|
|
mega_name,
|
|
texture_name
|
|
);
|
|
if (!process->continueProcess)
|
|
UNWIND();
|
|
else
|
|
{
|
|
++errors;
|
|
source.y = size.y;
|
|
continue;
|
|
}
|
|
}
|
|
memset(&used[dest_pixel], true, size.x);
|
|
|
|
//
|
|
//---------------------
|
|
// Copy the row of data
|
|
//---------------------
|
|
//
|
|
Mem_Copy(
|
|
&red[dest_pixel],
|
|
&r[source_pixel],
|
|
size.x,
|
|
red.GetLength() - dest_pixel
|
|
);
|
|
Mem_Copy(
|
|
&green[dest_pixel],
|
|
&g[source_pixel],
|
|
size.x,
|
|
green.GetLength() - dest_pixel
|
|
);
|
|
Mem_Copy(
|
|
&blue[dest_pixel],
|
|
&b[source_pixel],
|
|
size.x,
|
|
blue.GetLength() - dest_pixel
|
|
);
|
|
|
|
//
|
|
//----------------------------------------------------------------
|
|
// Copy the alpha data if enabled, otherwise just set it to opaque
|
|
//----------------------------------------------------------------
|
|
//
|
|
if (alpha_depth)
|
|
{
|
|
Mem_Copy(
|
|
&alpha[dest_pixel],
|
|
&a[source_pixel],
|
|
size.x,
|
|
alpha.GetLength() - dest_pixel
|
|
);
|
|
}
|
|
else
|
|
memset(&alpha[dest_pixel], static_cast<BYTE>(255), size.x);
|
|
}
|
|
|
|
//
|
|
//---------------------
|
|
// Clean up the texture
|
|
//---------------------
|
|
//
|
|
texture->DetachReference();
|
|
}
|
|
}
|
|
|
|
//
|
|
//-------------------------------------------------
|
|
// Tell the application if the megatexture is empty
|
|
//-------------------------------------------------
|
|
//
|
|
if (!texture_count)
|
|
{
|
|
process->ProcessCallback(
|
|
BuildMegatexturesProcess::EmptyMegatexture,
|
|
mega_name,
|
|
NULL
|
|
);
|
|
if (!process->continueProcess)
|
|
{
|
|
Check_Object(textures);
|
|
delete textures;
|
|
}
|
|
}
|
|
|
|
//
|
|
//---------------------------------------------------------------------
|
|
// If we didn't have any errors and aren't empty, copy the texture into
|
|
// the texture library
|
|
//---------------------------------------------------------------------
|
|
//
|
|
else if (!errors)
|
|
{
|
|
SetChannels(
|
|
&red,
|
|
&green,
|
|
&blue,
|
|
(alpha_used) ? &alpha : NULL
|
|
);
|
|
TextureProxy *proxy = library->UseMatchingTextureProxy(this);
|
|
Check_Object(proxy);
|
|
Verify(IsEqualTo(proxy));
|
|
proxy->DetachReference();
|
|
}
|
|
|
|
//
|
|
//---------
|
|
// Clean up
|
|
//---------
|
|
//
|
|
Check_Object(textures);
|
|
delete textures;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
TextureLibrary::BuildMegatextures(BuildMegatexturesProcess *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();
|
|
Check_Object(pages);
|
|
Page *page;
|
|
while ((page = pages->ReadAndNext()) != NULL)
|
|
{
|
|
Check_Object(page);
|
|
const char* mega_name = page->GetName();
|
|
Check_Pointer(mega_name);
|
|
process->ProcessCallback(
|
|
BuildMegatexturesProcess::StatusCheck,
|
|
mega_name,
|
|
NULL
|
|
);
|
|
if (!process->continueProcess)
|
|
{
|
|
Unwind_1:
|
|
Check_Object(pages);
|
|
delete pages;
|
|
return;
|
|
#undef UNWIND
|
|
#define UNWIND() goto Unwind_1
|
|
}
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// 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(
|
|
BuildMegatexturesProcess::BadPageSize,
|
|
mega_name,
|
|
NULL
|
|
);
|
|
if (!process->continueProcess)
|
|
UNWIND();
|
|
else
|
|
continue;
|
|
}
|
|
|
|
//
|
|
//---------------------------------------------------
|
|
// Create the megatexture proxy run the process on it
|
|
//---------------------------------------------------
|
|
//
|
|
TextureProxy *mega_texture =
|
|
TextureProxy::MakeProxy(mega_name, mega_size, this);
|
|
Check_Object(mega_texture);
|
|
mega_texture->BuildMegatextures(process);
|
|
mega_texture->DetachReference();
|
|
if (!process->continueProcess)
|
|
UNWIND();
|
|
}
|
|
|
|
//
|
|
//-----------------------
|
|
// Clean up the page list
|
|
//-----------------------
|
|
//
|
|
Check_Object(pages);
|
|
delete pages;
|
|
}
|