Exporter.cpp 11.31 KiB
#include "pch.h"
#include "AppSettings.h"
#include "niutils.h"
#include "obj/BSXFlags.h"
#include "obj/BSBound.h"
int Exporter::mVersion=013;
bool Exporter::mSelectedOnly=false;
bool Exporter::mTriStrips=true;
bool Exporter::mExportHidden=false;
bool Exporter::mExportFurn=true;
bool Exporter::mExportLights=false;
bool Exporter::mVertexColors=true;
float Exporter::mWeldThresh=0.1f;
string Exporter::mTexPrefix="textures";
bool Exporter::mExportCollision=true;
bool Exporter::mRemapIndices=true;
bool Exporter::mUseRegistry=false;
bool Exporter::mExportExtraNodes=false;
bool Exporter::mExportSkin=false;
bool Exporter::mUserPropBuffer=false;
bool Exporter::mFlattenHierarchy=false;
bool Exporter::mRemoveUnreferencedBones=false;
bool Exporter::mSortNodesToEnd=false;
string Exporter::mGameName = "User";
string Exporter::mNifVersion = "20.0.0.5";
int Exporter::mNifVersionInt = VER_20_0_0_5;
int Exporter::mNifUserVersion = 0;
bool Exporter::mSkeletonOnly=false;
bool Exporter::mExportCameras=false;
bool Exporter::mGenerateBoneCollision=false;
bool Exporter::mExportTransforms=true;
float Exporter::mDefaultPriority=0.0f;
Exporter::ExportType Exporter::mExportType = NIF_WO_ANIM;
bool Exporter::mMultiplePartitions=false;
int Exporter::mBonesPerVertex = 4;
int Exporter::mBonesPerPartition = 20;
bool Exporter::mUseTimeTags = false;
bool Exporter::mAutoDetect = true;
bool Exporter::mAllowAccum = true;
string Exporter::mCreatorName;
bool Exporter::mCollapseTransforms = true;
bool Exporter::mFixNormals = false;
bool Exporter::mTangentAndBinormalExtraData = false;
bool Exporter::mSupportPrnStrings = false;
stringlist Exporter::mRotate90Degrees;
bool Exporter::mSuppressPrompts = false;
bool Exporter::mUseAlternateStripper = false;
float Exporter::bhkScaleFactor = 7.0f;
static bool IsNodeOrParentSelected(INode *node) {
if (node == NULL)
return false;
if (node->Selected())
return true;
return IsNodeOrParentSelected(node->GetParentNode());
}
Exporter::Exporter(Interface *i, AppSettings *appSettings)
: mI(i), mAppSettings(appSettings), mSceneCollisionNode(NULL)
{
memset(progressCounters, 0, sizeof(progressCounters));
memset(progressMax, 0, sizeof(progressMax));
}
Exporter::Result Exporter::doExport(NiNodeRef &root, INode *node)
{
root->SetName("Scene Root");
int nifVersion = ParseVersionString(Exporter::mNifVersion);
mIsBethesda = (nifVersion == VER_20_0_0_5 || nifVersion == VER_20_0_0_4) && (Exporter::mNifUserVersion == 11);
if (mUseTimeTags && nifVersion >= VER_20_0_0_4) {
throw runtime_error("Time tag sequences are not supported for version 20.0.0.4 or higher.");
}
if (!Exporter::mSelectedOnly)
{
CalcBoundingBox(node, mBoundingBox);
if (mIsBethesda)
{
if (mSkeletonOnly)
{
BSBoundRef bsb = CreateNiObject<BSBound>();
bsb->SetName("BBX");
bsb->SetCenter( TOVECTOR3(mBoundingBox.Center()) );
bsb->SetDimensions( TOVECTOR3(mBoundingBox.Width() / 2.0f) );
root->AddExtraData(DynamicCast<NiExtraData>(bsb));
BSXFlagsRef bsx = CreateNiObject<BSXFlags>();
bsx->SetName("BSX");
bsx->SetData( 0x00000007 );
root->AddExtraData(DynamicCast<NiExtraData>(bsx));
}
else if (mExportType != NIF_WO_ANIM)
{
BSXFlagsRef bsx = CreateNiObject<BSXFlags>();
bsx->SetName("BSX");
bsx->SetData( 0x00000003 );
root->AddExtraData(DynamicCast<NiExtraData>(bsx));
}
else if (mExportCollision)
{
BSXFlagsRef bsx = CreateNiObject<BSXFlags>();
bsx->SetName("BSX");
bsx->SetData( 0x00000002 );
root->AddExtraData(DynamicCast<NiExtraData>(bsx));
}
}
exportUPB(root, node);
}
// Always Scan for Collision Nodes first
scanForCollision(node);
mNiRoot = root;
if (mSelectedOnly) {
int count = 0;
int n = mI->GetSelNodeCount();
vector<INode*> selectedRoots;
for (int i=0; i<n; ++i) {
INode * selNode = mI->GetSelNode(i);
if (!IsNodeOrParentSelected(selNode->GetParentNode())) {
selectedRoots.push_back(selNode);
count += countNodes(selNode);
}
}
if (selectedRoots.size() == 0) {
throw runtime_error("No Nodes have been selected for Export.");
}
progressMax[Geometry] = progressMax[Skin] = count;
if (mExportCollision)
progressMax[Collision] = progressMax[Geometry];
if (mExportType == NIF_WO_ANIM)
progressMax[Animation] = progressMax[Geometry];
for (int i=0; i<selectedRoots.size(); i++){
Result result = exportNodes(root, selectedRoots[i]);
if (result != Ok && result != Skip)
return result;
//if (mExportCollision) {
// result = exportCollision(root, selectedRoots[i]);
// if (result != Ok)
// return result;
//}
}
// Always Zero out root transforms
vector<NiAVObjectRef> children = root->GetChildren();
for (int i=0; i<children.size(); ++i){
children[i]->SetLocalTransform(Matrix44::IDENTITY);
}
// Fix Used Nodes that where never properly initialized. Happens normally during select export
for (NodeMap::iterator itr = mNodeMap.begin(); itr != mNodeMap.end(); ++itr) {
NiNodeRef bone = (*itr).second;
if (bone->GetParent() == NULL) {
if (INode* boneNode = mI->GetINodeByName((*itr).first.c_str())) {
makeNode(root, boneNode, false);
}
}
}
// Special case when exporting a single branch, use first child as scene root
if (selectedRoots.size() == 1 ) {
vector<NiNodeRef> childnodes = DynamicCast<NiNode>(root->GetChildren());
if (childnodes.size() == 1) {
NiNodeRef child = childnodes[0];
root->RemoveChild(child);
root = child;
mNiRoot = root;
exportPrn(root, selectedRoots[0]);
}
}
} else {
// Estimate progress bar
int count = countNodes(node);
progressMax[Geometry] = progressMax[Skin] = count;
if (mExportCollision)
progressMax[Collision] = progressMax[Geometry];
if (mExportType == NIF_WO_ANIM)
progressMax[Animation] = progressMax[Geometry];
// Normal export
Result result = exportNodes(root, node);
if (result != Ok)
return result;
// Fix Used Nodes that where never properly initialized. Happens normally during select export
for (NodeMap::iterator itr = mNodeMap.begin(); itr != mNodeMap.end(); ++itr) {
NiNodeRef bone = (*itr).second;
if (bone->GetParent() == NULL) {
if (INode* boneNode = mI->GetINodeByName((*itr).first.c_str())) {
makeNode(root, boneNode, false);
}
}
}
if (mExportCollision) {
result = exportCollision(root, node);
if (result != Ok)
return result;
}
}
// handle post export callbacks (like skin)
progressMax[Skin] = mPostExportCallbacks.size();
for (CallbackList::iterator cb = mPostExportCallbacks.begin(); cb != mPostExportCallbacks.end(); cb = mPostExportCallbacks.erase(cb)) {
ProgressUpdate(Skin, NULL);
(*cb)->execute();
delete (*cb);
}
// Remove unreferenced Bones
if (mRemoveUnreferencedBones)
removeUnreferencedBones(mNiRoot);
if (mSortNodesToEnd)
sortNodes(mNiRoot);
ApplyAllSkinOffsets(StaticCast<NiAVObject>(mNiRoot));
root = mNiRoot;
return Ok;
}
// Primary recursive decent routine
Exporter::Result Exporter::exportNodes(NiNodeRef &parent, INode *node)
{
TSTR nodeName = node->GetName();
//bool coll = npIsCollision(node);
bool coll = isCollision(node);
ProgressUpdate(Geometry, FormatText("'%s' Geometry", nodeName.data()));
// Abort if is a collision node or is hidden and we are not exporting hidden
if (coll || (node->IsHidden() && !mExportHidden))
return Skip;
bool local = !mFlattenHierarchy;
NiNodeRef nodeParent = mFlattenHierarchy ? mNiRoot : parent;
NiNodeRef newParent;
TimeValue t = 0;
ObjectState os = node->EvalWorldState(t);
// Always skip bones and bipeds
SClass_ID scid = node->SuperClassID();
Class_ID ncid = node->ClassID();
TSTR nodeClass; node->GetClassName(nodeClass);
if (node->IsBoneShowing())
newParent = exportBone(nodeParent, node);
else if (os.obj && os.obj->SuperClassID()==GEOMOBJECT_CLASS_ID)
{
TSTR objClass;
os.obj->GetClassName(objClass);
SClass_ID oscid = os.obj->SuperClassID();
Class_ID oncid = os.obj->ClassID();
if ( os.obj
&& ( os.obj->ClassID() == BONE_OBJ_CLASSID
|| os.obj->ClassID() == Class_ID(BONE_CLASS_ID,0)
|| os.obj->ClassID() == Class_ID(0x00009125,0) /* Biped Twist Helpers */
)
)
{
newParent = exportBone(nodeParent, node);
}
else if (!mSkeletonOnly)
{
if (mExportType != NIF_WO_ANIM && isNodeTracked(node)) {
// Create Node + Accum if has Start Track
newParent = createAccumNode( makeNode(nodeParent, node, local) , node);
} else if ( mExportExtraNodes || (mExportType != NIF_WO_ANIM && isNodeKeyed(node) ) ) {
// Create node if using Extra Nodes or if exporting with anim and node has key values
newParent = makeNode(nodeParent, node, local);
} else {
// Else dont create a node
newParent = nodeParent;
}
// No need to export meshes when NIF is not exported.
if (mExportType != SINGLE_KF_WO_NIF && mExportType != MULTI_KF_WO_NIF)
{
Result result = exportMesh(newParent, node, t);
if (result != Ok)
return result;
}
}
}
else if (mExportCameras && os.obj && os.obj->SuperClassID()==CAMERA_CLASS_ID)
{
newParent = makeNode(nodeParent, node, local);
}
else if (mExportLights && os.obj && os.obj->SuperClassID()==LIGHT_CLASS_ID)
{
return exportLight(nodeParent, node, (GenLight*)os.obj);
}
else if (isMeshGroup(node) && local) // only create node if local
{
newParent = makeNode(parent, node, local);
}
else
newParent = parent;
for (int i=0; i<node->NumberOfChildren(); i++)
{
Result result = exportNodes(newParent, node->GetChildNode(i));
if (result!=Ok && result!=Skip)
return result;
}
return Ok;
}
void Exporter::ProgressUpdate(ProgressSection section, const TCHAR *s)
{
if (mSuppressPrompts)
return;
if (mI->GetCancel()){
throw CancelExporterException();
}
int total = 1;
int percent = 1;
for (int i=0; i< int(ProgressSectionCount); i++){
total += progressMax[i];
if (i < section) {
percent += progressMax[i];
} else if (i == section ) {
percent += (++progressCounters[i]);
}
}
mI->ProgressUpdate( (percent * 100) / total , s == NULL ? TRUE : FALSE, const_cast<TCHAR*>(s));
}