From 99eca0e0fa40c041cabfcda2089a549fea011281 Mon Sep 17 00:00:00 2001 From: jonwd7 Date: Wed, 21 Aug 2019 15:48:10 -0400 Subject: [PATCH] Import OBJ as bhkNiTriStripsShape Older Bethesda NIF versions (Oblivion, FO3) can use bhkNiTriStripsShape as a Havok shape which is relatively easy to import to from a file. --- src/lib/importex/importex.cpp | 16 ++++--- src/lib/importex/obj.cpp | 83 ++++++++++++++++++++++------------- 2 files changed, 63 insertions(+), 36 deletions(-) diff --git a/src/lib/importex/importex.cpp b/src/lib/importex/importex.cpp index a253c9c34..d362a0a96 100644 --- a/src/lib/importex/importex.cpp +++ b/src/lib/importex/importex.cpp @@ -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 ); @@ -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 ) @@ -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" ) ) diff --git a/src/lib/importex/obj.cpp b/src/lib/importex/obj.cpp index 5e3b1515b..beb923feb 100644 --- a/src/lib/importex/obj.cpp +++ b/src/lib/importex/obj.cpp @@ -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; @@ -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 ) ); @@ -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 ); @@ -749,13 +758,15 @@ void importObj( NifModel * nif, const QModelIndex & index ) bool first_tri_shape = true; QMapIterator *> 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; @@ -986,7 +997,7 @@ void importObj( NifModel * nif, const QModelIndex & index ) nif->set( iData, "Radius", radius ); nif->set( iData, "Unknown Short 2", 0x4000 ); - } else if ( nif->getVersionNumber() == 0x14000005 ) { + } else if ( nif->getUserVersion2() > 0 ) { // create experimental havok collision mesh QVector verts; QVector norms; @@ -994,7 +1005,9 @@ void importObj( NifModel * nif, const QModelIndex & index ) QVector points; - foreach ( ObjFace oface, *( it.value() ) ) { + shapecount++; + + for ( const ObjFace & oface : *( it.value() ) ) { Triangle tri; for ( int t = 0; t < 3; t++ ) { @@ -1029,7 +1042,7 @@ void importObj( NifModel * nif, const QModelIndex & index ) nif->setArray( iData, "Normals", norms ); Vector3 center; - foreach ( Vector3 v, verts ) { + for ( const Vector3 & v : verts ) { center += v; } @@ -1038,7 +1051,7 @@ void importObj( NifModel * nif, const QModelIndex & index ) nif->set( iData, "Center", center ); float radius = 0; - foreach ( Vector3 v, verts ) { + for ( const Vector3 & v : verts ) { float d = ( center - v ).length(); if ( d > radius ) @@ -1047,7 +1060,7 @@ void importObj( NifModel * nif, const QModelIndex & index ) nif->set( iData, "Radius", radius ); // do not stitch, because it looks better in the cs - QVector > strips = stripify( triangles, false ); + QVector > strips = stripify( triangles ); nif->set( iData, "Num Strips", strips.count() ); nif->set( iData, "Has Points", 1 ); @@ -1060,7 +1073,7 @@ void importObj( NifModel * nif, const QModelIndex & index ) nif->updateArray( iPoints ); int x = 0; int z = 0; - foreach ( QVector strip, strips ) { + for ( const QVector & strip : strips ) { nif->set( iLengths.child( x, 0 ), strip.count() ); QModelIndex iStrip = iPoints.child( x, 0 ); nif->updateArray( iStrip ); @@ -1071,25 +1084,34 @@ void importObj( NifModel * nif, const QModelIndex & index ) nif->set( 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( iShape, "Unknown Floats 1", { 0.1f, 0.0f } ); - nif->setArray( iShape, "Unknown Ints 1", { 0, 0, 0, 0, 1 } ); - nif->set( iShape, "Scale", { 1.0, 1.0, 1.0 } ); - addLink( nif, iShape, "Strips Data", nif->getBlockNumber( iData ) ); - nif->set( iShape, "Num Data Layers", 1 ); - nif->updateArray( iShape, "Data Layers" ); - nif->setArray( 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( 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( iStripsShape, "Num Filters", shapecount ); + nif->updateArray( iStripsShape, "Filters" ); + } } spTangentSpace TSpacer; @@ -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 );