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.
409 lines
12 KiB
C++
409 lines
12 KiB
C++
#include "ProxyHeaders.hpp"
|
|
typedef int (*LPERROR_CALLBACKFN)(char *,bool);
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
CoalesceTexturesProcess::CoalesceTexturesProcess(
|
|
Stuff::NotationFile *megatexture_file,
|
|
const char *megatexture_list,
|
|
bool bSuppress,
|
|
void* fcn
|
|
) :
|
|
Process(NULL,bSuppress,fcn),
|
|
megatextureFile(megatexture_file)
|
|
{
|
|
if (megatexture_list)
|
|
{
|
|
megatextureList = MakeMegatextureChain(megatexture_list);
|
|
Register_Object(megatextureList);
|
|
Check_Object(megatextureFile);
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
CoalesceTexturesProcess::CoalesceTexturesProcess(
|
|
Stuff::NotationFile *megatexture_file,
|
|
const char *megatexture_list,
|
|
Stuff::NotationFile *config_file,
|
|
bool bSuppress,
|
|
void* fcn
|
|
):
|
|
Process(config_file,bSuppress,fcn),
|
|
megatextureFile(megatexture_file)
|
|
{
|
|
if (megatexture_list)
|
|
{
|
|
megatextureList = MakeMegatextureChain(megatexture_list);
|
|
Register_Object(megatextureList);
|
|
Check_Object(megatextureFile);
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
CoalesceTexturesProcess::~CoalesceTexturesProcess()
|
|
{
|
|
if (megatextureList)
|
|
{
|
|
MStringChainIterator iterator(megatextureList);
|
|
iterator.DeletePlugs();
|
|
Unregister_Object(megatextureList);
|
|
delete megatextureList;
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
MStringChain*
|
|
CoalesceTexturesProcess::MakeMegatextureChain(const char* list)
|
|
{
|
|
Check_Pointer(list);
|
|
|
|
//
|
|
//--------------------------------------------
|
|
// If the string is empty, don't make anything
|
|
//--------------------------------------------
|
|
//
|
|
MStringChain *chain = NULL;
|
|
const char* begin = list;
|
|
do
|
|
{
|
|
//
|
|
//-------------------------------------------------------------------
|
|
// If we can't find a comma, the remainder of the string is the token
|
|
//-------------------------------------------------------------------
|
|
//
|
|
const char* end = strchr(begin, ',');
|
|
MString token = begin;
|
|
if (!end)
|
|
begin = NULL;
|
|
|
|
//
|
|
//------------------------------------------------------
|
|
// Otherwise, clip the token to where we found the comma
|
|
//------------------------------------------------------
|
|
//
|
|
else
|
|
{
|
|
int len = end - begin;
|
|
token[len] = '\0';
|
|
begin = end;
|
|
while (*begin == ',')
|
|
++begin;
|
|
if (!*begin)
|
|
begin = NULL;
|
|
}
|
|
|
|
//
|
|
//----------------------------
|
|
// Put this token in the chain
|
|
//----------------------------
|
|
//
|
|
if (!chain)
|
|
chain = new MStringChain(NULL);
|
|
PlugOf<MString> *plug = new PlugOf<MString>(token);
|
|
Register_Object(plug);
|
|
chain->Add(plug);
|
|
|
|
//
|
|
//----------------------------------
|
|
// Skip to the next token until done
|
|
//----------------------------------
|
|
//
|
|
} while (begin);
|
|
return chain;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
PolygonMeshProxy::CoalesceTextures(CoalesceTexturesProcess *process)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(process);
|
|
|
|
//
|
|
//------------------------------------------------------------
|
|
// Make sure that the process says its OK to check the texture
|
|
//------------------------------------------------------------
|
|
//
|
|
process->CoalesceTexturesCallback(this);
|
|
if (!process->continueProcess)
|
|
return;
|
|
|
|
//
|
|
//--------------------------------------
|
|
// See if this will be a trivial process
|
|
//--------------------------------------
|
|
//
|
|
if (!process->megatextureList)
|
|
return;
|
|
Check_Object(process->megatextureFile);
|
|
Check_Object(process->megatextureFile);
|
|
|
|
//
|
|
//------------------------------------------------------------------------
|
|
// Get the state arrays for this mesh and make sure that they have already
|
|
// been split
|
|
//------------------------------------------------------------------------
|
|
//
|
|
DynamicArrayOf<PolygonProxy*> polygons;
|
|
unsigned polygon_count = UsePolygonArray(&polygons);
|
|
if (!polygon_count)
|
|
return;
|
|
DynamicArrayOf<MultiState*> states;
|
|
DynamicArrayOf<unsigned> index;
|
|
DynamicArrayOf<unsigned> count;
|
|
#if defined(_ARMOR)
|
|
unsigned unique_states =
|
|
#endif
|
|
UseMultiStateArray(&states, &index, &count, polygons);
|
|
Verify(unique_states==1 && count[0]==polygon_count);
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// If this mesh is not textured, we can just return. Otherwise, get the
|
|
// texture name
|
|
//----------------------------------------------------------------------
|
|
//
|
|
Check_Object(states[0]);
|
|
unsigned state_count = states[0]->GetLength();
|
|
|
|
for(unsigned state_loop=0;state_loop<state_count;++state_loop)
|
|
{
|
|
TextureProxy *texture = (*states[0])[state_loop]->UseTextureProxy();
|
|
if (!texture)
|
|
{
|
|
Unwind_1:
|
|
DetachArrayReferences(&states);
|
|
DetachArrayReferences(&polygons);
|
|
return;
|
|
#undef UNWIND
|
|
#define UNWIND() goto Unwind_1
|
|
}
|
|
|
|
MString name;
|
|
texture->GetName(&name);
|
|
Vector2DOf<int> texture_size;
|
|
texture->GetImageSize(&texture_size);
|
|
texture->DetachReference();
|
|
|
|
//
|
|
//------------------------------------------------------------------------
|
|
// Look through each of the listed megatextures to try and find where this
|
|
// texture should be rerouted
|
|
//------------------------------------------------------------------------
|
|
//
|
|
MStringChainIterator megatextures(process->megatextureList);
|
|
PlugOf<MString> *string;
|
|
Vector2DOf<Scalar> offset;
|
|
offset.x = offset.y = 0.0f;
|
|
const char* mega_name = NULL;
|
|
while ((string = megatextures.ReadAndNext()) != NULL)
|
|
{
|
|
mega_name = string->GetItem();
|
|
Check_Pointer(mega_name);
|
|
const char *offset_string;
|
|
Page *page = process->megatextureFile->FindPage(mega_name);
|
|
if (page)
|
|
{
|
|
if (page->GetEntry(name, &offset_string))
|
|
{
|
|
Check_Pointer(offset_string);
|
|
#if defined(_ARMOR)
|
|
int count =
|
|
#endif
|
|
sscanf(offset_string, "%f %f", &offset.x, &offset.y);
|
|
Verify(count == 2);
|
|
break;
|
|
}
|
|
}
|
|
// Look for alias
|
|
page = process->megatextureFile->FindPage(name);
|
|
if (page)
|
|
{
|
|
const char *alias_name;
|
|
if (page->GetEntry("alias",&alias_name))
|
|
{
|
|
page = process->megatextureFile->FindPage(alias_name);
|
|
if (page)
|
|
{
|
|
if (page->GetEntry(name, &offset_string))
|
|
{
|
|
Check_Pointer(offset_string);
|
|
#if defined(_ARMOR)
|
|
int count =
|
|
#endif
|
|
sscanf(offset_string, "%f %f", &offset.x, &offset.y);
|
|
Verify(count == 2);
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
// If no megatexture has this texture in its entries, this texture should
|
|
// be left alone
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
if (!string)
|
|
UNWIND();
|
|
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
// Now, get the megatexture from the library. If it isn't already there,
|
|
// an empty one will be created
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
SceneProxy *scene = GetSceneProxy();
|
|
Check_Object(scene);
|
|
StateLibrary *state_library = scene->GetStateLibrary();
|
|
Check_Object(state_library);
|
|
TextureLibrary *textures = state_library->GetTextureLibrary();
|
|
Check_Object(textures);
|
|
TextureProxy *mega_texture = textures->UseTextureProxy(mega_name);
|
|
|
|
//
|
|
//----------------------------------------
|
|
// Read the size from the megatexture page
|
|
//----------------------------------------
|
|
//
|
|
const char* size_string;
|
|
Page *page = process->megatextureFile->GetPage(mega_name);
|
|
Check_Object(page);
|
|
page->GetEntry("PageSize", &size_string);
|
|
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 == 2);
|
|
|
|
//
|
|
//---------------------------------------------
|
|
// Compute the new scale and offset for our uvs
|
|
//---------------------------------------------
|
|
//
|
|
Vector2DOf<Scalar>
|
|
pixel_size(1.0f/mega_size.x, 1.0f/mega_size.y),
|
|
pixel_count(
|
|
static_cast<Scalar>(texture_size.x),
|
|
static_cast<Scalar>(texture_size.y)
|
|
);
|
|
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// Get the array of vertices, then go through each one and set the UVs
|
|
// according to the coalesced values
|
|
//--------------------------------------------------------------------
|
|
//
|
|
DynamicArrayOf<VertexProxy*> vertices;
|
|
unsigned vertex_count = UseVertexArray(&vertices);
|
|
for (unsigned i=0; i<vertex_count; ++i)
|
|
{
|
|
Check_Object(vertices[i]);
|
|
DynamicArrayOf<Vector2DOf<Scalar> > uv;
|
|
|
|
vertices[i]->GetUVs(&uv);
|
|
uv[state_loop].x = (uv[state_loop].x*pixel_count.x + offset.x) * pixel_size.x;
|
|
uv[state_loop].y = (uv[state_loop].y*pixel_count.y + offset.y) * pixel_size.y;
|
|
vertices[i]->SetUVs(uv);
|
|
|
|
// If uv are wrapped for more than 4 pixels
|
|
if (uv[state_loop].x < -4.0/texture_size.x || uv[state_loop].x > 1+4.0/texture_size.x ||
|
|
uv[state_loop].y < -4.0/texture_size.y || uv[state_loop].y > 1+4.0/texture_size.y )
|
|
{
|
|
if (process->errorfn)
|
|
{
|
|
char buffer[200];
|
|
sprintf(buffer, "CoalesceTextures: %s has UV's wrapped (%f %f)!", (char*)name, uv[state_loop].x, uv[state_loop].y);
|
|
LPERROR_CALLBACKFN fcn = (LPERROR_CALLBACKFN)process->errorfn;
|
|
fcn(buffer,process->suppress);
|
|
}
|
|
}
|
|
}
|
|
DetachArrayReferences(&vertices);
|
|
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// Now change the states of the polygons so that they point at the new
|
|
// megatextures
|
|
//--------------------------------------------------------------------
|
|
//
|
|
(*states[0])[state_loop]->SetToMatchTextureProxy(mega_texture);
|
|
|
|
//
|
|
//---------
|
|
// Clean up
|
|
//---------
|
|
//
|
|
mega_texture->DetachReference();
|
|
DetachArrayReferences(&polygons);
|
|
}
|
|
SetToMatchMultiState(states[0]);
|
|
DetachArrayReferences(&states);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
SceneProxy::CoalesceTextures(CoalesceTexturesProcess *process)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(process);
|
|
|
|
//
|
|
//---------------------------------------
|
|
// Make sure that the process says its OK
|
|
//---------------------------------------
|
|
//
|
|
process->CoalesceTexturesCallback(this);
|
|
if (!process->continueProcess)
|
|
return;
|
|
|
|
//
|
|
//--------------------------------------
|
|
// See if this will be a trivial process
|
|
//--------------------------------------
|
|
//
|
|
if (!process->megatextureList)
|
|
return;
|
|
|
|
//
|
|
//------------------------------------------------------------------
|
|
// Coalesce the children. At this point, they must all be flattened
|
|
//------------------------------------------------------------------
|
|
//
|
|
unsigned child_count = GetChildCount();
|
|
ChildProxy *child = UseFirstChildProxy();
|
|
for (unsigned i=0; i<child_count; ++i)
|
|
{
|
|
Check_Object(child);
|
|
ChildProxy *next = child->UseNextSiblingProxy();
|
|
PolygonMeshProxy *mesh = Cast_Object(PolygonMeshProxy*, child);
|
|
mesh->CoalesceTextures(process);
|
|
child->DetachReference();
|
|
child = next;
|
|
if (!process->continueProcess)
|
|
{
|
|
if (child)
|
|
child->DetachReference();
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
//-------------------------------------------
|
|
// Make sure to discard any remaining proxies
|
|
//-------------------------------------------
|
|
//
|
|
if (child)
|
|
child->DetachReference();
|
|
}
|