495 lines
21 KiB
C++
495 lines
21 KiB
C++
/*===========================================================================
|
|
File: main.cpp
|
|
|
|
This tool turns a greyscale image into a raw look up table.
|
|
|
|
Copyright (c) Radical Entertainment, Inc. All rights reserved.
|
|
|
|
===========================================================================*/
|
|
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
#include <toollib.hpp>
|
|
#include <dospath.hpp>
|
|
#include <tlFrameControllerChunk16.hpp>
|
|
#include <tlAnimationChunk.hpp>
|
|
#include <atenum.hpp>
|
|
#include <chunkids.hpp>
|
|
|
|
#include "Param.hpp"
|
|
#include <tlSetChunk.hpp>
|
|
|
|
Parameters* Param;
|
|
char outFile[P3DMAXNAME];
|
|
|
|
struct TextureData
|
|
{
|
|
TextureData() :
|
|
m_TextureID( 0 ),
|
|
m_NumIDs( 0 ),
|
|
m_ShaderID( 0 ),
|
|
m_AnimationChunk( 0 ),
|
|
m_ControllerChunk( 0 ),
|
|
// m_MultiControllerChunk( 0 ),
|
|
m_pSetChunk( 0 ),
|
|
m_WritenOut( false ) {};
|
|
~TextureData() { delete m_TextureID; } // Set chunk is deleted by the output chunk.
|
|
TLUID* m_TextureID; // List of texture chunks to put into set chunk.
|
|
int m_NumIDs; // Number in list. If number is negative then just the animation/controller chunk are removed and multicontroller modified.
|
|
TLUID m_ShaderID; // The shader this data is for.
|
|
TLUID m_AnimationChunk; // Texture animation chunk to remove.
|
|
TLUID m_ControllerChunk; // Texture frame controller chunk to remove.
|
|
// TLUID m_MultiControllerChunk; // MultiController to remove texture frame controller from.
|
|
tlSetChunk* m_pSetChunk; // Set chunk which will be written to file.
|
|
bool m_WritenOut; // Has the set chunk been written out?
|
|
};
|
|
|
|
static const int TEXTURE_COLLECTION_SIZE = 256;
|
|
static TextureData* g_TextureCollection[ TEXTURE_COLLECTION_SIZE ];
|
|
static int g_TextureCollectionCount = 0;
|
|
|
|
int main(int argc, char* argv[])
|
|
{
|
|
Param = new Parameters(argc,argv);
|
|
bool sameFile = false; // Have we switched to a new output file.
|
|
|
|
tlDataChunk::RegisterDefaultChunks();
|
|
|
|
tlFile* output = 0;
|
|
|
|
for( int i = 0; i < TEXTURE_COLLECTION_SIZE; ++i )
|
|
{
|
|
g_TextureCollection[ i ] = 0;
|
|
}
|
|
|
|
if( Param->Files.Count() < 1 )
|
|
{
|
|
printf( "Must specify at least one input file.\n" );
|
|
exit( -1 );
|
|
}
|
|
if( Param->ShaderName == 0 )
|
|
{
|
|
printf( "Must specify the name of the shader with texture animation.\n" );
|
|
exit( -1 );
|
|
}
|
|
|
|
bool wildCardShader = false;
|
|
int shaderNameLen = strlen( Param->ShaderName );
|
|
if( Param->ShaderName[ shaderNameLen - 1 ] == '*' )
|
|
{
|
|
wildCardShader = true;
|
|
--shaderNameLen;
|
|
Param->ShaderName[ shaderNameLen ] = '\0';
|
|
}
|
|
|
|
// for each file on the command-line, do the following:
|
|
for(int curFile = 0; curFile < Param->Files.Count(); curFile++)
|
|
{
|
|
tlFile input(new tlFileByteStream(Param->Files[ curFile ], omREAD), tlFile::FROMFILE);
|
|
if(!input.IsOpen())
|
|
{
|
|
printf("Could not open %s\n", Param->Files[ curFile ]);
|
|
exit( -1 );
|
|
}
|
|
tlDataChunk* inChunk = new tlDataChunk( &input );
|
|
|
|
char outFileName[256];
|
|
if( output == 0 )
|
|
{
|
|
if( Param->OutputFile )
|
|
{
|
|
strcpy(outFileName, Param->OutputFile);
|
|
}
|
|
else
|
|
{
|
|
strcpy(outFileName, Param->Files[curFile]);
|
|
RemoveExtension(outFileName);
|
|
strcat(outFileName, ".p3d");
|
|
}
|
|
output = new tlFile(new tlFileByteStream(outFileName, omWRITE), tlFile::CHUNK32);
|
|
if( ( output == 0 ) || ( !output->IsOpen() ) )
|
|
{
|
|
printf("Could not open %s for writing\n", outFileName);
|
|
exit(-1);
|
|
}
|
|
sameFile = false;
|
|
}
|
|
for( int i = 0; i < g_TextureCollectionCount; ++i )
|
|
{
|
|
delete g_TextureCollection[ i ];
|
|
g_TextureCollection[ i ] = 0;
|
|
}
|
|
|
|
/*************************************************/
|
|
// First pass for input file, nose through the frame controllers.
|
|
// Go through all the sub-chunks of the input and look for shader name
|
|
//in heirarchy of texture frame controller.
|
|
int inChunkIndex;
|
|
for( inChunkIndex = 0; inChunkIndex < inChunk->SubChunkCount(); inChunkIndex++ )
|
|
{
|
|
tlDataChunk* sub = inChunk->GetSubChunk( inChunkIndex );
|
|
if( sub->ID() == Pure3D::Animation::FrameControllerData::FRAME_CONTROLLER )
|
|
{
|
|
// We have a frame controller...
|
|
tlFrameControllerChunk* controller = static_cast<tlFrameControllerChunk*>( sub );
|
|
if( controller->GetType() == Pure3DAnimationTypes::TEXTURE_TEX )
|
|
{
|
|
// and it's a texture controller. So check the hierarchy name.
|
|
bool foundShader = false;
|
|
const char* heirName = controller->HierarchyName();
|
|
if( wildCardShader )
|
|
{
|
|
foundShader = strncmp( heirName, Param->ShaderName, shaderNameLen ) == 0;
|
|
}
|
|
else
|
|
{
|
|
foundShader = strcmp( heirName, Param->ShaderName ) == 0;
|
|
}
|
|
if( foundShader )
|
|
{
|
|
// We found the shader so remember the texture animation data.
|
|
TextureData* texData = new TextureData();
|
|
if( texData == 0 )
|
|
{
|
|
printf( "Unable to allocate texture data.\n" );
|
|
exit( -1 );
|
|
}
|
|
texData->m_ShaderID = tlEntity::MakeUID( heirName );
|
|
texData->m_ControllerChunk = tlEntity::MakeUID( controller->GetName() );
|
|
texData->m_AnimationChunk = tlEntity::MakeUID( controller->AnimationName() );
|
|
// Look to see if we already have this shader.
|
|
//If we do, we'll just erase the controller and animation and modify the multicontroller.
|
|
//The first texData will be used to create the chunk set.
|
|
for( int i = 0; i < g_TextureCollectionCount; ++i )
|
|
{
|
|
if( g_TextureCollection[ i ]->m_ShaderID == texData->m_ShaderID )
|
|
{
|
|
texData->m_NumIDs = -1;
|
|
break;
|
|
}
|
|
}
|
|
g_TextureCollection[ g_TextureCollectionCount ] = texData;
|
|
++g_TextureCollectionCount;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/*************************************************/
|
|
// Second pass. Now we look for the animation to go with all those texture frame controllers
|
|
//we found.
|
|
for( inChunkIndex = 0; inChunkIndex < inChunk->SubChunkCount(); inChunkIndex++ )
|
|
{
|
|
tlDataChunk* sub = inChunk->GetSubChunk( inChunkIndex );
|
|
// Is this an animation?
|
|
if( sub->ID() != Pure3D::Animation::AnimationData::ANIMATION )
|
|
{
|
|
// No, not interested.
|
|
continue;
|
|
}
|
|
tlAnimationChunk* anim = static_cast<tlAnimationChunk*>( sub );
|
|
// Is it a texture animation?
|
|
if( anim->GetAnimationType() != Pure3DAnimationTypes::TEXTURE_TEX )
|
|
{
|
|
// No, not interested.
|
|
continue;
|
|
}
|
|
// We've found a texture animation. Check if it's one we are looking for.
|
|
TLUID animID = tlEntity::MakeUID( anim->GetName() );
|
|
for( int i = 0; i < g_TextureCollectionCount; ++i )
|
|
{
|
|
if( ( g_TextureCollection[ i ]->m_NumIDs == 0 ) && ( g_TextureCollection[ i ]->m_AnimationChunk == animID ) )
|
|
{
|
|
// We've found the animation to match a frame controller. Let the mess begin.
|
|
TextureData* texData = g_TextureCollection[ i ]; // just for convience.
|
|
// Examine the sub chunks...there should be a goup list in there...
|
|
for( int subChunkIndex = 0; subChunkIndex < anim->SubChunkCount(); ++subChunkIndex )
|
|
{
|
|
tlDataChunk* animSub = anim->GetSubChunk( subChunkIndex );
|
|
if( animSub->ID() == Pure3D::Animation::AnimationData::GROUP_LIST )
|
|
{
|
|
// Found the group list. Now we'll nose around the groups...
|
|
tlAnimationGroupListChunk* groupList = static_cast<tlAnimationGroupListChunk*>( animSub );
|
|
for( int listChunkIndex = 0; listChunkIndex < groupList->SubChunkCount(); ++listChunkIndex )
|
|
{
|
|
// Look at all the channels in the groups in the group list.
|
|
tlDataChunk* listSub = groupList->GetSubChunk( listChunkIndex );
|
|
if( listSub->ID() == Pure3D::Animation::AnimationData::GROUP )
|
|
{
|
|
tlAnimationGroupChunk* group = static_cast<tlAnimationGroupChunk*>( listSub );
|
|
for( int channelIndex = 0; channelIndex < (int)group->GetNumChannels(); ++channelIndex )
|
|
{
|
|
int i;
|
|
// In the group (whew), look at all the channels.
|
|
// Is it an entity channel?
|
|
tlDataChunk* channelSub = group->GetSubChunk( channelIndex );
|
|
if( channelSub->ID() != Pure3D::Animation::ChannelData::ENTITY )
|
|
{
|
|
// No, not interested.
|
|
continue;
|
|
}
|
|
// Is the entity channel a texture?
|
|
tlEntityChannelChunk* entityChannel = static_cast<tlEntityChannelChunk*>( channelSub );
|
|
if( entityChannel->Param() != Pure3DAnimationTypes::TEXTURE_TEX )
|
|
{
|
|
// No, not interested.
|
|
continue;
|
|
}
|
|
// Okay we have the entity channel for a texture animation, now we
|
|
//record the texture names.
|
|
// We might end up allocating extra space for textures, but we'll
|
|
//leave the UIDs at zero at the end of the array.
|
|
texData->m_TextureID = new TLUID[ entityChannel->GetNumFrames() ];
|
|
if( texData->m_TextureID == 0 )
|
|
{
|
|
printf( "Unable to allocate texture array.\n" );
|
|
exit( -1 );
|
|
}
|
|
for( i = 0; i < (int)entityChannel->GetNumFrames(); ++i )
|
|
{
|
|
texData->m_TextureID[ i ] = 0;
|
|
}
|
|
// Get all the frame values.
|
|
char** values = entityChannel->GetValues();
|
|
for( i = 0; i < (int)entityChannel->GetNumFrames(); ++i )
|
|
{
|
|
TLUID textureID = tlEntity::MakeUID( values[ i ] );
|
|
// We have the texture tUID, look if it's a duplicate.
|
|
bool textureDup = false;
|
|
for( int j = 0; j < texData->m_NumIDs; ++j )
|
|
{
|
|
if( texData->m_TextureID[ j ] == textureID )
|
|
{
|
|
// this one is a duplicate, skip it.
|
|
textureDup = true;
|
|
break;
|
|
}
|
|
}
|
|
if( !textureDup )
|
|
{
|
|
// It's not a duplicate so remember it.
|
|
texData->m_TextureID[ texData->m_NumIDs ] = textureID;
|
|
++texData->m_NumIDs;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/*************************************************/
|
|
// Third pass, create the set chunks from the textures we got from those texture animations.
|
|
for( inChunkIndex = 0; inChunkIndex < inChunk->SubChunkCount(); inChunkIndex++ )
|
|
{
|
|
tlDataChunk* sub = inChunk->GetSubChunk( inChunkIndex );
|
|
if( sub->ID() == Pure3D::Texture::TEXTURE )
|
|
{
|
|
// We've found a texture, check to see if it's one of the textures we are looking for.
|
|
TextureData* texData = 0;
|
|
int textureIDIndex = 0;
|
|
TLUID textureID = tlEntity::MakeUID( sub->GetName() );
|
|
for( int i = 0; i < g_TextureCollectionCount; ++i )
|
|
{
|
|
texData = g_TextureCollection[ i ];
|
|
for( textureIDIndex = 0; textureIDIndex < texData->m_NumIDs; ++textureIDIndex )
|
|
{
|
|
if( textureID == texData->m_TextureID[ textureIDIndex ] )
|
|
{
|
|
if( texData->m_pSetChunk == 0 )
|
|
{
|
|
texData->m_pSetChunk = new tlSetChunk();
|
|
if( texData->m_pSetChunk == 0 )
|
|
{
|
|
printf( "Unable to create tlSetChunk.\n" );
|
|
exit( -1 );
|
|
}
|
|
}
|
|
if( textureIDIndex == 0 )
|
|
{
|
|
// This is the first texture in the set so we'll use the name for
|
|
//the set.
|
|
texData->m_pSetChunk->SetName( sub->GetName() );
|
|
}
|
|
texData->m_pSetChunk->AppendSubChunk( sub, 0 );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*************************************************/
|
|
// Forth pass, process the file. Copy all the chunks over,
|
|
//except the texture frame controllers, texture animations,
|
|
//and textures we've used. Also, modify the multicontrollers
|
|
//to exclude the texture frame controllers.
|
|
tlDataChunk* outChunk = new tlDataChunk;
|
|
|
|
if( Param->WriteHistory && !sameFile )
|
|
{
|
|
// put a history chunk in the output
|
|
// a history chunk shows what version of the tool
|
|
// was run on the file with what command-line
|
|
// parameters
|
|
outChunk->AppendSubChunk(Param->HistoryChunk());
|
|
sameFile = true;
|
|
}
|
|
|
|
if( outChunk == 0 )
|
|
{
|
|
printf( "Unable to create output chunk.\n" );
|
|
exit( -1 );
|
|
}
|
|
for( inChunkIndex = 0; inChunkIndex < inChunk->SubChunkCount(); inChunkIndex++ )
|
|
{
|
|
tlDataChunk* sub = inChunk->GetSubChunk( inChunkIndex );
|
|
if( sub->ID() == Pure3D::Texture::TEXTURE )
|
|
{
|
|
bool inAnySet = false;
|
|
for( int i = 0; i < g_TextureCollectionCount; ++i )
|
|
{
|
|
// Theorically a texture can go into multiple sets so go through them all.
|
|
bool found = false;
|
|
TextureData* texData = g_TextureCollection[ i ];
|
|
for( int j = 0; j < texData->m_NumIDs; ++j )
|
|
{
|
|
// Look to see if this texture is in this set.
|
|
if( tlEntity::MakeUID( sub->GetName() ) == texData->m_TextureID[ j ] )
|
|
{
|
|
found = true;
|
|
inAnySet = true;
|
|
break;
|
|
}
|
|
}
|
|
if( found && ( texData->m_NumIDs > 0 ) )
|
|
{
|
|
// This texture has gone into this set chunk. We'll write the
|
|
//set chunk out at this point so it's roughly in the same place in
|
|
//file to protect against load order dependency problems.
|
|
if( texData->m_WritenOut == false )
|
|
{
|
|
texData->m_pSetChunk->SetChildCount( texData->m_NumIDs );
|
|
outChunk->AppendSubChunk( texData->m_pSetChunk );
|
|
texData->m_WritenOut = true; // Only write it once.
|
|
}
|
|
}
|
|
}
|
|
if( !inAnySet )
|
|
{
|
|
// This isn't a texture we're interested in so write it out.
|
|
outChunk->AppendSubChunk( sub );
|
|
}
|
|
}
|
|
else if( sub->ID() == Pure3D::Animation::FrameControllerData::FRAME_CONTROLLER )
|
|
{
|
|
// This is a frame controller. If it's one we've used then don't include it.
|
|
bool found = false;
|
|
TLUID controllerID = tlEntity::MakeUID( sub->GetName() );
|
|
for( int i = 0; i < g_TextureCollectionCount; ++i )
|
|
{
|
|
if( controllerID == g_TextureCollection[ i ]->m_ControllerChunk )
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if( !found )
|
|
{
|
|
outChunk->AppendSubChunk( sub );
|
|
}
|
|
}
|
|
else if( sub->ID() == Pure3D::Animation::AnimationData::ANIMATION )
|
|
{
|
|
// This is an animation. If it's one we've used then don't include it.
|
|
bool found = false;
|
|
TLUID animID = tlEntity::MakeUID( sub->GetName() );
|
|
for( int i = 0; i < g_TextureCollectionCount; ++i )
|
|
{
|
|
if( animID == g_TextureCollection[ i ]->m_AnimationChunk )
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if( !found )
|
|
{
|
|
outChunk->AppendSubChunk( sub );
|
|
}
|
|
}
|
|
else if( sub->ID() == P3D_MULTI_CONTROLLER )
|
|
{
|
|
// This is a multicontroller. If a track references an framecontroller we
|
|
//used then exclude that track.
|
|
tlMultiControllerChunk16* multiCont = static_cast<tlMultiControllerChunk16*>( sub );
|
|
for( int subIndex = 0; subIndex < multiCont->SubChunkCount(); ++subIndex )
|
|
{
|
|
tlDataChunk* subChunk = multiCont->GetSubChunk( subIndex );
|
|
if( subChunk->ID() == P3D_MULTI_CONTROLLER_TRACKS )
|
|
{
|
|
tlMultiControllerTracksChunk16* tracks = static_cast<tlMultiControllerTracksChunk16*>( subChunk );
|
|
unsigned long numTracks = tracks->GetNumTracks();
|
|
tlMultiControllerTrackData* trackArray = tracks->GetTracks();
|
|
for( int i = 0; i < numTracks; ++i )
|
|
{
|
|
TLUID trackID = tlEntity::MakeUID( trackArray[ i ].name );
|
|
for( int j = 0; j < g_TextureCollectionCount; ++j )
|
|
{
|
|
if( trackID == g_TextureCollection[ j ]->m_ControllerChunk )
|
|
{
|
|
// Remove this track.
|
|
--numTracks;
|
|
tracks->SetNumTracks( numTracks );
|
|
multiCont->SetNumTracks( multiCont->GetNumTracks() - 1 );
|
|
if( i < numTracks )
|
|
{
|
|
trackArray[ i ] = trackArray[ numTracks ];
|
|
--i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if( subChunk->ID() == P3D_MULTI_CONTROLLER_TRACK )
|
|
{
|
|
tlMultiControllerTrackChunk16* track = static_cast<tlMultiControllerTrackChunk16*>( subChunk );
|
|
TLUID trackID = tlEntity::MakeUID( track->GetName() );
|
|
for( int i = 0; i < g_TextureCollectionCount; ++i )
|
|
{
|
|
if( trackID == g_TextureCollection[ i ]->m_ControllerChunk )
|
|
{
|
|
multiCont->RemoveSubChunk( subIndex );
|
|
multiCont->SetNumTracks( multiCont->GetNumTracks() - 1 );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
outChunk->AppendSubChunk( sub );
|
|
}
|
|
else
|
|
{
|
|
outChunk->AppendSubChunk( sub );
|
|
}
|
|
}
|
|
outChunk->Write( output );
|
|
delete outChunk;
|
|
outChunk = 0;
|
|
if( Param->OutputFile == 0 )
|
|
{
|
|
// We have multiple output files so get rid of the
|
|
//the output file now.
|
|
delete output;
|
|
output = 0;
|
|
}
|
|
for( i = 0; i < g_TextureCollectionCount; ++i )
|
|
{
|
|
delete g_TextureCollection[ i ];
|
|
g_TextureCollection[ i ] = 0;
|
|
}
|
|
} // end of main for loop
|
|
|
|
delete Param;
|
|
return 0;
|
|
}
|