Skip to content
Snippets Groups Projects
Commit b6f13338 authored by Tazpn's avatar Tazpn
Browse files

Added initial animation support for embedded NIF controller data. External...

Added initial animation support for embedded NIF controller data.  External files are suspect at the moment.
parent 244fb632
No related branches found
No related tags found
No related merge requests found
Showing
with 2290 additions and 1175 deletions
/**********************************************************************
*<
FILE: BaseImporter.h
DESCRIPTION: Base Importer class
CREATED BY: tazpn (Theo)
HISTORY:
*> Copyright (c) 2006, All Rights Reserved.
**********************************************************************/
#ifndef __BASEIMPORTER_H__
#define __BASEIMPORTER_H__
extern IBipMaster * (_cdecl * Max8CreateNewBiped)(float,float,class Point3 const &,int,int,int,int,int,int,int,int,int,int,int,int,float,int,int,int,int,int,int,int,int);
// Importer Base
class BaseImporter
{
public:
string name;
string path;
ImpInterface *i;
Interface *gi;
BOOL suppressPrompts;
bool iniFileValid;
string iniFileName;
AppSettings *appSettings;
Niflib::NiObjectRef root;
BaseImporter(){}
void BaseInit(const TCHAR *Name,ImpInterface *I,Interface *GI, BOOL SuppressPrompts)
{
name = Name;
i=I;
gi=GI;
suppressPrompts = SuppressPrompts;
char buffer[MAX_PATH] = {0}, *p = NULL;
GetFullPathName(Name, _countof(buffer), buffer, &p);
if (p) *p = 0;
path = buffer;
iniFileValid = false;
LPCTSTR Max8CreateNewBipedName = TEXT("?CreateNewBiped@@YAPAVIBipMaster@@MMABVPoint3@@HHHHHHHHHHHHMHHHHHHHH@Z");
HMODULE hBiped = GetModuleHandle("biped.dlc");
if (NULL != hBiped && 0 == Max8CreateNewBiped)
*(FARPROC*)&Max8CreateNewBiped = GetProcAddress(hBiped, Max8CreateNewBipedName);
// Load ini settings
iniFileValid = false;
LoadIniSettings();
ReadBlocks();
Initialize();
}
virtual void LoadIniSettings() = 0;
virtual void ReadBlocks() = 0;
virtual void Initialize() = 0;
virtual bool isValid() const { return (NULL != root); }
// Generic IniFile reading routine
template<typename T>
T GetIniValue(LPCTSTR Section, LPCTSTR Setting, T Default){
if (!iniFileValid)
return Default;
return ::GetIniValue<T>(Section, Setting, Default, iniFileName.c_str());
}
// Generic IniFile reading routine
template<typename T>
void SetIniValue(LPCTSTR Section, LPCTSTR Setting, T value){
if (!iniFileValid)
return;
::SetIniValue<T>(Section, Setting, value, iniFileName.c_str());
}
};
extern void GoToSkeletonBindPosition(std::vector<Niflib::NiNodeRef>& blocks);
#endif
/**********************************************************************
*<
FILE: ImportAnimation.cpp
DESCRIPTION: Animation Import Routines
CREATED BY: tazpn (Theo)
HISTORY:
*> Copyright (c) 2006, All Rights Reserved.
**********************************************************************/
#include "stdafx.h"
#include "MaxNifImport.h"
#include "NIFImporter.h"
#include "KFMImporter.h"
#include <obj/NiInterpolator.h>
#include <obj/NiTransformInterpolator.h>
#include <obj/NiTransformData.h>
#include <obj/NiTimeController.h>
#include <obj/NiTransformController.h>
#include <obj/NiTextKeyExtraData.h>
#include <obj/NiKeyframeController.h>
#include <obj/NiKeyframeData.h>
#include <obj/NiStringPalette.h>
using namespace Niflib;
const Class_ID IPOS_CONTROL_CLASS_ID = Class_ID(0x118f7e02,0xffee238a);
enum {
IPOS_X_REF = 0,
IPOS_Y_REF = 1,
IPOS_Z_REF = 2,
IPOS_W_REF = 3,
};
const float FramesPerSecond = 30.0f;
const int TicksPerFrame = GetTicksPerFrame();
inline TimeValue TimeToFrame(float t) {
return TimeValue(t * FramesPerSecond * TicksPerFrame);
}
typedef Key<float> FloatKey;
typedef Key<Quaternion> QuatKey;
typedef Key<Vector3> Vector3Key;
template<typename T, typename U>
inline T MapKey(U& key, float time);
template <typename T, typename U>
inline T& InitLinKey(T& rKey, U& key, float time)
{
rKey.time = TimeToFrame(time + key.time);
rKey.flags = 0;
return rKey;
}
template<>
inline ILinFloatKey MapKey<ILinFloatKey,FloatKey>(FloatKey& key, float time)
{
ILinFloatKey rKey;
rKey.val = key.data;
return InitLinKey(rKey, key, time);
}
template<>
inline ILinRotKey MapKey<ILinRotKey, QuatKey>(QuatKey& key, float time)
{
ILinRotKey rKey;
rKey.val = TOQUAT(key.data);
return InitLinKey(rKey, key, time);
}
template<>
inline ILinScaleKey MapKey<ILinScaleKey, FloatKey>(FloatKey& key, float time)
{
ILinScaleKey rKey;
rKey.val.s.Set(key.data, key.data, key.data);
rKey.val.q.Identity();
return InitLinKey(rKey, key, time);
}
template<>
inline ILinPoint3Key MapKey<ILinPoint3Key, Vector3Key>(Vector3Key& key, float time)
{
ILinPoint3Key rKey;
rKey.val = TOPOINT3(key.data);
return InitLinKey(rKey, key, time);
}
template <typename T, typename U>
inline T& InitBezKey(T& rKey, U& key, float time)
{
rKey.time = TimeToFrame(time + key.time);
rKey.flags = 0;
SetInTanType(rKey.flags,BEZKEY_FLAT);
SetOutTanType(rKey.flags,BEZKEY_FLAT);
return rKey;
}
template<>
inline IBezFloatKey MapKey<IBezFloatKey, FloatKey>(FloatKey& key, float time)
{
IBezFloatKey rKey;
rKey.val = key.data;
rKey.intan = key.forward_tangent;
rKey.outtan = key.backward_tangent;
return InitBezKey(rKey, key, time);
}
template<>
inline IBezPoint3Key MapKey<IBezPoint3Key, Vector3Key>(Vector3Key& key, float time)
{
IBezPoint3Key rKey;
rKey.val = TOPOINT3(key.data);
rKey.intan = TOPOINT3(key.forward_tangent);
rKey.outtan = TOPOINT3(key.backward_tangent);
return InitBezKey(rKey, key, time);
}
template<>
inline IBezQuatKey MapKey<IBezQuatKey, QuatKey>(QuatKey& key, float time)
{
IBezQuatKey rKey;
rKey.val = TOQUAT(key.data);
return InitBezKey(rKey, key, time);
}
template<>
inline IBezScaleKey MapKey<IBezScaleKey, FloatKey>(FloatKey& key, float time)
{
IBezScaleKey rKey;
rKey.val.s.Set(key.data, key.data, key.data);
rKey.val.q.Identity();
return InitBezKey(rKey, key, time);
}
template <typename T, typename U>
inline T& InitTCBKey(T& rKey, U& key, float time)
{
rKey.time = TimeToFrame(time + key.time);
rKey.tens = key.tension;
rKey.cont = key.continuity;
rKey.bias = key.bias;
rKey.easeIn = 0.0f;
rKey.easeOut = 0.0f;
return rKey;
}
template<>
inline ITCBFloatKey MapKey<ITCBFloatKey, FloatKey>(FloatKey& key, float time)
{
ITCBFloatKey rKey;
rKey.val = key.data;
return InitTCBKey(rKey, key, time);
}
template<>
inline ITCBRotKey MapKey<ITCBRotKey, QuatKey>(QuatKey& key, float time)
{
ITCBRotKey rKey;
rKey.val = TOQUAT(key.data);
InitTCBKey(rKey, key, time);
rKey.flags = TCBKEY_QUATVALID;
return rKey;
}
template<>
inline ITCBPoint3Key MapKey<ITCBPoint3Key, Vector3Key>(Vector3Key& key, float time)
{
ITCBPoint3Key rKey;
rKey.val = TOPOINT3(key.data);
return InitTCBKey(rKey, key, time);
}
template<>
inline ITCBScaleKey MapKey<ITCBScaleKey, FloatKey>(FloatKey& key, float time)
{
ITCBScaleKey rKey;
rKey.val.s.Set(key.data, key.data, key.data);
rKey.val.q.Identity();
return InitTCBKey(rKey, key, time);
}
template<typename T, typename U>
inline void SetKeys(Control *subCtrl, vector<U>& keys, float time)
{
if (subCtrl && !keys.empty()){
if (IKeyControl *ikeys = GetKeyControlInterface(subCtrl)){
ikeys->SetNumKeys(keys.size());
for (int i=0,n=keys.size(); i<n; ++i) {
ikeys->SetKey(i, &MapKey<T>(keys[i], time));
}
}
}
}
enum V3T
{
V3T_X, V3T_Y, V3T_Z
};
inline float GetValue(Vector3& value, V3T type)
{
switch (type) {
case V3T_X: return value.x;
case V3T_Y: return value.y;
case V3T_Z: return value.z;
}
return 0.0f;
}
FloatKey& CopyKey( FloatKey& dst, Vector3Key& src, V3T type)
{
dst.time = src.time;
dst.bias = src.bias;
dst.continuity = src.continuity;
dst.tension = src.tension;
dst.backward_tangent = GetValue(src.backward_tangent, type);
dst.forward_tangent = GetValue(src.forward_tangent, type);
dst.data = GetValue(src.data, type);
return dst;
}
void SplitKeys(vector<Vector3Key>&keys, vector<FloatKey>&xkeys, vector<FloatKey>&ykeys, vector<FloatKey>&zkeys)
{
int n = keys.size();
xkeys.resize(n), ykeys.resize(n), zkeys.resize(n);
for (int i=0,n=keys.size(); i<n; ++i) {
Vector3Key& key = keys[i];
CopyKey(xkeys[i], key, V3T_X), CopyKey(ykeys[i], key, V3T_Y), CopyKey(zkeys[i], key, V3T_Z);
}
}
typedef Key<string> KeyTextValue;
struct AnimationImport
{
AnimationImport(NifImporter& parent) : ni(parent) {}
NifImporter &ni;
vector<KeyTextValue> BuildKeyValues(NiObjectNETRef nref);
bool AddValues(NiObjectNETRef nref);
bool AddValues(vector<NiObjectNETRef>& nodes);
bool AddValues(Control *c, NiKeyframeDataRef data, float time);
Control* MakePositionXYZ(Control *tmCont, Class_ID clsid);
Control* MakeRotation(Control *tmCont, Class_ID rotClsid, Class_ID rollClsid);
Control* MakeScale(Control *tmCont, Class_ID clsid);
Control* GetTMController(const string& name);
};
bool NifImporter::ImportAnimation()
{
if (!enableAnimations)
return false;
AnimationImport ai(*this);
return ai.AddValues(DynamicCast<NiObjectNET>(nodes[0]->GetChildren()));
}
bool KFMImporter::ImportAnimation()
{
bool ok = false;
const float FramesIncrement = 1.0f/30.0f;
int curFrame = 0;
// Read Kf files
#ifdef USE_UNSUPPORTED_CODE
AnimationImport ai(*this);
float time = 0.0f;
for(vector<NiControllerSequenceRef>::iterator itr = kf.begin(); itr != kf.end(); ++itr){
NiControllerSequenceRef cntr = (*itr);
vector<ControllerLink> links = cntr->GetControllerData();
for (vector<ControllerLink>::iterator lnk=links.begin(); lnk != links.end(); ++lnk){
string name = (*lnk).targetName;
if (name.empty()) {
NiStringPaletteRef strings = lnk->stringPalette;
name = strings->GetSubStr((*lnk).nodeNameOffset);
}
if (name.empty())
continue;
Control *c = ai.GetTMController(name);
if (NULL == c)
continue;
float start = cntr->GetStartTime();
float stop = cntr->GetStopTime();
if ((*lnk).interpolator){
if (NiTransformInterpolatorRef interp = (*lnk).interpolator) {
if (NiTransformDataRef data = interp->GetData()){
ok |= ai.AddValues(c, data, time);
time += (stop-start) + FramesIncrement; // round to nearest 10f ?
}
}
} else if ((*lnk).controller) {
if (NiTransformControllerRef tc = DynamicCast<NiTransformController>((*lnk).controller)) {
if (NiTransformInterpolatorRef interp = tc->GetInterpolator()) {
if (NiTransformDataRef data = interp->GetData()){
ok |= ai.AddValues(c, data, time);
time += (stop-start) + FramesIncrement; // round to nearest 10f ?
}
}
} else if (NiKeyframeControllerRef kfc = DynamicCast<NiKeyframeController>((*lnk).controller)) {
if (NiKeyframeDataRef kfData = kfc->GetData()) {
ok |= ai.AddValues(c, kfData, time);
time += (stop-start) + FramesIncrement; // round to nearest 10f ?
}
}
}
}
}
#endif
return ok;
}
Control *AnimationImport::GetTMController(const string& name)
{
INode *n = ni.gi->GetINodeByName(name.c_str());
if (NULL == n)
return NULL;
Control *c = n->GetTMController();
if (NULL == c)
return NULL;
// ignore bipeds for now.
if ( (c->ClassID() == BIPSLAVE_CONTROL_CLASS_ID)
||(c->ClassID() == BIPBODY_CONTROL_CLASS_ID)
||(c->ClassID() == FOOTPRINT_CLASS_ID))
return NULL;
return c;
}
vector<KeyTextValue> AnimationImport::BuildKeyValues(NiObjectNETRef nref)
{
if (NiTextKeyExtraDataRef keydata = SelectFirstObjectOfType<NiTextKeyExtraData>(nref->GetExtraData()))
return keydata->GetKeys();
return vector<KeyTextValue>();
}
bool AnimationImport::AddValues(vector<NiObjectNETRef>& nodes)
{
bool ok = false;
for (vector<NiObjectNETRef>::iterator itr = nodes.begin(), end = nodes.end(); itr != end; ++itr)
{
ok |= AddValues(*itr);
NiNodeRef ninode = (*itr);
if (ninode)
ok |= AddValues(DynamicCast<NiObjectNET>(ninode->GetChildren()));
}
return ok;
}
bool AnimationImport::AddValues(NiObjectNETRef nref)
{
Control *c = GetTMController(nref->GetName().c_str());
if (NULL == c)
return false;
/* vector<KeyTextValue> tkeys = BuildKeyValues(nref);
if (tkeys.empty())
return false;*/
float time = 0.0f;
list< NiTimeControllerRef > clist = nref->GetControllers();
if (NiTransformControllerRef tc = SelectFirstObjectOfType<NiTransformController>(clist)) {
if (NiTransformInterpolatorRef interp = tc->GetInterpolator()) {
if (NiTransformDataRef data = interp->GetData()) {
return AddValues(c, data, time);
}
}
} else if (NiKeyframeControllerRef kf = SelectFirstObjectOfType<NiKeyframeController>(clist)) {
if (NiKeyframeDataRef kfData = kf->GetData()) {
return AddValues(c, kfData, time);
}
}
return false;
}
bool AnimationImport::AddValues(Control *c, NiKeyframeDataRef data, float time)
{
// Handle Translation
switch (data->GetTranslateType())
{
case LINEAR_KEY:
if (Control *subCtrl = MakePositionXYZ(c, Class_ID(LININTERP_FLOAT_CLASS_ID,0))) {
vector<FloatKey> xkeys, ykeys, zkeys;
SplitKeys(data->GetTranslateKeys(), xkeys, ykeys, zkeys);
SetKeys<ILinFloatKey, FloatKey>(subCtrl->GetXController(), xkeys, time);
SetKeys<ILinFloatKey, FloatKey>(subCtrl->GetYController(), ykeys, time);
SetKeys<ILinFloatKey, FloatKey>(subCtrl->GetZController(), zkeys, time);
}
break;
case QUADRATIC_KEY:
case XYZ_ROTATION_KEY:
if (Control *subCtrl = MakePositionXYZ(c, Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0))) {
vector<FloatKey> xkeys, ykeys, zkeys;
SplitKeys(data->GetTranslateKeys(), xkeys, ykeys, zkeys);
SetKeys<IBezFloatKey, FloatKey>(subCtrl->GetXController(), xkeys, time);
SetKeys<IBezFloatKey, FloatKey>(subCtrl->GetYController(), ykeys, time);
SetKeys<IBezFloatKey, FloatKey>(subCtrl->GetZController(), zkeys, time);
}
break;
case TBC_KEY:
if (Control *subCtrl = MakePositionXYZ(c, Class_ID(TCBINTERP_FLOAT_CLASS_ID,0))) {
vector<FloatKey> xkeys, ykeys, zkeys;
SplitKeys(data->GetTranslateKeys(), xkeys, ykeys, zkeys);
SetKeys<ITCBFloatKey, FloatKey>(subCtrl->GetXController(), xkeys, time);
SetKeys<ITCBFloatKey, FloatKey>(subCtrl->GetYController(), ykeys, time);
SetKeys<ITCBFloatKey, FloatKey>(subCtrl->GetZController(), zkeys, time);
}
break;
}
// Handle Rotation
switch (data->GetRotateType())
{
case LINEAR_KEY:
if (Control *subCtrl = MakeRotation(c, Class_ID(LININTERP_ROTATION_CLASS_ID,0), Class_ID(LININTERP_FLOAT_CLASS_ID,0))) {
SetKeys<ILinRotKey, QuatKey>(subCtrl, data->GetQuatRotateKeys(), time);
}
break;
case QUADRATIC_KEY:
if (Control *subCtrl = MakeRotation(c, Class_ID(HYBRIDINTERP_ROTATION_CLASS_ID,0), Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0))) {
SetKeys<IBezQuatKey, QuatKey>(subCtrl, data->GetQuatRotateKeys(), time);
}
break;
case XYZ_ROTATION_KEY:
if (Control *subCtrl = MakeRotation(c, Class_ID(EULER_CONTROL_CLASS_ID,0), Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0))) {
SetKeys<IBezFloatKey, FloatKey>(subCtrl->GetXController(), data->GetXRotateKeys(), time);
SetKeys<IBezFloatKey, FloatKey>(subCtrl->GetYController(), data->GetYRotateKeys(), time);
SetKeys<IBezFloatKey, FloatKey>(subCtrl->GetZController(), data->GetZRotateKeys(), time);
}
break;
case TBC_KEY:
if (ni.replaceTCBRotationWithBezier) {
// TCB simply is not working for me. Better off with Bezier as a workaround
if (Control *subCtrl = MakeRotation(c, Class_ID(HYBRIDINTERP_ROTATION_CLASS_ID,0), Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0))) {
SetKeys<IBezQuatKey, QuatKey>(subCtrl, data->GetQuatRotateKeys(), time);
}
} else {
if (Control *subCtrl = MakeRotation(c, Class_ID(TCBINTERP_ROTATION_CLASS_ID,0), Class_ID(TCBINTERP_FLOAT_CLASS_ID,0))) {
SetKeys<ITCBRotKey, QuatKey>(subCtrl, data->GetQuatRotateKeys(), time);
}
}
break;
}
// Handle Scale
switch (data->GetScaleType())
{
case LINEAR_KEY:
if (Control *subCtrl = MakeScale(c, Class_ID(LININTERP_SCALE_CLASS_ID,0))) {
SetKeys<ILinScaleKey, FloatKey>(subCtrl, data->GetScaleKeys(), time);
}
break;
case QUADRATIC_KEY:
case XYZ_ROTATION_KEY:
if (Control *subCtrl = MakeScale(c, Class_ID(HYBRIDINTERP_SCALE_CLASS_ID,0))) {
SetKeys<IBezScaleKey, FloatKey>(subCtrl, data->GetScaleKeys(), time);
}
break;
case TBC_KEY:
if (Control *subCtrl = MakeScale(c, Class_ID(TCBINTERP_SCALE_CLASS_ID,0))) {
SetKeys<ITCBScaleKey, FloatKey>(subCtrl, data->GetScaleKeys(), time);
}
break;
}
return true;
}
Control* AnimationImport::MakeRotation(Control *tmCont, Class_ID rotClsid, Class_ID rollClsid)
{
Interface *ip = ni.gi;
if (Control *c = tmCont->GetRotationController()) {
if (c->ClassID()!=rotClsid) {
if (Control *tmpCtrl = (Control*)ip->CreateInstance(CTRL_ROTATION_CLASS_ID, rotClsid)) {
if (!tmCont->SetRotationController(tmpCtrl))
tmpCtrl->DeleteThis();
else
c = tmpCtrl;
}
}
if (Control *r = tmCont->GetRollController()) {
if (r->ClassID()!=rollClsid) {
if (Control *r = (Control*)ip->CreateInstance(CTRL_FLOAT_CLASS_ID,rollClsid))
if (!tmCont->SetRollController(r))
r->DeleteThis();
}
}
return c;
}
return NULL;
}
Control* AnimationImport::MakeScale(Control *tmCont, Class_ID clsid)
{
Interface *ip = ni.gi;
if (Control *c = tmCont->GetScaleController()) {
if (c->ClassID()!=clsid) {
if (Control *tmpCtrl = (Control*)ip->CreateInstance(CTRL_SCALE_CLASS_ID, clsid)){
if (!tmCont->SetScaleController(tmpCtrl))
tmpCtrl->DeleteThis();
else
c = tmpCtrl;
}
}
return c;
}
return NULL;
}
Control* AnimationImport::MakePositionXYZ(Control *tmCont, Class_ID clsid)
{
Interface *ip = ni.gi;
if (Control *c = tmCont->GetPositionController()){
// First make the controller and XYZ Independent position controller, then fix individuals
if (c->ClassID()!= IPOS_CONTROL_CLASS_ID) {
if (Control *tmp = (Control*)ip->CreateInstance(CTRL_POSITION_CLASS_ID, IPOS_CONTROL_CLASS_ID)) {
if (!tmCont->SetPositionController(tmp)) {
tmp->DeleteThis();
} else {
c = tmp;
}
}
}
if (Control *x = c->GetXController()){
if (x->ClassID()!= clsid) {
if (Control *tmp = (Control*)ip->CreateInstance(CTRL_FLOAT_CLASS_ID, clsid))
c->SetReference(IPOS_X_REF, tmp);
}
}
if (Control *y = c->GetYController()){
if (y->ClassID()!= clsid) {
if (Control *tmp = (Control*)ip->CreateInstance(CTRL_FLOAT_CLASS_ID, clsid))
c->SetReference(IPOS_Y_REF, tmp);
}
}
if (Control *z = c->GetZController()){
if (z->ClassID()!= clsid) {
if (Control *tmp = (Control*)ip->CreateInstance(CTRL_FLOAT_CLASS_ID, clsid))
c->SetReference(IPOS_Z_REF, tmp);
}
}
return c;
}
return NULL;
}
/**********************************************************************
*<
FILE: ImportMeshAndSkin.cpp
DESCRIPTION: Mesh and Skin Import routines
CREATED BY: tazpn (Theo)
HISTORY:
*> Copyright (c) 2006, All Rights Reserved.
**********************************************************************/
#include "stdafx.h"
#include "MaxNifImport.h"
using namespace Niflib;
// Locate a TriObject in an Object if it exists
TriObject* GetTriObject(Object *o)
{
if (o && o->CanConvertToType(triObjectClassID))
return (TriObject *)o->ConvertToType(0, triObjectClassID);
while (o->SuperClassID() == GEN_DERIVOB_CLASS_ID && o)
{
IDerivedObject* dobj = (IDerivedObject *)(o);
o = dobj->GetObjRef();
if (o && o->CanConvertToType(triObjectClassID))
return (TriObject *)o->ConvertToType(0, triObjectClassID);
}
return NULL;
}
// Get or Create the Skin Modifier
Modifier *GetSkin(INode *node)
{
Object* pObj = node->GetObjectRef();
if (!pObj) return NULL;
while (pObj->SuperClassID() == GEN_DERIVOB_CLASS_ID)
{
IDerivedObject* pDerObj = (IDerivedObject *)(pObj);
int Idx = 0;
while (Idx < pDerObj->NumModifiers())
{
// Get the modifier.
Modifier* mod = pDerObj->GetModifier(Idx);
if (mod->ClassID() == SKIN_CLASSID)
{
// is this the correct Physique Modifier based on index?
return mod;
}
Idx++;
}
pObj = pDerObj->GetObjRef();
}
IDerivedObject *dobj = CreateDerivedObject(node->GetObjectRef());
//create a skin modifier and add it
Modifier *skinMod = (Modifier*) CreateInstance(OSM_CLASS_ID, SKIN_CLASSID);
dobj->SetAFlag(A_LOCK_TARGET);
dobj->AddModifier(skinMod);
dobj->ClearAFlag(A_LOCK_TARGET);
node->SetObjectRef(dobj);
return skinMod;
}
bool NifImporter::ImportTransform(ImpNode *node, NiAVObjectRef avObject)
{
node->SetTransform(0,TOMATRIX3(avObject->GetWorldTransform()));
return true;
}
bool NifImporter::ImportMeshes(NiNodeRef node)
{
bool ok = true;
try
{
vector<NiTriShapeRef> trinodes = DynamicCast<NiTriShape>(node->GetChildren());
for (vector<NiTriShapeRef>::iterator itr = trinodes.begin(), end = trinodes.end(); itr != end; ++itr){
ok |= ImportMesh(*itr);
}
vector<NiTriStripsRef> tristrips = DynamicCast<NiTriStrips>(node->GetChildren());
for (vector<NiTriStripsRef>::iterator itr = tristrips.begin(), end = tristrips.end(); itr != end; ++itr){
ok |= ImportMesh(*itr);
}
vector<NiNodeRef> nodes = DynamicCast<NiNode>(node->GetChildren());
for (vector<NiNodeRef>::iterator itr = nodes.begin(), end = nodes.end(); itr != end; ++itr){
ok |= ImportMeshes(*itr);
}
}
catch( exception & e )
{
e=e;
ok = false;
}
catch( ... )
{
ok = false;
}
return ok;
}
bool NifImporter::ImportMesh(ImpNode *node, TriObject *o, NiTriBasedGeomRef triGeom, NiTriBasedGeomDataRef triGeomData, bool hasTexture)
{
Mesh& mesh = o->GetMesh();
node->SetTransform(0,TOMATRIX3(triGeom->GetLocalTransform()));
// Vertex info
{
int nVertices = triGeomData->GetVertexCount();
vector<Vector3> vertices = triGeomData->GetVertices();
mesh.setNumVerts(nVertices);
for (int i=0; i < nVertices; ++i){
Vector3 &v = vertices[i];
mesh.verts[i].Set(v.x, v.y, v.z);
}
}
// uv texture info
{
int nUVSet = triGeomData->GetUVSetCount();
int n = 0;
for (int j=0; j<nUVSet; j++){
vector<TexCoord> texCoords = triGeomData->GetUVSet(j);
n = texCoords.size();
mesh.setNumTVerts(n, FALSE);
for (int i=0; i<n; ++i) {
TexCoord& texCoord = texCoords[i];
mesh.tVerts[i].Set(texCoord.u, (flipUVTextures) ? -texCoord.v : texCoord.v, 0);
}
}
}
if (removeDegenerateFaces)
mesh.RemoveDegenerateFaces();
if (removeIllegalFaces)
mesh.RemoveIllegalFaces();
if (enableAutoSmooth)
mesh.AutoSmooth(TORAD(autoSmoothAngle), FALSE, FALSE);
// Normals
{
mesh.checkNormals(TRUE);
vector<Vector3> n = triGeomData->GetNormals();
for (int i=0; i<n.size(); i++){
Vector3 v = n[i];
mesh.setNormal(i, Point3(v.x, v.y, v.z));
}
}
// vertex coloring
{
bool hasAlpha = false;
vector<Color4> cv = triGeomData->GetColors();
mesh.setNumVertCol(cv.size());
for (int i=0; i<cv.size(); i++){
Color4& c = cv[i];
mesh.vertCol[i].Set(c.r, c.g, c.b);
hasAlpha |= (c.a != 0.0);
}
}
return true;
}
void NifImporter::SetTrangles(Mesh& mesh, vector<Triangle>& v, bool hasTexture)
{
int n = v.size();
mesh.setNumFaces(n);
//if (hasTexture)
mesh.setNumTVFaces(n);
for (int i=0; i<n; ++i) {
Triangle& t = v[i];
Face& f = mesh.faces[i];
f.setVerts(t.v1, t.v2, t.v3);
f.Show();
f.setEdgeVisFlags(EDGE_VIS, EDGE_VIS, EDGE_VIS);
//if (hasTexture) {
TVFace& tf = mesh.tvFace[i];
tf.setTVerts(t.v1, t.v2, t.v3);
//}
}
}
bool NifImporter::ImportMesh(NiTriShapeRef triShape)
{
bool ok = true;
ImpNode *node = i->CreateNode();
if(!node) return false;
TriObject *triObject = CreateNewTriObject();
node->Reference(triObject);
string name = triShape->GetName();
node->SetName(name.c_str());
INode *inode = node->GetINode();
// Texture
bool hasTexture = ImportMaterialAndTextures(node, triShape);
Mesh& mesh = triObject->GetMesh();
NiTriShapeDataRef triShapeData = DynamicCast<NiTriShapeData>(triShape->GetData());
if (triShapeData == NULL)
return false;
ok |= ImportMesh(node, triObject, triShape, triShapeData, hasTexture);
// Triangles and texture vertices
if (ok)
{
vector<Triangle> v = triShapeData->GetTriangles();
SetTrangles(mesh, v, hasTexture);
}
if (enableSkinSupport)
ImportSkin(node, triShape);
i->AddNodeToScene(node);
// attach child
if (INode *parent = GetNode(triShape->GetParent()))
parent->AttachChild(inode, 1);
inode->Hide(triShape->GetHidden() ? TRUE : FALSE);
if (enableAutoSmooth){
if (TriObject *tri = GetTriObject(inode->GetObjectRef())){
tri->GetMesh().AutoSmooth(TORAD(autoSmoothAngle), FALSE, FALSE);
}
}
return ok;
}
bool NifImporter::ImportMesh(NiTriStripsRef triStrips)
{
bool ok = true;
ImpNode *node = i->CreateNode();
if(!node) return false;
INode *inode = node->GetINode();
TriObject *triObject = CreateNewTriObject();
node->Reference(triObject);
string name = triStrips->GetName();
node->SetName(name.c_str());
// Texture
bool hasTexture = ImportMaterialAndTextures(node, triStrips);
Mesh& mesh = triObject->GetMesh();
NiTriStripsDataRef triStripsData = DynamicCast<NiTriStripsData>(triStrips->GetData());
if (triStripsData == NULL)
return false;
ok |= ImportMesh(node, triObject, triStrips, triStripsData, hasTexture);
// Triangles and texture vertices
if (ok)
{
vector<Triangle> v = triStripsData->GetTriangles();
SetTrangles(mesh, v, hasTexture);
}
if (enableSkinSupport)
ImportSkin(node, triStrips);
i->AddNodeToScene(node);
// attach child
if (INode *parent = GetNode(triStrips->GetParent())) {
parent->AttachChild(inode, 0);
}
inode->Hide(triStrips->GetHidden() ? TRUE : FALSE);
// apply autosmooth after object creation for it to take hold
if (enableAutoSmooth){
if (TriObject *tri = GetTriObject(inode->GetObjectRef())){
tri->GetMesh().AutoSmooth(TORAD(autoSmoothAngle), FALSE, FALSE);
}
}
return ok;
}
struct VertexHolder
{
VertexHolder() : vertIndex(0), count(0) {}
int vertIndex;
int count;
Tab<INode*> boneNodeList;
Tab<float> weights;
};
bool NifImporter::ImportSkin(ImpNode *node, NiTriBasedGeomRef triGeom)
{
bool ok = true;
NiSkinInstanceRef nifSkin = triGeom->GetSkinInstance();
if (!nifSkin)
return false;
INode *tnode = node->GetINode();
NiSkinDataRef data = nifSkin->GetSkinData();
NiSkinPartitionRef part = nifSkin->GetSkinPartition();
vector<NiNodeRef> nifBones = nifSkin->GetBones();
//create a skin modifier and add it
Modifier *skinMod = GetSkin(tnode);
TriObject *triObject = GetTriObject(tnode->GetObjectRef());
Mesh& m = triObject->GetMesh();
//get the skin interface
if (ISkin *skin = (ISkin *) skinMod->GetInterface(I_SKIN)){
ISkinImportData* iskinImport = (ISkinImportData*) skinMod->GetInterface(I_SKINIMPORTDATA);
// Create Bone List
Tab<INode*> bones;
int i=0;
for (vector<NiNodeRef>::iterator itr = nifBones.begin(), end = nifBones.end(); itr != end; ++itr, ++i){
string name = (*itr)->GetName();
if (INode *boneRef = gi->GetINodeByName(name.c_str())) {
bones.Append(1, &boneRef);
iskinImport->AddBoneEx(boneRef, TRUE);
}
// Set Bone Transform
//Matrix3 tm = boneRef->GetObjectTM(0);
//Matrix3 m = TOMATRIX3(data->GetBoneTransform(i));
//iskinImport->SetBoneTm(boneRef, tm, m);
}
ObjectState os = tnode->EvalWorldState(0);
// Need to trigger ModifyObject in BonesDefMod prior to adding vertices or nothing is added
GetCOREInterface()->ForceCompleteRedraw();
// Need to get a list of bones and weights for each vertex.
vector<VertexHolder> vertexHolders;
vertexHolders.resize(m.numVerts);
for (int i=0, n=data->GetBoneCount();i<n; ++i){
if (INode *boneRef = bones[i]){
vector<SkinWeight> weights = data->GetBoneWeights(i);
for (vector<SkinWeight>::iterator itr=weights.begin(), end=weights.end(); itr != end; ++itr){
VertexHolder& h = vertexHolders[itr->index];
h.vertIndex = itr->index;
++h.count;
h.weights.Append(1, &itr->weight);
h.boneNodeList.Append(1, &boneRef);
}
}
}
// Assign the weights
for (vector<VertexHolder>::iterator itr=vertexHolders.begin(), end=vertexHolders.end(); itr != end; ++itr){
VertexHolder& h = (*itr);
if (h.count){
float sum = 0.0f;
for (int i=0; i<h.count; ++i)
sum += h.weights[i];
ASSERT(fabs(sum-1.0) < 0.001);
BOOL add = iskinImport->AddWeights(tnode, h.vertIndex, h.boneNodeList, h.weights);
add = add;
}
}
}
return ok;
}
/**********************************************************************
*<
FILE: ImportMtlAndTex.cpp
DESCRIPTION: Material and Texture Import routines
CREATED BY: tazpn (Theo)
HISTORY:
*> Copyright (c) 2006, All Rights Reserved.
**********************************************************************/
#include "stdafx.h"
#include <shaders.h>
#include "MaxNifImport.h"
#include "obj/NiWireframeProperty.h"
#include "obj/NiAlphaProperty.h"
#include "obj/NiStencilProperty.h"
#include "objectParams.h"
using namespace Niflib;
Texmap* NifImporter::CreateTexture(TexDesc& desc)
{
BitmapManager *bmpMgr = TheManager;
if (NiSourceTextureRef texSrc = desc.source){
string filename = texSrc->GetExternalFileName();
if (bmpMgr->CanImport(filename.c_str())){
BitmapTex *bmpTex = NewDefaultBitmapTex();
bmpTex->SetName(texSrc->GetName().c_str());
bmpTex->SetMapName(const_cast<TCHAR*>(FindImage(filename).c_str()));
bmpTex->SetAlphaAsMono(TRUE);
bmpTex->SetAlphaSource(ALPHA_NONE);
return bmpTex;
}
}
return NULL;
}
bool NifImporter::ImportMaterialAndTextures(ImpNode *node, NiAVObjectRef avObject)
{
// Texture
vector<NiPropertyRef> props = avObject->GetProperties();
NiTexturingPropertyRef texRef = SelectFirstObjectOfType<NiTexturingProperty>(props);
NiMaterialPropertyRef matRef = SelectFirstObjectOfType<NiMaterialProperty>(props);
NiWireframePropertyRef wireRef = SelectFirstObjectOfType<NiWireframeProperty>(props);
NiAlphaPropertyRef alphaRef = SelectFirstObjectOfType<NiAlphaProperty>(props);
NiStencilPropertyRef stencilRef = SelectFirstObjectOfType<NiStencilProperty>(props);
bool hasTexture = (texRef && matRef);
if (matRef != NULL){
StdMat2 *m = NewDefaultStdMat();
m->SetName(matRef->GetName().c_str());
if (showTextures)
m->SetMtlFlag(MTL_DISPLAY_ENABLE_FLAGS, TRUE);
m->SetAmbient(TOCOLOR(matRef->GetAmbientColor()),0);
m->SetDiffuse(TOCOLOR(matRef->GetDiffuseColor()),0);
m->SetSpecular(TOCOLOR(matRef->GetSpecularColor()),0);
Color c = TOCOLOR(matRef->GetEmissiveColor());
if (c.r != 0 || c.b != 0 || c.g != 0) {
m->SetSelfIllumColorOn(TRUE);
m->SetSelfIllumColor(c,0);
}
m->SetShinStr(0.0,0);
m->SetShininess(matRef->GetGlossiness()/100.0,0);
m->SetOpacity(matRef->GetTransparency(),0);
bool hasShaderAttributes = (wireRef != NULL) || (stencilRef != NULL);
if (m->SupportsShaders() && hasShaderAttributes) {
if (Shader *s = m->GetShader()) {
if (wireRef != NULL && (wireRef->GetFlags() & 1)) {
BOOL value = TRUE;
setMAXScriptValue(s->GetReference(0), "wire", 0, value);
}
if (stencilRef != NULL) {
}
}
}
if (NULL != texRef)
{
// Handle Base/Detail ???
if (texRef->HasTexture(DECAL_0_MAP)){
if (Texmap* tex = CreateTexture(texRef->GetTexture(DECAL_0_MAP)))
m->SetSubTexmap(ID_DI, tex);
if (texRef->HasTexture(BASE_MAP)){
m->LockAmbDiffTex(FALSE);
if (Texmap* tex = CreateTexture(texRef->GetTexture(BASE_MAP)))
m->SetSubTexmap(ID_AM, tex);
}
} else if (texRef->HasTexture(BASE_MAP)) {
if (Texmap* tex = CreateTexture(texRef->GetTexture(BASE_MAP)))
m->SetSubTexmap(ID_DI, tex);
}
// Handle Bump map
if (texRef->HasTexture(BUMP_MAP)) {
if (Texmap* tex = CreateTexture(texRef->GetTexture(BUMP_MAP)))
m->SetSubTexmap(ID_BU, tex);
}
// Shiny map
if (texRef->HasTexture(GLOSS_MAP)) {
if (Texmap* tex = CreateTexture(texRef->GetTexture(GLOSS_MAP)))
m->SetSubTexmap(ID_SS, tex);
}
// Self illumination
if (texRef->HasTexture(GLOW_MAP)) {
if (Texmap* tex = CreateTexture(texRef->GetTexture(GLOW_MAP)))
m->SetSubTexmap(ID_SI, tex);
}
}
gi->GetMaterialLibrary().Add(m);
node->GetINode()->SetMtl(m);
}
return hasTexture;
}
string NifImporter::FindImage(const string& name)
{
TCHAR buffer[MAX_PATH];
// Simply check for fully qualified path
if (PathIsRoot(name.c_str())) {
if (-1 != _taccess(name.c_str(), 0))
return string(buffer);
}
if (!path.empty()) {
PathCombine(buffer, path.c_str(), name.c_str()); // try as-is
if (-1 != _taccess(buffer, 0))
return string(buffer);
// try only filename in nif directory
PathCombine(buffer, path.c_str(), PathFindFileName(name.c_str()));
if (-1 != _taccess(buffer, 0))
return string(buffer);
}
if (appSettings != NULL) {
return appSettings->FindImage(name);
}
return name;
}
\ No newline at end of file
/**********************************************************************
*<
FILE: ImportSkeleton.cpp
DESCRIPTION: Skeleton import routines
CREATED BY: tazpn (Theo)
HISTORY:
*> Copyright (c) 2006, All Rights Reserved.
**********************************************************************/
#include "stdafx.h"
#include "MaxNifImport.h"
#include <obj/NiTriBasedGeom.h>
#include <obj/NiTriBasedGeomData.h>
#include <obj/NiTimeController.h>
#include <float.h>
#include <dummy.h>
using namespace Niflib;
struct NiNodeNameEquivalence : public NumericStringEquivalence
{
bool operator()(const NiNodeRef& n1, const NiNodeRef& n2) const {
return NumericStringEquivalence::operator()(n1->GetName(), n2->GetName());
}
};
void GoToSkeletonBindPosition(vector<NiNodeRef>& blocks)
{
//Send all skeleton roots to bind position
for (uint i = 0; i < blocks.size(); ++i) {
NiNodeRef node = blocks[i];
if ( node != NULL && node->IsSkeletonRoot() ) {
node->GoToSkeletonBindPosition();
}
}
}
bool NifImporter::HasSkeleton()
{
if (!skeletonCheck.empty()){
vector<NiNodeRef> bipedRoots = SelectNodesByName(nodes, skeletonCheck.c_str());
return !bipedRoots.empty();
}
return false;
}
bool NifImporter::IsBiped()
{
if (hasSkeleton){
list<NiExtraDataRef> extraData = nodes[0]->GetExtraData();
if (!extraData.empty()) {
return ( DynamicCast<BSBound>(extraData).size() != 0 );
}
}
return false;
}
void NifImporter::ImportBipeds(vector<NiNodeRef>& nodes)
{
IBipMaster* master = NULL;
try
{
vector<NiNodeRef> bipedRoots = SelectNodesByName(nodes, "Bip??");
std::stable_sort(bipedRoots.begin(), bipedRoots.end(), NiNodeNameEquivalence());
for (vector<NiNodeRef>::iterator bipedItr = bipedRoots.begin(); bipedItr != bipedRoots.end(); ++bipedItr)
{
string bipname = (*bipedItr)->GetName();
string match = bipname + "*";
vector<NiNodeRef> bipedNodes = SelectNodesByName(nodes, match.c_str());
float height = this->bipedHeight;
#if USE_CUSTOM_BSBOUND
list<NiExtraDataRef> extraData = nodes[0]->GetExtraData();
if (!extraData.empty()) {
BSBoundRef bound = SelectFirst<BSBound>(extraData);
if (bound) {
array<float,6> floats = bound->GetUnknownFloats();
height = floats[2] * 2.0f;
}
}
#endif
float angle = TORAD(bipedAngle);
Point3 wpos(0.0f,0.0f,0.0f);
BOOL arms = (CountNodesByName(bipedNodes, FormatText("%s L UpperArm", bipname.c_str())) > 0) ? TRUE : FALSE;
BOOL triPelvis = bipedTrianglePelvis ? TRUE : FALSE;
int nnecklinks=CountNodesByName(bipedNodes, FormatText("%s Neck*", bipname.c_str()));
int nspinelinks=CountNodesByName(bipedNodes, FormatText("%s Spine*", bipname.c_str()));
int nleglinks = 3 + CountNodesByName(bipedNodes, FormatText("%s L HorseLink", bipname.c_str()));
int ntaillinks = CountNodesByName(bipedNodes, FormatText("%s Tail*", bipname.c_str()));
int npony1links = CountNodesByName(bipedNodes, FormatText("%s Ponytail1*", bipname.c_str()));
int npony2links = CountNodesByName(bipedNodes, FormatText("%s Ponytail2*", bipname.c_str()));
int numfingers = CountNodesByName(bipedNodes, FormatText("%s L Finger?", bipname.c_str()));
int nfinglinks = CountNodesByName(bipedNodes, FormatText("%s L Finger0*", bipname.c_str()));
int numtoes = CountNodesByName(bipedNodes, FormatText("%s L Toe?", bipname.c_str()));
int ntoelinks = CountNodesByName(bipedNodes, FormatText("%s L Toe0*", bipname.c_str()));
BOOL prop1exists = CountNodesByName(bipedNodes, FormatText("%s Prop1", bipname.c_str())) ? TRUE : FALSE;
BOOL prop2exists = CountNodesByName(bipedNodes, FormatText("%s Prop2", bipname.c_str())) ? TRUE : FALSE;
BOOL prop3exists = CountNodesByName(bipedNodes, FormatText("%s Prop3", bipname.c_str())) ? TRUE : FALSE;
int forearmTwistLinks = CountNodesByName(bipedNodes, FormatText("%s L Fore*Twist*", bipname.c_str()));
int upperarmTwistLinks = CountNodesByName(bipedNodes, FormatText("%s L Up*Twist*", bipname.c_str()));
int thighTwistLinks = CountNodesByName(bipedNodes, FormatText("%s L Thigh*Twist*", bipname.c_str()));
int calfTwistLinks = CountNodesByName(bipedNodes, FormatText("%s L Calf*Twist*", bipname.c_str()));
int horseTwistLinks = CountNodesByName(bipedNodes, FormatText("%s L Horse*Twist*", bipname.c_str()));
NiNodeRef root = nodes[0];
IBipMaster* master = Max8CreateNewBiped(height, angle, wpos, arms, triPelvis,
nnecklinks, nspinelinks, nleglinks, ntaillinks, npony1links, npony2links,
numfingers, nfinglinks, numtoes, ntoelinks, bipedAnkleAttach, prop1exists,
prop2exists, prop3exists, forearmTwistLinks, upperarmTwistLinks, thighTwistLinks,
calfTwistLinks, horseTwistLinks);
master->SetRootName(const_cast<TCHAR*>(bipname.c_str()));
if (master)
{
master->BeginModes(BMODE_FIGURE, FALSE);
master->SetTrianglePelvis(FALSE);
master->SetDisplaySettings(BDISP_BONES);
LPCTSTR bipname = master->GetRootName();
// Rename twists, if necessary
RenameNode(gi, FormatText("%s L ForeTwist", bipname), FormatText("%s L ForearmTwist", bipname));
RenameNode(gi, FormatText("%s R ForeTwist", bipname), FormatText("%s R ForearmTwist", bipname));
RenameNode(gi, FormatText("%s R LUpArmTwist", bipname), FormatText("%s L UpperArmTwist", bipname));
RenameNode(gi, FormatText("%s R LUpArmTwist", bipname), FormatText("%s R UpperArmTwist", bipname));
NiNodeRef nifBip = FindNodeByName(nodes, bipname);
//AlignBiped(master, nifBip);
ScaleBiped(master, nifBip, true);
PositionBiped(master, nifBip, true);
RotateBiped(master, nifBip, true);
}
}
}
catch( exception & e )
{
e=e;
}
catch( ... )
{
}
if (master)
master->EndModes(BMODE_FIGURE, TRUE);
}
void NifImporter::AlignBiped(IBipMaster* master, NiNodeRef block)
{
string name = block->GetName();
Matrix44 wt = block->GetWorldTransform();
Matrix44 lt = block->GetLocalTransform();
Vector3 pos; Matrix33 rot; float scale;
wt.Decompose(pos, rot, scale);
INode *node = gi->GetINodeByName(name.c_str());
if (node != NULL)
{
Matrix3 m3 = node->GetNodeTM(TimeValue(), NULL); // local translation m
master->SetBipedPos(Point3(pos.x, pos.y, pos.z), 0, node, TRUE);
Matrix3 m(rot.rows[0].data, rot.rows[1].data, rot.rows[2].data, Point3(0,0,0));
Matrix3 im = Inverse(m);
Point3 p; Quat q; Point3 s;
DecomposeMatrix(im, p, q, s);
master->SetBipedRot(q, 0, node, TRUE);
}
else
{
}
vector<NiNodeRef> nodes = DynamicCast<NiNode>(block->GetChildren());
for (vector<NiNodeRef>::iterator itr = nodes.begin(), end = nodes.end(); itr != end; ++itr){
AlignBiped(master, *itr);
}
}
void NifImporter::PositionBiped(IBipMaster* master, NiNodeRef block, bool Recurse)
{
string name = block->GetName();
Matrix44 wt = block->GetWorldTransform();
Matrix44 lt = block->GetLocalTransform();
Vector3 pos; Matrix33 rot; float scale;
wt.Decompose(pos, rot, scale);
INode *node = gi->GetINodeByName(name.c_str());
if (node != NULL)
{
Matrix3 m3 = node->GetNodeTM(TimeValue(), NULL); // local translation m
master->SetBipedPos(Point3(pos.x, pos.y, pos.z), 0, node, TRUE);
Matrix3 m(rot.rows[0].data, rot.rows[1].data, rot.rows[2].data, Point3(0,0,0));
Matrix3 im = Inverse(m);
Point3 p; Quat q; Point3 s;
DecomposeMatrix(im, p, q, s);
master->SetBipedRot(q, 0, node, TRUE);
}
else
{
}
if (Recurse)
{
vector<NiNodeRef> nodes = DynamicCast<NiNode>(block->GetChildren());
for (vector<NiNodeRef>::iterator itr = nodes.begin(), end = nodes.end(); itr != end; ++itr){
PositionBiped(master, *itr, Recurse);
}
}
}
void NifImporter::RotateBiped(IBipMaster* master, NiNodeRef block, bool Recurse)
{
string name = block->GetName();
Matrix44 wt = block->GetWorldTransform();
Matrix44 lt = block->GetLocalTransform();
Vector3 pos; Matrix33 rot; float scale;
wt.Decompose(pos, rot, scale);
INode *node = gi->GetINodeByName(name.c_str());
if (node != NULL)
{
Matrix3 m3 = node->GetNodeTM(TimeValue(), NULL); // local translation m
master->SetBipedPos(Point3(pos.x, pos.y, pos.z), 0, node, TRUE);
Matrix3 m(rot.rows[0].data, rot.rows[1].data, rot.rows[2].data, Point3(0,0,0));
Matrix3 im = Inverse(m);
Point3 p; Quat q; Point3 s;
DecomposeMatrix(im, p, q, s);
master->SetBipedRot(q, 0, node, TRUE);
}
else
{
}
if (Recurse)
{
vector<NiNodeRef> nodes = DynamicCast<NiNode>(block->GetChildren());
for (vector<NiNodeRef>::iterator itr = nodes.begin(), end = nodes.end(); itr != end; ++itr){
RotateBiped(master, *itr, Recurse);
}
}
}
void NifImporter::ScaleBiped(IBipMaster* master, NiNodeRef block, bool Recurse)
{
string name = block->GetName();
Matrix44 wt = block->GetWorldTransform();
Matrix44 lt = block->GetLocalTransform();
Vector3 pos; Matrix33 rot; float scale;
wt.Decompose(pos, rot, scale);
INode *node = gi->GetINodeByName(name.c_str());
if (node != NULL)
{
Matrix3 m3 = node->GetNodeTM(TimeValue(), NULL); // local translation m
master->SetBipedPos(Point3(pos.x, pos.y, pos.z), 0, node, TRUE);
Matrix3 m(rot.rows[0].data, rot.rows[1].data, rot.rows[2].data, Point3(0,0,0));
Matrix3 im = Inverse(m);
Point3 p; Quat q; Point3 s;
DecomposeMatrix(im, p, q, s);
master->SetBipedRot(q, 0, node, TRUE);
}
else
{
}
if (Recurse)
{
vector<NiNodeRef> nodes = DynamicCast<NiNode>(block->GetChildren());
for (vector<NiNodeRef>::iterator itr = nodes.begin(), end = nodes.end(); itr != end; ++itr){
ScaleBiped(master, *itr, Recurse);
}
}
}
INode *NifImporter::CreateBone(const string& name, Point3 startPos, Point3 endPos, Point3 zAxis)
{
if (FPInterface * fpBones = GetCOREInterface(Interface_ID(0x438aff72, 0xef9675ac)))
{
FunctionID createBoneID = fpBones->FindFn(TEXT("createBone"));
FPValue result;
FPParams params (3, TYPE_POINT3, &startPos, TYPE_POINT3, &endPos, TYPE_POINT3, &zAxis);
FPStatus status = fpBones->Invoke(createBoneID, result, &params);
if (status == FPS_OK && result.type == TYPE_INODE)
{
if (INode *n = result.n)
{
n->SetName(const_cast<TCHAR*>(name.c_str()));
float len = Length(endPos-startPos);
float width = max(minBoneWidth, min(maxBoneWidth, len * boneWidthToLengthRatio));
if (Object* o = n->GetObjectRef())
{
setMAXScriptValue(o->GetReference(0), "width", 0, width);
setMAXScriptValue(o->GetReference(0), "height", 0, width);
}
n->ShowBone(1);
}
return result.n;
}
fpBones->ReleaseInterface();
}
return NULL;
}
INode *NifImporter::CreateHelper(const string& name, Point3 startPos)
{
//POINTHELP_CLASS_ID
if (DummyObject *ob = (DummyObject *)gi->CreateInstance(HELPER_CLASS_ID,Class_ID(DUMMY_CLASS_ID,0))) {
const float DUMSZ = 1.0f;
ob->SetBox(Box3(Point3(-DUMSZ,-DUMSZ,-DUMSZ),Point3(DUMSZ,DUMSZ,DUMSZ)));
if (INode *n = gi->CreateObjectNode(ob)) {
n->SetName(const_cast<TCHAR*>(name.c_str()));
Quat q; q.Identity();
PosRotScaleNode(n, startPos, q, 1.0f, prsPos);
return n;
}
}
return NULL;
}
void NifImporter::ImportBones(vector<NiNodeRef>& bones)
{
for (vector<NiNodeRef>::iterator itr = bones.begin(), end = bones.end(); itr != end; ++itr){
ImportBones(*itr);
}
}
float GetObjectLength(NiAVObjectRef obj)
{
float clen = obj->GetLocalTranslation().Magnitude();
if (clen < (FLT_EPSILON*10)) {
if (NiTriBasedGeomRef geom = DynamicCast<NiTriBasedGeom>(obj)) {
if (NiTriBasedGeomDataRef data = geom->GetData()) {
clen = data->GetRadius() * 2.0f;
}
}
}
return clen;
}
void NifImporter::ImportBones(NiNodeRef node)
{
try
{
string name = node->GetName();
vector<NiAVObjectRef> children = node->GetChildren();
vector<NiNodeRef> childNodes = DynamicCast<NiNode>(children);
NiAVObject::CollisionType cType = node->GetCollision();
if (cType == NiAVObject::ctBoundingBox && children.empty() && name=="Bounding Box")
return;
NiNodeRef parent = node->GetParent();
PosRotScale prs = prsDefault;
Matrix44 m4 = node->GetWorldTransform();
Vector3 pos; Matrix33 rot; float scale;
m4.Decompose(pos, rot, scale);
Matrix3 im = TOMATRIX3(m4);
Point3 p = im.GetTrans();
Quat q(im);
//q.Normalize();
Vector3 ppos;
Point3 zAxis(0,1,0);
bool hasChildren = !children.empty();
if (hasChildren) {
float len = 0.0f;
for (vector<NiAVObjectRef>::iterator itr=children.begin(), end = children.end(); itr != end; ++itr) {
len += GetObjectLength(*itr);
}
len /= float(children.size());
ppos = pos + Vector3(len, 0.0f, 0.0f); // just really need magnitude as rotation will take care of positioning
}
else if (parent)
{
float len = node->GetLocalTranslation().Magnitude();
ppos = pos + Vector3(len/3.0f, 0.0f, 0.0f);
}
Point3 pp(ppos.x, ppos.y, ppos.z);
INode *bone = gi->GetINodeByName(name.c_str());
if (bone)
{
// Is there a better way of "Affect Pivot Only" behaviors?
INode *pinode = bone->GetParentNode();
if (pinode)
bone->Detach(0,1);
PosRotScaleNode(bone, p, q, scale, prs);
if (pinode)
pinode->AttachChild(bone, 1);
}
else
{
list<NiTimeControllerRef> ctrls = node->GetControllers();
if (parent == NULL || parent->GetParent() == NULL || ctrls.empty())
{
bone = CreateHelper(name, p);
if (ctrls.empty())
bone->Hide(TRUE);
}
else if (bone = CreateBone(name, p, pp, zAxis))
{
PosRotScaleNode(bone, p, q, scale, prs);
if (createNubsForBones && childNodes.empty()){
if (INode *helper = CreateHelper(string().assign(name).append(" Nub"), pp)){
helper->Hide(TRUE);
bone->AttachChild(helper, 1);
}
}
}
if (bone)
{
if (parent)
{
if (INode *pn = gi->GetINodeByName(parent->GetName().c_str()))
pn->AttachChild(bone, 1);
}
bone->Hide(node->GetHidden() ? TRUE : FALSE);
}
}
if (bone)
{
ImportBones(childNodes);
}
}
catch( exception & e )
{
e=e;
}
catch( ... )
{
}
}
\ No newline at end of file
#include "StdAfx.h"
#include "KFMImporter.h"
#include <kfm.h>
#include "gen/ControllerLink.h"
using namespace Niflib;
KFMImporter::KFMImporter(const TCHAR *Name,ImpInterface *I,Interface *GI, BOOL SuppressPrompts)
: BaseClass()
{
BaseInit(Name, I, GI, SuppressPrompts);
}
KFMImporter::~KFMImporter(void)
{
}
void KFMImporter::ReadBlocks()
{
try
{
Kfm kfm;
int ver = kfm.Read(name);
if (ver != VER_UNSUPPORTED)
{
TCHAR buffer[MAX_PATH];
GetFullPathName(name.c_str(), MAX_PATH, buffer, NULL);
PathRemoveFileSpec(buffer);
#ifdef USE_UNSUPPORTED_CODE
//root = kfm.MergeActions(string(buffer));
BuildNodes();
string last_file;
vector<NiControllerSequenceRef> ctrllist;
for ( vector<KfmAction>::iterator it = kfm.actions.begin(); it != kfm.actions.end(); it++ ) {
string action_filename = path + '\\' + it->action_filename;
if (-1 != _taccess(action_filename.c_str(), 0)){
if (action_filename != last_file) {
ctrllist = DynamicCast<NiControllerSequence>(ReadNifList(action_filename));
}
if ((*it).unk_int2 && (*it).unk_int2 <= ctrllist.size()) {
if (NiControllerSequenceRef ctrlSeq = ctrllist[(*it).unk_int2-1])
kf.push_back(ctrlSeq);
}
}
}
#endif
}
}
catch (std::exception&)
{
}
catch (...)
{
}
}
bool KFMImporter::DoImport()
{
// might check if blocks exist and if not go ahead and import nif.
return ImportAnimation();
//return BaseClass::DoImport();
}
/**********************************************************************
*<
FILE: KFMImporter.h
DESCRIPTION: KFM Importer helper class
CREATED BY: tazpn (Theo)
HISTORY:
*> Copyright (c) 2006, All Rights Reserved.
**********************************************************************/
#ifndef _KFMIMPORTER_H_
#define _KFMIMPORTER_H_
#pragma once
#include "NIFImporter.h"
#include "niutils.h"
#include "obj/NiControllerSequence.h"
class KFMImporter : public NifImporter
{
typedef NifImporter BaseClass;
public:
KFMImporter(const TCHAR *Name,ImpInterface *I,Interface *GI, BOOL SuppressPrompts);
~KFMImporter(void);
void ReadBlocks();
virtual bool DoImport();
virtual bool isValid() const { return (NULL != root) || !kf.empty(); }
// Implemented in ImportAnimation.cpp
virtual bool ImportAnimation();
std::vector<Niflib::NiControllerSequenceRef> kf;
};
#endif
\ No newline at end of file
This diff is collapsed.
......@@ -14,19 +14,64 @@
#ifndef __MaxNifImport__H
#define __MaxNifImport__H
#include "Max.h"
#include <math.h>
#include <io.h>
#include <sstream>
#include <Max.h>
#include "resource.h"
#include "istdplug.h"
#include "iparamb2.h"
#include "iparamm2.h"
//SIMPLE TYPE
#include <istdplug.h>
#include <iparamb2.h>
#include <iparamm2.h>
#include <cs/Biped8Api.h>
#include <scenetraversal.h>
#include <plugapi.h>
#include <triobj.h>
#include <bitmap.h>
#include <modstack.h>
#include <iskin.h>
#include <strclass.h>
#include "objectParams.h"
#undef ALPHA_NONE
#include <math.h>
#include <string.h>
#include "niflib.h"
#include "obj\NiObject.h"
#include "obj\NiNode.h"
#include "obj\NiTriShape.h"
#include "obj\NiTriShapeData.h"
#include "obj\NiTriStrips.h"
#include "obj\NiTriStripsData.h"
#include "obj\NiMaterialProperty.h"
#include "obj\NiTexturingProperty.h"
#include "obj\NiSourceTexture.h"
#include "obj\NiExtraData.h"
#include "obj\BSBound.h"
#include "obj\NiSkinData.h"
#include "obj\NiSkinInstance.h"
#include "obj\NiSkinPartition.h"
#include "niutils.h"
#include "AppSettings.h"
#ifndef ASSERT
#ifdef _DEBUG
#include <crtdbg.h>
#define ASSERT _ASSERTE
#else
#define ASSERT(exp)
#endif
#endif
#include <direct.h>
#include <commdlg.h>
#include "NifImporter.h"
extern TCHAR *GetString(int id);
extern string shortDescription;
extern HINSTANCE hInstance;
#endif
......@@ -88,7 +88,7 @@ END
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 0,1,3,0
FILEVERSION 0,1,3,2
PRODUCTVERSION 0,1,3,0
FILEFLAGSMASK 0x37L
#ifdef _DEBUG
......@@ -105,7 +105,7 @@ BEGIN
BLOCK "040904b0"
BEGIN
VALUE "FileDescription", "3ds Max Nif Importer"
VALUE "FileVersion", "0, 1, 3, 0"
VALUE "FileVersion", "0, 1, 3, 2"
VALUE "InternalName", "MaxNifImport.dli"
VALUE "LegalCopyright", "Copyright (c) 2006, NIF File Format Library and Tools\r\nAll rights reserved."
VALUE "OriginalFilename", "MaxNifImport.dli"
......
......@@ -52,6 +52,7 @@
AdditionalIncludeDirectories="C:\3dsmax8\maxsdk\include"
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;USE_NIFLIB_TEMPLATE_HELPERS;_USE_MATH_DEFINES"
StringPooling="true"
ExceptionHandling="2"
RuntimeLibrary="0"
EnableFunctionLevelLinking="true"
ForceConformanceInForLoopScope="false"
......@@ -62,6 +63,7 @@
ProgramDataBaseFileName=".\Release\"
WarningLevel="3"
SuppressStartupBanner="true"
DebugInformationFormat="3"
CompileAs="0"
/>
<Tool
......@@ -78,7 +80,7 @@
<Tool
Name="VCLinkerTool"
AdditionalOptions="/MACHINE:I386"
AdditionalDependencies="odbc32.lib odbccp32.lib comctl32.lib shlwapi.lib core.lib geom.lib gfx.lib mesh.lib maxutil.lib maxscrpt.lib paramblk2.lib biped.lib bmm.lib niflib.lib"
AdditionalDependencies="odbc32.lib odbccp32.lib comctl32.lib shlwapi.lib core.lib geom.lib gfx.lib mesh.lib maxutil.lib maxscrpt.lib paramblk2.lib bmm.lib niflib.lib"
OutputFile="C:\3dsmax8\plugins\MaxNifImport.dli"
LinkIncremental="1"
SuppressStartupBanner="true"
......@@ -115,7 +117,7 @@
/>
<Tool
Name="VCPostBuildEventTool"
CommandLine="if exist &quot;C:\3dsmax8\plugcfg&quot; (&#x0D;&#x0A;if not exist &quot;C:\3dsmax8\plugcfg\MaxNifTools.ini&quot; (&#x0D;&#x0A;copy &quot;$(ProjectDir)MaxNifTools.ini&quot; &quot;C:\3dsmax8\plugcfg\MaxNifTools.ini&quot;&#x0D;&#x0A;)&#x0D;&#x0A;)&#x0D;&#x0A;if exist &quot;C:\3dsmax8&quot; (&#x0D;&#x0A;xcopy /D /Y /I &quot;$(ProjectDir)..\MaxNifPlugins_Readme.txt&quot; &quot;$(ProjectDir)Staging\&quot;&#x0D;&#x0A;)&#x0D;&#x0A;xcopy /D /Y /I &quot;$(TargetPath)&quot; &quot;$(ProjectDir)Staging\plugins\&quot;&#x0D;&#x0A;xcopy /D /Y /I &quot;$(ProjectDir)MaxNifTools.ini&quot; &quot;$(ProjectDir)Staging\plugcfg&quot;"
CommandLine="if exist &quot;C:\3dsmax8\plugcfg&quot; (&#x0D;&#x0A;if not exist &quot;C:\3dsmax8\plugcfg\MaxNifTools.ini&quot; (&#x0D;&#x0A;copy &quot;$(ProjectDir)MaxNifTools.ini&quot; &quot;C:\3dsmax8\plugcfg\MaxNifTools.ini&quot;&#x0D;&#x0A;)&#x0D;&#x0A;)&#x0D;&#x0A;if exist &quot;C:\3dsmax8&quot; (&#x0D;&#x0A;xcopy /D /Y /I &quot;$(ProjectDir)..\MaxNifPlugins_Readme.txt&quot; &quot;$(ProjectDir)Staging\&quot;&#x0D;&#x0A;)&#x0D;&#x0A;xcopy /D /Y /I &quot;$(TargetPath)&quot; &quot;$(ProjectDir)Staging\plugins\&quot;&#x0D;&#x0A;xcopy /D /Y /I &quot;$(ProjectDir)MaxNifTools.ini&quot; &quot;$(ProjectDir)Staging\plugcfg&quot;&#x0D;&#x0A;"
/>
</Configuration>
<Configuration
......@@ -164,7 +166,7 @@
ProgramDataBaseFileName=".\Debug\"
WarningLevel="3"
SuppressStartupBanner="true"
DebugInformationFormat="4"
DebugInformationFormat="3"
CompileAs="0"
/>
<Tool
......@@ -181,7 +183,7 @@
<Tool
Name="VCLinkerTool"
AdditionalOptions="/MACHINE:I386"
AdditionalDependencies="odbc32.lib odbccp32.lib comctl32.lib shlwapi.lib core.lib geom.lib gfx.lib mesh.lib maxutil.lib maxscrpt.lib paramblk2.lib biped.lib bmm.lib niflibd.lib"
AdditionalDependencies="odbc32.lib odbccp32.lib comctl32.lib shlwapi.lib core.lib geom.lib gfx.lib mesh.lib maxutil.lib maxscrpt.lib paramblk2.lib bmm.lib niflibd.lib"
OutputFile="C:\3dsmax8\plugins\MaxNifImport.dli"
LinkIncremental="2"
SuppressStartupBanner="true"
......@@ -298,6 +300,34 @@
RelativePath=".\AppSettings.h"
>
</File>
<File
RelativePath=".\BaseImporter.h"
>
</File>
<File
RelativePath=".\ImportAnimation.cpp"
>
</File>
<File
RelativePath=".\ImportMeshAndSkin.cpp"
>
</File>
<File
RelativePath=".\ImportMtlAndTex.cpp"
>
</File>
<File
RelativePath=".\ImportSkeleton.cpp"
>
</File>
<File
RelativePath=".\KFMImporter.cpp"
>
</File>
<File
RelativePath=".\KFMImporter.h"
>
</File>
<File
RelativePath=".\MaxNifImport.cpp"
>
......@@ -314,11 +344,23 @@
RelativePath=".\MaxNifImport.h"
>
</File>
<File
RelativePath=".\NIFImport.cpp"
>
</File>
<File
RelativePath=".\NIFImporter.h"
>
</File>
<File
RelativePath=".\niutils.cpp"
>
</File>
</Filter>
<File
RelativePath="..\MaxNifPlugins_Readme.txt"
>
</File>
<File
RelativePath=".\MaxNifTools.ini"
>
......
......@@ -44,15 +44,23 @@ BipedTrianglePelvis=0
; Remove unused bones from the biped on import of a mesh. Default: 1
RemoveUnusedImportedBones=1
; Minimum Bone Width / Maximum Bone Width / Ratio of Width to Length
MinBoneWidth=0.5
MaxBoneWidth=3
BoneWidthToLengthRatio=0.25
MinBoneWidth=0.001
MaxBoneWidth=0.1
BoneWidthToLengthRatio=0.01
; Force nub to point back to parent at expense of loss of rotation data. Default: 1
ForceRotation=1
ForceRotation=0
; Browse for skeleton on import. Default: 1
BrowseForSkeleton=1
; DefaultName for Skeletons (use if in same directory as imported nif)
DefaultSkeletonName=skeleton.nif
; Create Nubs for final bone in chain (not supported yet)
CreateNubsForBones=1
[AnimationImport]
; Enable Animation Import. Default: 1
EnableAnimations=1
; Replace TCB Rotation with Bezier (workaround for unexpected behavior with TCB rotations)
ReplaceTCBRotationWithBezier=1
; [Applications]
; RootPaths - Semicolon separated list of base directories to use when determining which app the imported file is from
......
/**********************************************************************
*<
FILE: ImporterCore.cpp
DESCRIPTION: Core Import helper routines
CREATED BY: tazpn (Theo)
HISTORY:
*> Copyright (c) 2006, All Rights Reserved.
**********************************************************************/
#include "stdafx.h"
#include "MaxNifImport.h"
using namespace Niflib;
// Define the standard section names used in the ini file
LPCTSTR NifImportSection = TEXT("MaxNifImport");
LPCTSTR SystemSection = TEXT("System");
LPCTSTR BipedImportSection = TEXT("BipedImport");
LPCTSTR AnimImportSection = TEXT("AnimationImport");
class IBipMaster;
IBipMaster * (_cdecl * Max8CreateNewBiped)(float,float,class Point3 const &,int,int,int,int,int,int,int,int,int,int,int,int,float,int,int,int,int,int,int,int,int) = 0;
NifImporter::NifImporter(const TCHAR *Name,ImpInterface *I,Interface *GI, BOOL SuppressPrompts)
: BaseImporter()
{
BaseInit(Name,I,GI,SuppressPrompts);
}
NifImporter::NifImporter()
{
}
void NifImporter::ReadBlocks()
{
//blocks = ReadNifList( name );
root = ReadNifTree(name);
BuildNodes();
}
static void BuildNodes(NiNodeRef object, vector<NiNodeRef>& nodes)
{
if (!object)
return;
vector<NiNodeRef> links = DynamicCast<NiNode>(object->GetChildren());
nodes.insert(nodes.end(), links.begin(), links.end());
for (vector<NiNodeRef>::iterator itr = links.begin(), end = links.end(); itr != end; ++itr)
BuildNodes(*itr, nodes);
}
void NifImporter::BuildNodes()
{
::BuildNodes(root, nodes);
std::sort(nodes.begin(), nodes.end(), NodeEquivalence());
}
void NifImporter::Initialize()
{
// Apply post processing checks after reading blocks
if (isValid()){
if (goToSkeletonBindPosition && !nodes.empty())
GoToSkeletonBindPosition(nodes);
// Only support biped if CreateNewBiped can be found.
useBiped &= (Max8CreateNewBiped != NULL);
hasSkeleton = HasSkeleton();
isBiped = IsBiped();
skeleton = (appSettings != NULL) ? appSettings->Skeleton : "";
importSkeleton = (appSettings != NULL) ? appSettings->useSkeleton : false;
importSkeleton &= hasSkeleton;
// Guess that the skeleton is the same one in the current directory
if (importSkeleton && !defaultSkeletonName.empty()) {
TCHAR buffer[MAX_PATH];
GetFullPathName(name.c_str(), _countof(buffer), buffer, NULL);
PathRemoveFileSpec(buffer);
PathAddBackslash(buffer);
PathAppend(buffer, defaultSkeletonName.c_str());
if (-1 != _taccess(buffer, 0))
skeleton = buffer;
}
}
}
void NifImporter::LoadIniSettings()
{
TCHAR iniName[MAX_PATH];
LPCTSTR pluginDir = gi->GetDir(APP_PLUGCFG_DIR);
PathCombine(iniName, pluginDir, "MaxNifTools.ini");
this->iniFileName = iniName;
iniFileValid = (-1 != _access(iniName, 0));
// Locate which application to use. If Auto, find first app where this file appears in the root path list
appSettings = NULL;
string curapp = GetIniValue<string>(NifImportSection, "CurrentApp", "AUTO");
if (0 == _tcsicmp(curapp.c_str(), "AUTO")) {
// Scan Root paths
for (AppSettingsMap::iterator itr = TheAppSettings.begin(), end = TheAppSettings.end(); itr != end; ++itr){
if ((*itr).IsFileInRootPaths(this->name)) {
appSettings = &(*itr);
break;
}
}
} else {
appSettings = FindAppSetting(curapp);
}
if (appSettings == NULL && !TheAppSettings.empty()){
appSettings = &TheAppSettings.front();
}
useBiped = GetIniValue<bool>(NifImportSection, "UseBiped", false);
skeletonCheck = GetIniValue<string>(NifImportSection, "SkeletonCheck", "Bip*");
showTextures = GetIniValue<bool>(NifImportSection, "ShowTextures", true);
removeIllegalFaces = GetIniValue<bool>(NifImportSection, "RemoveIllegalFaces", true);
removeDegenerateFaces = GetIniValue<bool>(NifImportSection, "RemoveDegenerateFaces", true);
enableAutoSmooth = GetIniValue<bool>(NifImportSection, "EnableAutoSmooth", true);
autoSmoothAngle = GetIniValue<float>(NifImportSection, "AutoSmoothAngle", 30.0f);
flipUVTextures = GetIniValue<bool>(NifImportSection, "FlipUVTextures", true);
enableSkinSupport = GetIniValue<bool>(NifImportSection, "EnableSkinSupport", true);
bipedHeight = GetIniValue<float>(BipedImportSection, "BipedHeight", 131.90f);
bipedAngle = GetIniValue<float>(BipedImportSection, "BipedAngle", 90.0f);
bipedAnkleAttach = GetIniValue<float>(BipedImportSection, "BipedAnkleAttach", 0.2f);
bipedTrianglePelvis = GetIniValue<bool>(BipedImportSection, "BipedTrianglePelvis", false);
removeUnusedImportedBones = GetIniValue<bool>(BipedImportSection, "RemoveUnusedImportedBones", false);
forceRotation = GetIniValue<bool>(BipedImportSection, "ForceRotation", true);
browseForSkeleton = GetIniValue<bool>(BipedImportSection, "BrowseForSkeleton", true);
defaultSkeletonName = GetIniValue<string>(BipedImportSection, "DefaultSkeletonName", "Skeleton.Nif");
minBoneWidth = GetIniValue<float>(BipedImportSection, "MinBoneWidth", 0.5f);
maxBoneWidth = GetIniValue<float>(BipedImportSection, "MaxBoneWidth", 3.0f);
boneWidthToLengthRatio = GetIniValue<float>(BipedImportSection, "BoneWidthToLengthRatio", 0.25f);
createNubsForBones = GetIniValue<bool>(BipedImportSection, "CreateNubsForBones", true);
replaceTCBRotationWithBezier = GetIniValue<bool>(AnimImportSection, "ReplaceTCBRotationWithBezier", true);
enableAnimations = GetIniValue<bool>(AnimImportSection, "EnableAnimations", true);
goToSkeletonBindPosition = (appSettings ? appSettings->goToSkeletonBindPosition : false);
}
void NifImporter::SaveIniSettings()
{
SetIniValue<bool>(NifImportSection, "UseBiped", useBiped);
SetIniValue<string>(NifImportSection, "Skeleton", skeleton);
SetIniValue<string>(NifImportSection, "SkeletonCheck", skeletonCheck);
SetIniValue<float>(BipedImportSection, "BipedHeight", bipedHeight);
SetIniValue<float>(BipedImportSection, "BipedAngle", bipedAngle);
SetIniValue<float>(BipedImportSection, "BipedAnkleAttach", bipedAnkleAttach);
SetIniValue<bool>(BipedImportSection, "BipedTrianglePelvis", bipedTrianglePelvis);
}
INode *NifImporter::GetNode(Niflib::NiNodeRef node)
{
// may want to make this a map if its hit a lot
if (NULL == node) return NULL;
return gi->GetINodeByName(node->GetName().c_str());
}
bool NifImporter::DoImport()
{
bool ok = true;
NiNodeRef rootNode = root;
if (isBiped)
{
if (useBiped){
ImportBipeds(nodes);
} else {
ImportBones(DynamicCast<NiNode>(rootNode->GetChildren()));
}
}
else
{
vector<string> importedBones;
if (importSkeleton)
{
if (browseForSkeleton)
{
TCHAR filter[64], *pfilter=filter;
pfilter = _tcscpy(filter, shortDescription.c_str());
pfilter = _tcscat(pfilter, " (*.NIF)");
pfilter += strlen(pfilter);
*pfilter++ = '\0';
_tcscpy(pfilter, "*.NIF");
pfilter += strlen(pfilter);
*pfilter++ = '\0';
*pfilter++ = '\0';
TCHAR filename[MAX_PATH];
GetFullPathName(skeleton.c_str(), _countof(filename), filename, NULL);
OPENFILENAME ofn;
memset(&ofn, 0, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = gi->GetMAXHWnd();
ofn.lpstrFilter = filter;
ofn.lpstrFile = filename;
ofn.nMaxFile = _countof(filename);
ofn.lpstrTitle = TEXT("Browse for Skeleton NIF...");
ofn.lpstrDefExt = TEXT("NIF");
ofn.Flags = OFN_HIDEREADONLY|OFN_EXPLORER|OFN_FILEMUSTEXIST|OFN_NOCHANGEDIR|OFN_PATHMUSTEXIST;
importSkeleton = GetOpenFileName(&ofn) ? true : false;
if (importSkeleton) {
skeleton = filename;
}
}
if (importSkeleton && !skeleton.empty()) {
NifImporter skelImport(skeleton.c_str(), i, gi, suppressPrompts);
if (skelImport.isValid())
{
if (skelImport.useBiped){
skelImport.ImportBipeds(skelImport.nodes);
} else {
NiNodeRef skelRoot = skelImport.root;
skelImport.ImportBones(DynamicCast<NiNode>(skelRoot->GetChildren()));
if (removeUnusedImportedBones){
importedBones = GetNamesOfNodes(skelImport.nodes);
}
}
}
}
} else if (hasSkeleton && useBiped) {
ImportBipeds(nodes);
}
if (isValid()) {
ImportBones(DynamicCast<NiNode>(rootNode->GetChildren()));
ok = ImportMeshes(rootNode);
if (importSkeleton && removeUnusedImportedBones){
vector<string> importedNodes = GetNamesOfNodes(nodes);
sort(importedBones.begin(), importedBones.end());
sort(importedNodes.begin(), importedNodes.end());
vector<string> results;
results.resize(importedBones.size());
vector<string>::iterator end = set_difference (
importedBones.begin(), importedBones.end(),
importedNodes.begin(), importedNodes.end(), results.begin());
for (vector<string>::iterator itr = results.begin(); itr != end; ++itr){
if (INode *node = gi->GetINodeByName((*itr).c_str())){
node->Delete(0, TRUE);
}
}
}
}
}
// Kick of animation import
ImportAnimation();
return true;
}
\ No newline at end of file
/**********************************************************************
*<
FILE: NifImporter.h
DESCRIPTION: NIF Importer
CREATED BY: tazpn (Theo)
HISTORY:
*> Copyright (c) 2006, All Rights Reserved.
**********************************************************************/
#ifndef __NIFIMPORTER_H__
#define __NIFIMPORTER_H__
#include "BaseImporter.h"
// NIF Importer
class NifImporter : public BaseImporter
{
public:
// Ini settings
bool showTextures; // show textures in viewport
bool removeIllegalFaces;
bool removeDegenerateFaces;
bool enableAutoSmooth;
float autoSmoothAngle;
bool flipUVTextures;
bool enableSkinSupport;
bool goToSkeletonBindPosition;
// Biped/Bones related settings
string skeleton;
float bipedHeight;
string skeletonCheck;
float bipedAngle;
float bipedAnkleAttach;
bool bipedTrianglePelvis;
bool importSkeleton;
bool useBiped;
bool hasSkeleton;
bool isBiped;
bool removeUnusedImportedBones;
bool forceRotation;
bool browseForSkeleton;
string defaultSkeletonName;
float minBoneWidth;
float maxBoneWidth;
float boneWidthToLengthRatio;
bool createNubsForBones;
// Animation related Settings
bool replaceTCBRotationWithBezier;
bool enableAnimations;
vector<Niflib::NiObjectRef> blocks;
vector<Niflib::NiNodeRef> nodes;
NifImporter(const TCHAR *Name,ImpInterface *I,Interface *GI, BOOL SuppressPrompts);
virtual void Initialize();
virtual void ReadBlocks();
void BuildNodes();
// Ini File related routines
void LoadIniSettings();
void SaveIniSettings();
bool HasSkeleton();
bool IsBiped();
void ImportBones(vector<Niflib::NiNodeRef>& bones);
void ImportBones(Niflib::NiNodeRef blocks);
void ImportBipeds(vector<Niflib::NiNodeRef>& blocks);
void AlignBiped(IBipMaster* master, Niflib::NiNodeRef block);
void PositionBiped(IBipMaster* master, Niflib::NiNodeRef block, bool Recurse = false);
void RotateBiped(IBipMaster* master, Niflib::NiNodeRef block, bool Recurse = false);
void ScaleBiped(IBipMaster* master, Niflib::NiNodeRef block, bool Recurse = false);
bool ImportMeshes(Niflib::NiNodeRef block);
string FindImage(const string& name);
void SetTrangles(Mesh& mesh, vector<Niflib::Triangle>& v, bool hasTexture);
bool ImportMesh(Niflib::NiTriShapeRef triShape);
bool ImportMesh(Niflib::NiTriStripsRef triStrips);
bool ImportMaterialAndTextures(ImpNode *node, Niflib::NiAVObjectRef avObject);
bool ImportTransform(ImpNode *node, Niflib::NiAVObjectRef avObject);
bool ImportMesh(ImpNode *node, TriObject *o, Niflib::NiTriBasedGeomRef triGeom, Niflib::NiTriBasedGeomDataRef triGeomData, bool hasTexture);
bool ImportSkin(ImpNode *node, Niflib::NiTriBasedGeomRef triGeom);
Texmap* CreateTexture(Niflib::TexDesc& desc);
INode *CreateBone(const string& name, Point3 startPos, Point3 endPos, Point3 zAxis);
INode *CreateHelper(const string& name, Point3 startPos);
INode *GetNode(Niflib::NiNodeRef node);
virtual bool DoImport();
// Animation Helpers
bool ImportAnimation();
protected:
NifImporter();
};
#endif
......@@ -44,15 +44,23 @@ BipedTrianglePelvis=0
; Remove unused bones from the biped on import of a mesh. Default: 1
RemoveUnusedImportedBones=1
; Minimum Bone Width / Maximum Bone Width / Ratio of Width to Length
MinBoneWidth=0.5
MaxBoneWidth=3
BoneWidthToLengthRatio=0.25
MinBoneWidth=0.001
MaxBoneWidth=0.1
BoneWidthToLengthRatio=0.01
; Force nub to point back to parent at expense of loss of rotation data. Default: 1
ForceRotation=1
ForceRotation=0
; Browse for skeleton on import. Default: 1
BrowseForSkeleton=1
; DefaultName for Skeletons (use if in same directory as imported nif)
DefaultSkeletonName=skeleton.nif
; Create Nubs for final bone in chain (not supported yet)
CreateNubsForBones=1
[AnimationImport]
; Enable Animation Import. Default: 1
EnableAnimations=1
; Replace TCB Rotation with Bezier (workaround for unexpected behavior with TCB rotations)
ReplaceTCBRotationWithBezier=1
; [Applications]
; RootPaths - Semicolon separated list of base directories to use when determining which app the imported file is from
......
No preview for this file type
......@@ -306,61 +306,20 @@ void RenameNode(Interface *gi, LPCTSTR SrcName, LPCTSTR DstName)
if (node != NULL) node->SetName(const_cast<LPTSTR>(DstName));
}
// Locate a TriObject in an Object if it exists
TriObject* GetTriObject(Object *o)
void PosRotScaleNode(INode *n, Matrix3& m3, PosRotScale prs, TimeValue t)
{
if (o && o->CanConvertToType(triObjectClassID))
return (TriObject *)o->ConvertToType(0, triObjectClassID);
while (o->SuperClassID() == GEN_DERIVOB_CLASS_ID && o)
{
IDerivedObject* dobj = (IDerivedObject *)(o);
o = dobj->GetObjRef();
if (o && o->CanConvertToType(triObjectClassID))
return (TriObject *)o->ConvertToType(0, triObjectClassID);
}
return NULL;
}
// Get or Create the Skin Modifier
Modifier *GetSkin(INode *node)
{
Object* pObj = node->GetObjectRef();
if (!pObj) return NULL;
while (pObj->SuperClassID() == GEN_DERIVOB_CLASS_ID)
{
IDerivedObject* pDerObj = (IDerivedObject *)(pObj);
int Idx = 0;
while (Idx < pDerObj->NumModifiers())
{
// Get the modifier.
Modifier* mod = pDerObj->GetModifier(Idx);
if (mod->ClassID() == SKIN_CLASSID)
{
// is this the correct Physique Modifier based on index?
return mod;
}
Idx++;
}
pObj = pDerObj->GetObjRef();
}
IDerivedObject *dobj = CreateDerivedObject(node->GetObjectRef());
//create a skin modifier and add it
Modifier *skinMod = (Modifier*) CreateInstance(OSM_CLASS_ID, SKIN_CLASSID);
dobj->SetAFlag(A_LOCK_TARGET);
dobj->AddModifier(skinMod);
dobj->ClearAFlag(A_LOCK_TARGET);
node->SetObjectRef(dobj);
return skinMod;
Point3 p = m3.GetTrans();
Quat q = m3;
PosRotScaleNode(n, p, q, 1.0f, prs, t);
}
// Set Position and Rotation on a standard controller will need to handle bipeds
// Always in World Transform coordinates
void PositionAndRotateNode(INode *n, Point3 p, Quat& q, PosRotScale prs, TimeValue t)
void PosRotScaleNode(INode *n, Point3 p, Quat& q, float s, PosRotScale prs, TimeValue t)
{
if (Control *c = n->GetTMController()) {
ScaleValue sv(Point3(s,s,s));
// Bipeds are special. And will crash if you dont treat them with care
if ( (c->ClassID() == BIPSLAVE_CONTROL_CLASS_ID)
||(c->ClassID() == BIPBODY_CONTROL_CLASS_ID)
......@@ -369,6 +328,8 @@ void PositionAndRotateNode(INode *n, Point3 p, Quat& q, PosRotScale prs, TimeVal
// Get the Biped Export Interface from the controller
//IBipedExport *BipIface = (IBipedExport *) c->GetInterface(I_BIPINTERFACE);
IOurBipExport *BipIface = (IOurBipExport *) c->GetInterface(I_OURINTERFACE);
if (prs & prsScale)
BipIface->SetBipedScale(sv, t, n);
if (prs & prsRot)
BipIface->SetBipedRotation(q, t, n, 0/*???*/);
if (prs & prsPos)
......@@ -376,6 +337,9 @@ void PositionAndRotateNode(INode *n, Point3 p, Quat& q, PosRotScale prs, TimeVal
}
else
{
if (prs & prsScale)
if (Control *sclCtrl = c->GetScaleController())
sclCtrl->SetValue(t, &sv, 1, CTRL_ABSOLUTE);
if (prs & prsRot)
if (Control *rotCtrl = c->GetRotationController())
rotCtrl->SetValue(t, &q, 1, CTRL_ABSOLUTE);
......@@ -516,13 +480,3 @@ void FindImages(NameValueCollection& images, const string& rootPath, const strin
}
void GoToSkeletonBindPosition(vector<NiNodeRef>& blocks)
{
//Send all skeleton roots to bind position
for (uint i = 0; i < blocks.size(); ++i) {
NiNodeRef node = blocks[i];
if ( node != NULL && node->IsSkeletonRoot() ) {
node->GoToSkeletonBindPosition();
}
}
}
\ No newline at end of file
......@@ -31,6 +31,9 @@ INFO: See Implementation for minimalist comments
#include <color.h>
// Niflib Headers
#include <obj\NiObject.h>
#include <obj\NiAVObject.h>
#include <obj\NiObjectNET.h>
#include <obj\NiNode.h>
#include <nif_math.h>
......@@ -192,8 +195,6 @@ extern string ExpandEnvironment(const string& src);
extern void FindImages(NameValueCollection& images, const string& rootPath, const stringlist& searchpaths, const stringlist& extensions);
extern void RenameNode(Interface *gi, LPCTSTR SrcName, LPCTSTR DstName);
extern TriObject* GetTriObject(Object *o);
extern Modifier *GetSkin(INode *node);
enum PosRotScale
{
......@@ -202,13 +203,37 @@ enum PosRotScale
prsScale = 0x4,
prsDefault = prsPos | prsRot | prsScale,
};
extern void PositionAndRotateNode(INode *n, Point3 p, Quat& q, PosRotScale prs = prsDefault, TimeValue t = 0);
extern void PosRotScaleNode(INode *n, Point3 p, Quat& q, float s, PosRotScale prs = prsDefault, TimeValue t = 0);
extern void PosRotScaleNode(INode *n, Matrix3& m3, PosRotScale prs = prsDefault, TimeValue t = 0);
extern Niflib::NiNodeRef FindNodeByName( const vector<Niflib::NiNodeRef>& blocks, const string& name );
extern std::vector<Niflib::NiNodeRef> SelectNodesByName( const vector<Niflib::NiNodeRef>& blocks, LPCTSTR match);
extern int CountNodesByName( const vector<Niflib::NiNodeRef>& blocks, LPCTSTR match );
extern std::vector<std::string> GetNamesOfNodes( const vector<Niflib::NiNodeRef>& blocks );
extern void GoToSkeletonBindPosition(std::vector<Niflib::NiNodeRef>& blocks);
extern std::vector<Niflib::NiNodeRef> SelectNodesByName( const vector<Niflib::NiNodeRef>& blocks, LPCTSTR match);
struct NodeEquivalence
{
bool operator()(const Niflib::NiNodeRef& lhs, const Niflib::NiNodeRef& rhs) const{
return (!lhs || !rhs) ? (lhs < rhs) : (lhs->GetName() < rhs->GetName());
}
bool operator()(const Niflib::NiNodeRef& lhs, const std::string& rhs) const{
return (lhs->GetName() < rhs);
}
bool operator()(const std::string& lhs, const Niflib::NiNodeRef& rhs) const{
return (lhs < rhs->GetName());
}
};
inline Niflib::NiNodeRef BinarySearch(vector<Niflib::NiNodeRef> &nodes, const string& name)
{
typedef std::pair<vector<Niflib::NiNodeRef>::iterator, vector<Niflib::NiNodeRef>::iterator> NiNodePair;
NiNodePair pair = std::equal_range(nodes.begin(), nodes.end(), name, NodeEquivalence());
if (pair.first != pair.second) {
return (*pair.first);
}
return Niflib::NiNodeRef();
}
// Simple conversion helpers
static inline float TODEG(float x) { return x * 180.0f / PI; }
......@@ -218,7 +243,7 @@ static inline Color TOCOLOR(const Niflib::Color3& c3) {
return Color(c3.r, c3.g, c3.b);
}
static inline Matrix3 TOMATRIX3(const Niflib::Matrix44 &tm, bool invert = true){
static inline Matrix3 TOMATRIX3(const Niflib::Matrix44 &tm, bool invert = false){
Niflib::Vector3 pos; Niflib::Matrix33 rot; float scale;
tm.Decompose(pos, rot, scale);
Matrix3 m(rot.rows[0].data, rot.rows[1].data, rot.rows[2].data, Point3());
......@@ -227,8 +252,39 @@ static inline Matrix3 TOMATRIX3(const Niflib::Matrix44 &tm, bool invert = true){
return m;
}
static inline Quat TOQUAT(const Niflib::Quaternion& q){
return Quat(q.x, q.y, q.z, q.w);
static inline Point3 TOPOINT3(const Niflib::Vector3& v){
return Point3(v.x, v.y, v.z);
}
static inline Quat TOQUAT(const Niflib::Quaternion& q, bool inverse = false){
Quat qt(q.x, q.y, q.z, q.w);
return (inverse) ? qt.Inverse() : qt;
}
static inline AngAxis TOANGAXIS(const Niflib::Quaternion& q, bool inverse = false){
Quat qt(q.x, q.y, q.z, q.w);
if (inverse) qt.Invert();
return AngAxis(q.x, q.y, q.z, q.w);
}
template <typename U, typename T>
inline Niflib::Ref<U> SelectFirstObjectOfType( vector<Niflib::Ref<T> > const & objs ) {
for (vector<Niflib::Ref<T> >::const_iterator itr = objs.begin(), end = objs.end(); itr != end; ++itr) {
Niflib::Ref<U> obj = DynamicCast<U>(*itr);
if (obj) return obj;
}
return Niflib::Ref<U>();
}
template <typename U, typename T>
inline Niflib::Ref<U> SelectFirstObjectOfType( list<Niflib::Ref<T> > const & objs ) {
for (list<Niflib::Ref<T> >::const_iterator itr = objs.begin(), end = objs.end(); itr != end; ++itr) {
Niflib::Ref<U> obj = DynamicCast<U>(*itr);
if (obj) return obj;
}
return Niflib::Ref<U>();
}
#endif // _NIUTILS_H_
\ No newline at end of file
......@@ -190,7 +190,7 @@ bool getMAXScriptValue(ReferenceTarget* obj, LPTSTR name, TimeValue t, T& value)
}
// Get the parameter controller
Control* getMAXScriptController(ReferenceTarget* obj, LPTSTR name, ParamDimension*& dim)
inline Control* getMAXScriptController(ReferenceTarget* obj, LPTSTR name, ParamDimension*& dim)
{
Control* rval = NULL;
assert(obj != NULL);
......@@ -217,7 +217,7 @@ Control* getMAXScriptController(ReferenceTarget* obj, LPTSTR name, ParamDimensio
}
// Set the parameter controller
bool setMAXScriptController(ReferenceTarget* obj, LPTSTR name, Control* control, ParamDimension* dim)
inline bool setMAXScriptController(ReferenceTarget* obj, LPTSTR name, Control* control, ParamDimension* dim)
{
bool rval = false;
assert(obj != NULL);
......
......@@ -21,8 +21,4 @@
#include <commctrl.h>
#include <shlwapi.h>
#include "Max.h"
#include "resource.h"
#include "istdplug.h"
#include "iparamb2.h"
#include "iparamm2.h"
#include "MaxNifImport.h"
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