Skip to content

Commit

Permalink
Import OBJ as bhkNiTriStripsShape
Browse files Browse the repository at this point in the history
Older Bethesda NIF versions (Oblivion, FO3) can use bhkNiTriStripsShape as a Havok shape which is relatively easy to import to from a file.
  • Loading branch information
hexabits committed Aug 21, 2019
1 parent e14f6d6 commit 99eca0e
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 36 deletions.
16 changes: 10 additions & 6 deletions src/lib/importex/importex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

void exportObj( const NifModel * nif, const QModelIndex & index );
void exportCol( const NifModel * nif, QFileInfo );
void importObj( NifModel * nif, const QModelIndex & index );
void importObj( NifModel * nif, const QModelIndex & index, bool collision = false );
void import3ds( NifModel * nif, const QModelIndex & index );


Expand All @@ -53,6 +53,7 @@ void NifSkope::fillImportExportMenus()
//mExport->addAction( tr( "Export .DAE" ) );
//mImport->addAction( tr( "Import .3DS" ) );
mImport->addAction( tr( "Import .OBJ" ) );
mImport->addAction( tr( "Import .OBJ as Collision" ) );
}

void NifSkope::sltImportExport( QAction * a )
Expand Down Expand Up @@ -80,18 +81,21 @@ void NifSkope::sltImportExport( QAction * a )
mImport->setDisabled( true );
return;
} else {
if ( nif->getUserVersion2() >= 100 )
mImport->setDisabled( true );
else
mImport->setDisabled( false );

mImport->setDisabled( false );
mExport->setDisabled( false );

if ( nif->getUserVersion2() >= 100 )
mImport->actions().at(0)->setDisabled( true );
else if ( nif->getUserVersion2() == 0 )
mImport->actions().at(1)->setDisabled( true );
}

if ( a->text() == tr( "Export .OBJ" ) )
exportObj( nif, index );
else if ( a->text() == tr( "Import .OBJ" ) )
importObj( nif, index );
else if ( a->text() == tr( "Import .OBJ as Collision" ) )
importObj( nif, index, true );
//else if ( a->text() == tr( "Import .3DS" ) )
// import3ds( nif, index );
//else if ( a->text() == tr( "Export .DAE" ) )
Expand Down
83 changes: 53 additions & 30 deletions src/lib/importex/obj.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -562,12 +562,12 @@ static void addLink( NifModel * nif, const QModelIndex & iBlock, const QString &
nif->setLink( iArray.child( numIndices, 0 ), link );
}

void importObj( NifModel * nif, const QModelIndex & index )
void importObj( NifModel * nif, const QModelIndex & index, bool collision )
{
//--Determine how the file will import, and be sure the user wants to continue--//

// If no existing node is selected, create a group node. Otherwise use selected node
QPersistentModelIndex iNode, iShape, iMaterial, iData, iTexProp, iTexSource;
QPersistentModelIndex iNode, iShape, iStripsShape, iMaterial, iData, iTexProp, iTexSource;
QModelIndex iBlock = nif->getBlock( index );
bool cBSShaderPPLightingProperty = false;

Expand All @@ -579,7 +579,9 @@ void importObj( NifModel * nif, const QModelIndex & index )

if ( iBlock.isValid() && nif->inherits( iBlock, "NiNode" ) ) {
iNode = iBlock;
} else if ( iBlock.isValid() && nif->itemName( iBlock ) == "NiTriShape" ) {
} else if ( iBlock.isValid()
&& (nif->itemName( iBlock ) == "NiTriShape"
|| (collision && nif->inherits( iBlock, "BSTriShape" ))) ) {
iShape = iBlock;
//Find parent of NiTriShape
int par_num = nif->getParent( nif->getBlockNumber( iBlock ) );
Expand Down Expand Up @@ -625,14 +627,21 @@ void importObj( NifModel * nif, const QModelIndex & index )

QString question;

if ( iNode.isValid() == true ) {
if ( iShape.isValid() == true ) {
question = tr( "NiTriShape selected. The first imported mesh will replace the selected one." );
if ( !collision ) {
if ( iNode.isValid() ) {
if ( iShape.isValid() ) {
question = tr( "NiTriShape selected. The first imported mesh will replace the selected one." );
} else {
question = tr( "NiNode selected. Meshes will be attached to the selected node." );
}
} else {
question = tr( "NiNode selected. Meshes will be attached to the selected node." );
question = tr( "No NiNode or NiTriShape selected. Meshes will be imported to the root of the file." );
}
} else {
question = tr( "No NiNode or NiTriShape selected. Meshes will be imported to the root of the file." );
if ( iNode.isValid() || iShape.isValid() ) {
question = tr( "The Havok collision will be added to this object." );
}
question = tr( "The Havok collision will be added to the root of the file." );
}

int result = QMessageBox::question( 0, tr( "Import OBJ" ), question, QMessageBox::Ok, QMessageBox::Cancel );
Expand Down Expand Up @@ -749,13 +758,15 @@ void importObj( NifModel * nif, const QModelIndex & index )
bool first_tri_shape = true;
QMapIterator<QString, QVector<ObjFace> *> it( ofaces );

nif->holdUpdates( true );

while ( it.hasNext() ) {
it.next();

if ( !it.value()->count() )
continue;

if ( it.key() != "collision" ) {
if ( !collision ) {
//If we are on the first shape, and one was selected in the 3D view, use the existing one
bool newiShape = false;

Expand Down Expand Up @@ -986,15 +997,17 @@ void importObj( NifModel * nif, const QModelIndex & index )
nif->set<float>( iData, "Radius", radius );

nif->set<int>( iData, "Unknown Short 2", 0x4000 );
} else if ( nif->getVersionNumber() == 0x14000005 ) {
} else if ( nif->getUserVersion2() > 0 ) {
// create experimental havok collision mesh
QVector<Vector3> verts;
QVector<Vector3> norms;
QVector<Triangle> triangles;

QVector<ObjPoint> points;

foreach ( ObjFace oface, *( it.value() ) ) {
shapecount++;

for ( const ObjFace & oface : *( it.value() ) ) {
Triangle tri;

for ( int t = 0; t < 3; t++ ) {
Expand Down Expand Up @@ -1029,7 +1042,7 @@ void importObj( NifModel * nif, const QModelIndex & index )
nif->setArray<Vector3>( iData, "Normals", norms );

Vector3 center;
foreach ( Vector3 v, verts ) {
for ( const Vector3 & v : verts ) {
center += v;
}

Expand All @@ -1038,7 +1051,7 @@ void importObj( NifModel * nif, const QModelIndex & index )

nif->set<Vector3>( iData, "Center", center );
float radius = 0;
foreach ( Vector3 v, verts ) {
for ( const Vector3 & v : verts ) {
float d = ( center - v ).length();

if ( d > radius )
Expand All @@ -1047,7 +1060,7 @@ void importObj( NifModel * nif, const QModelIndex & index )
nif->set<float>( iData, "Radius", radius );

// do not stitch, because it looks better in the cs
QVector<QVector<quint16> > strips = stripify( triangles, false );
QVector<QVector<quint16> > strips = stripify( triangles );

nif->set<int>( iData, "Num Strips", strips.count() );
nif->set<int>( iData, "Has Points", 1 );
Expand All @@ -1060,7 +1073,7 @@ void importObj( NifModel * nif, const QModelIndex & index )
nif->updateArray( iPoints );
int x = 0;
int z = 0;
foreach ( QVector<quint16> strip, strips ) {
for ( const QVector<quint16> & strip : strips ) {
nif->set<int>( iLengths.child( x, 0 ), strip.count() );
QModelIndex iStrip = iPoints.child( x, 0 );
nif->updateArray( iStrip );
Expand All @@ -1071,25 +1084,34 @@ void importObj( NifModel * nif, const QModelIndex & index )
nif->set<int>( iData, "Num Triangles", z );
}

QPersistentModelIndex iShape = nif->insertNiBlock( "bhkNiTriStripsShape" );
if ( shapecount == 1 ) {
iStripsShape = nif->insertNiBlock( "bhkNiTriStripsShape" );

// For some reason need to update all the fixed arrays...
nif->updateArray( iStripsShape, "Unused" );

nif->setArray<float>( iShape, "Unknown Floats 1", { 0.1f, 0.0f } );
nif->setArray<int>( iShape, "Unknown Ints 1", { 0, 0, 0, 0, 1 } );
nif->set<Vector3>( iShape, "Scale", { 1.0, 1.0, 1.0 } );
addLink( nif, iShape, "Strips Data", nif->getBlockNumber( iData ) );
nif->set<int>( iShape, "Num Data Layers", 1 );
nif->updateArray( iShape, "Data Layers" );
nif->setArray<int>( iShape, "Data Layers", { 1 } );
QPersistentModelIndex iBody = nif->insertNiBlock( "bhkRigidBody" );
nif->setLink( iBody, "Shape", nif->getBlockNumber( iStripsShape ) );
for( int i = 0; i < nif->rowCount( iBody ); i++ ) {
auto iChild = iBody.child( i, 0 );
if ( nif->isArray( iChild ) )
nif->updateArray( iChild );
}

QPersistentModelIndex iBody = nif->insertNiBlock( "bhkRigidBody" );
nif->setLink( iBody, "Shape", nif->getBlockNumber( iShape ) );
QPersistentModelIndex iObject = nif->insertNiBlock( "bhkCollisionObject" );

QPersistentModelIndex iObject = nif->insertNiBlock( "bhkCollisionObject" );
nif->setLink( iObject, "Parent", nif->getBlockNumber( iNode ) );
nif->set<int>( iObject, "Unknown Short", 1 );
nif->setLink( iObject, "Body", nif->getBlockNumber( iBody ) );
QPersistentModelIndex iParent = (iShape.isValid()) ? iShape : iNode;
nif->setLink( iObject, "Parent", nif->getBlockNumber( iParent ) );
nif->setLink( iObject, "Body", nif->getBlockNumber( iBody ) );

nif->setLink( iNode, "Collision Object", nif->getBlockNumber( iObject ) );
nif->setLink( iParent, "Collision Object", nif->getBlockNumber( iObject ) );
}

if ( shapecount >= 1 ) {
addLink( nif, iStripsShape, "Strips Data", nif->getBlockNumber( iData ) );
nif->set<int>( iStripsShape, "Num Filters", shapecount );
nif->updateArray( iStripsShape, "Filters" );
}
}

spTangentSpace TSpacer;
Expand All @@ -1098,6 +1120,7 @@ void importObj( NifModel * nif, const QModelIndex & index )
//Finished with the first shape which is the only one that can import over the top of existing data
first_tri_shape = false;
}
nif->holdUpdates( false );

qDeleteAll( ofaces );

Expand Down

0 comments on commit 99eca0e

Please sign in to comment.