Skip to content
Snippets Groups Projects
Commit 4a657bee authored by Amorilia's avatar Amorilia
Browse files

Added SplitKfTree and SplitNifTree functions to the interface. SplitNifTree is...

Added SplitKfTree and SplitNifTree functions to the interface. SplitNifTree is implemented for Morrowind only (copied code from old WriteNifTree function), SplitKfTree is not implemented at all. These two functions replace WriteFileGroup.
parent 2a73f9c4
No related branches found
No related tags found
No related merge requests found
......@@ -601,98 +601,94 @@ list<blk_ref> SearchAllNifTree( blk_ref const & root_block, string block_name )
// return result;
//};
// Writes valid XNif & XKf Files given a file name, and a pointer to the root block of the Nif file tree.
// (XNif and XKf file blocks are automatically extracted from the Nif tree if there are animation groups.)
void WriteFileGroup( string const & file_name, blk_ref const & root_block, unsigned int version ) {
// Write the full Nif file.
WriteNifTree( file_name, root_block, version );
// Split off XNif & XKf Files given a pointer to the root block of the full Nif file tree.
void SplitNifTree( blk_ref const & root_block, blk_ref & xnif_root, blk_ref & xkf_root, int game ) {
// Do we have animation groups (a NiTextKeyExtraData block)?
// If so, write out XNif and XKf files.
// If so, create XNif and XKf trees.
blk_ref txtkey_block = SearchNifTree( root_block, "NiTextKeyExtraData" );
if ( txtkey_block.is_null() == false ) {
// Create file names for the XKf and XNif files.
uint file_name_slash = uint(file_name.rfind("\\") + 1);
string file_name_path = file_name.substr(0, file_name_slash);
string file_name_base = file_name.substr(file_name_slash, file_name.length());
uint file_name_dot = uint(file_name_base.rfind("."));
file_name_base = file_name_base.substr(0, file_name_dot);
string xkf_name = file_name_path + "x" + file_name_base + ".kf";
string xnif_name = file_name_path + "x" + file_name_base + ".nif";
// Now construct the XNif file...
// We are lazy. Copy the Nif file. (TODO: remove keyframe controllers & keyframe data)
WriteNifTree( xnif_name, root_block, version );
// Now the XKf file...
// Create xkf root header.
blk_ref xkf_root = CreateBlock("NiSequenceStreamHelper");
// Add a copy of the NiTextKeyExtraData block to the XKf header.
blk_ref xkf_txtkey_block = CreateBlock("NiTextKeyExtraData");
xkf_root["Extra Data"] = xkf_txtkey_block;
ITextKeyExtraData const *itxtkey_block = QueryTextKeyExtraData(txtkey_block);
ITextKeyExtraData *ixkf_txtkey_block = QueryTextKeyExtraData(xkf_txtkey_block);
ixkf_txtkey_block->SetKeys(itxtkey_block->GetKeys());
// Append NiNodes with a NiKeyFrameController as NiStringExtraData blocks.
list<blk_ref> nodes = SearchAllNifTree( root_block, "NiNode" );
for ( list<blk_ref>::iterator it = nodes.begin(); it != nodes.end(); ) {
if ( (*it)->GetAttr("Controller")->asLink().is_null() || (*it)->GetAttr("Controller")->asLink()->GetBlockType() != "NiKeyframeController" )
it = nodes.erase( it );
else
it++;
};
blk_ref last_block = xkf_txtkey_block;
for ( list<blk_ref>::const_iterator it = nodes.begin(); it != nodes.end(); ++it ) {
blk_ref nodextra = CreateBlock("NiStringExtraData");
nodextra["String Data"] = (*it)["Name"]->asString();
last_block["Next Extra Data"] = nodextra;
last_block = nodextra;
};
// Add controllers & controller data.
last_block = xkf_root;
for ( list<blk_ref>::const_iterator it = nodes.begin(); it != nodes.end(); ++it ) {
blk_ref controller = (*it)->GetAttr("Controller")->asLink();
blk_ref xkf_controller = CreateBlock("NiKeyframeController");
xkf_controller["Flags"] = controller["Flags"]->asInt();
xkf_controller["Frequency"] = controller["Frequency"]->asFloat();
xkf_controller["Phase"] = controller["Phase"]->asFloat();
xkf_controller["Start Time"] = controller["Start Time"]->asFloat();
xkf_controller["Stop Time"] = controller["Stop Time"]->asFloat();
if ( game == GAME_MW ) {
// Construct the XNif file...
// We are lazy. (TODO: clone & remove keyframe controllers & keyframe data)
xnif_root = root_block;
blk_ref xkf_data = CreateBlock("NiKeyframeData");
xkf_controller["Data"] = xkf_data;
IKeyframeData const *ikfdata = QueryKeyframeData(controller["Data"]->asLink());
IKeyframeData *ixkfdata = QueryKeyframeData(xkf_data);
ixkfdata->SetRotateType(ikfdata->GetRotateType());
ixkfdata->SetTranslateType(ikfdata->GetTranslateType());
ixkfdata->SetScaleType(ikfdata->GetScaleType());
ixkfdata->SetRotateKeys(ikfdata->GetRotateKeys());
ixkfdata->SetTranslateKeys(ikfdata->GetTranslateKeys());
ixkfdata->SetScaleKeys(ikfdata->GetScaleKeys());
if ( last_block == xkf_root ) {
if ( ! last_block["Controller"]->asLink().is_null() )
throw runtime_error("Cannot create .kf file for multicontrolled nodes."); // not sure 'bout this one...
last_block["Controller"] = xkf_controller;
} else {
if ( ! last_block["Next Controller"]->asLink().is_null() )
throw runtime_error("Cannot create .kf file for multicontrolled nodes."); // not sure 'bout this one...
last_block["Next Controller"] = xkf_controller;
// Now the XKf file...
// Create xkf root header.
xkf_root = CreateBlock("NiSequenceStreamHelper");
// Add a copy of the NiTextKeyExtraData block to the XKf header.
blk_ref xkf_txtkey_block = CreateBlock("NiTextKeyExtraData");
xkf_root["Extra Data"] = xkf_txtkey_block;
ITextKeyExtraData const *itxtkey_block = QueryTextKeyExtraData(txtkey_block);
ITextKeyExtraData *ixkf_txtkey_block = QueryTextKeyExtraData(xkf_txtkey_block);
ixkf_txtkey_block->SetKeys(itxtkey_block->GetKeys());
// Append NiNodes with a NiKeyFrameController as NiStringExtraData blocks.
list<blk_ref> nodes = SearchAllNifTree( root_block, "NiNode" );
for ( list<blk_ref>::iterator it = nodes.begin(); it != nodes.end(); ) {
if ( (*it)->GetAttr("Controller")->asLink().is_null() || (*it)->GetAttr("Controller")->asLink()->GetBlockType() != "NiKeyframeController" )
it = nodes.erase( it );
else
it++;
};
last_block = xkf_controller;
// note: targets are automatically calculated, we don't need to reset them
};
// Now write it out...
WriteNifTree( xkf_name, xkf_root, version );
blk_ref last_block = xkf_txtkey_block;
for ( list<blk_ref>::const_iterator it = nodes.begin(); it != nodes.end(); ++it ) {
blk_ref nodextra = CreateBlock("NiStringExtraData");
nodextra["String Data"] = (*it)["Name"]->asString();
last_block["Next Extra Data"] = nodextra;
last_block = nodextra;
};
// Add controllers & controller data.
last_block = xkf_root;
for ( list<blk_ref>::const_iterator it = nodes.begin(); it != nodes.end(); ++it ) {
blk_ref controller = (*it)->GetAttr("Controller")->asLink();
blk_ref xkf_controller = CreateBlock("NiKeyframeController");
xkf_controller["Flags"] = controller["Flags"]->asInt();
xkf_controller["Frequency"] = controller["Frequency"]->asFloat();
xkf_controller["Phase"] = controller["Phase"]->asFloat();
xkf_controller["Start Time"] = controller["Start Time"]->asFloat();
xkf_controller["Stop Time"] = controller["Stop Time"]->asFloat();
blk_ref xkf_data = CreateBlock("NiKeyframeData");
xkf_controller["Data"] = xkf_data;
IKeyframeData const *ikfdata = QueryKeyframeData(controller["Data"]->asLink());
IKeyframeData *ixkfdata = QueryKeyframeData(xkf_data);
ixkfdata->SetRotateType(ikfdata->GetRotateType());
ixkfdata->SetTranslateType(ikfdata->GetTranslateType());
ixkfdata->SetScaleType(ikfdata->GetScaleType());
ixkfdata->SetRotateKeys(ikfdata->GetRotateKeys());
ixkfdata->SetTranslateKeys(ikfdata->GetTranslateKeys());
ixkfdata->SetScaleKeys(ikfdata->GetScaleKeys());
if ( last_block == xkf_root ) {
if ( ! last_block["Controller"]->asLink().is_null() )
throw runtime_error("Cannot create .kf file for multicontrolled nodes."); // not sure 'bout this one...
last_block["Controller"] = xkf_controller;
} else {
if ( ! last_block["Next Controller"]->asLink().is_null() )
throw runtime_error("Cannot create .kf file for multicontrolled nodes."); // not sure 'bout this one...
last_block["Next Controller"] = xkf_controller;
};
last_block = xkf_controller;
// note: targets are automatically calculated, we don't need to reset them
};
} else // TODO other games
throw runtime_error("Not yet implemented.");
} else {
// no animation groups: nothing to do
xnif_root = blk_ref();
xkf_root = blk_ref();
};
}
// Split an animation tree into multiple animation trees (one per animation group) and a kfm block.
void SplitKfTree( blk_ref const & root_block, Kfm & kfm, vector<blk_ref> & kf ) {
throw runtime_error("Not yet implemented.");
};
//Returns the total number of blocks in memory
unsigned int BlocksInMemory() {
return blocks_in_memory;
......
......@@ -79,6 +79,7 @@ struct SkinWeight;
struct ControllerLink;
struct TexDesc;
struct LODRange;
struct Kfm;
//--Constants--//
......@@ -159,6 +160,12 @@ const unsigned int VER_20_0_0_4 = 0x14000004; /*!< Nif Version 20.0.0.4 */
const unsigned int VER_UNSUPPORTED = 0xFFFFFFFF; /*!< Unsupported Nif Version */
const unsigned int VER_INVALID = 0xFFFFFFFE; /*!< Not a Nif file */
// Keyframe trees are game dependent, so here we define a few games.
const int GAME_MW = 0; // keyframe files: NiSequenceStreamHelper header, .kf extension
const int GAME_DAOC = 1; // keyframe files: NiNode header, .kfa extension
const int GAME_ZOO2 = 2; // (keyframe files not supported)
const int GAME_CIV4 = 3; // keyframe files: NiControllerSequence header, .kf extension
/*! Lists the basic texture types availiable from the ITexturingProperty interface*/
enum TexType {
BASE_MAP = 0, /*!< The basic texture used by most meshes. */
......@@ -337,12 +344,21 @@ void WriteNifTree( string const & file_name, blk_ref const & root_block, unsigne
void WriteNifTree( ostream & stream, blk_ref const & root_block, unsigned int version );
/*!
* Writes valid XNif & XKf Files given a file name, and a pointer to the root block of the Nif file tree. (XNif and XKf file blocks are automatically extracted from the Nif tree if there are animation groups.)
* \param file_name The desired file name for the new NIF file. This name serves as the basis for the names of any Kf files as well. The path is relative to the working directory unless a full path is specified.
* \param root_block The root block to start from when writing out the NIF file. All decedents of this block will be written to the file in tree-descending order.
* \param version The version of the NIF format to use when writing a file. Default is version 4.0.0.2.
* Split off animation from a nif tree. If no animation groups are defined, then both xnif_root and xkf_root will be null blocks.
* \param root_block The root block of the full tree.
* \param xnif_root The root block of the tree without animation.
* \param xkf_root The root block of the animation tree.
* \param game The game; determines what type of keyframe tree to write.
*/
void SplitNifTree( blk_ref const & root_block, blk_ref & xnif_root, blk_ref & xkf_root, int game );
/*!
* Split an animation tree into multiple animation trees (one per animation group) and a kfm block.
* \param root_block The root block of the full tree.
* \param kfm The root block of the tree without animation.
* \param kf Vector of root blocks of the new animation trees.
*/
void WriteFileGroup( string const & file_name, blk_ref const & root_block, unsigned int version );
void SplitKfTree( blk_ref const & root_block, Kfm & kfm, vector<blk_ref> & kf ); // I don't know whether we will be needing a game parameter here... so far we will only use this for CIV4.
/*!
* Merges two Nif trees into one. For standard Nif files, any blocks with the same name are merged. For Kf files, blocks are attatched to those that match the name specified in the KF root block. The data stored in a NIF file varies from version to version. Usually you are safe with the default option (the highest availiable version) but you may need to use an earlier version if you need to clone an obsolete piece of information.
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment