diff --git a/build/nif.xml b/build/nif.xml
index 34d4aabe..89177232 100644
--- a/build/nif.xml
+++ b/build/nif.xml
@@ -1728,6 +1728,14 @@
Alpha color component.
+
+ A color with alpha (blue, green, red, alpha).
+ Blue color component.
+ Green color component.
+ Red color component.
+ Alpha color component.
+
+
A string that contains the path to a file.
The normal string.
@@ -1767,6 +1775,13 @@
Third coordinate.
+
+ A vector in 3D space (x,y,z).
+ First coordinate.
+ Second coordinate.
+ Third coordinate.
+
+
A vector in 3D space (x,y,z).
First coordinate.
@@ -1789,6 +1804,21 @@
Fourth coordinate.
+
+ A 4-dimensional vector.
+ First coordinate.
+ Second coordinate.
+ Third coordinate.
+ Fourth coordinate.
+
+
+
+ First coordinate.
+ Second coordinate.
+ Third coordinate.
+ Fourth coordinate.
+
+
A quaternion.
The w-coordinate.
@@ -9184,16 +9214,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Vertex coordinate scale * 32767.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
-
+
@@ -9202,7 +9285,7 @@
-
+
diff --git a/src/data/niftypes.h b/src/data/niftypes.h
index b0c76006..f70b8f6e 100644
--- a/src/data/niftypes.h
+++ b/src/data/niftypes.h
@@ -482,6 +482,24 @@ class HalfVector3 : public Vector3
}
};
+class ShortVector3 : public Vector3
+{
+public:
+ //! Default constructor
+ ShortVector3()
+ {
+ xyz[0] = xyz[1] = xyz[2] = 0.0;
+ }
+ //! Constructor
+ ShortVector3( float x, float y, float z ) : Vector3( x, y, z )
+ {
+ }
+
+ ShortVector3( Vector3 v ) : Vector3( v )
+ {
+ }
+};
+
class UshortVector3 : public Vector3
{
public:
@@ -570,6 +588,14 @@ class Vector4
xyzw[2] = v3[2];
xyzw[3] = w;
}
+ inline Vector4 & operator=( const FloatVector4 & v )
+ {
+ xyzw[0] = v[0];
+ xyzw[1] = v[1];
+ xyzw[2] = v[2];
+ xyzw[3] = v[3];
+ return *this;
+ }
//! Add-equals operator
Vector4 & operator+=( const Vector4 & v )
{
@@ -768,6 +794,44 @@ inline Vector3::Vector3( const Vector4 & v4 )
xyz[2] = v4[2];
}
+class ByteVector4 : public Vector4
+{
+public:
+ ByteVector4() : Vector4( FloatVector4( 0.0f, 0.0f, 1.0f, 1.0f ) )
+ {
+ }
+
+ ByteVector4( const std::uint32_t & v ) : Vector4( ( FloatVector4(v) - 127.5f ) / 127.5f )
+ {
+ }
+
+ inline operator std::uint32_t() const
+ {
+ return std::uint32_t( FloatVector4( *this ) * 127.5f + 127.5f );
+ }
+};
+
+class UDecVector4 : public Vector4
+{
+public:
+ UDecVector4() : Vector4( FloatVector4( 0.0f, 0.0f, 1.0f, 1.0f ) )
+ {
+ }
+
+ UDecVector4( const std::uint32_t & v ) : Vector4( FloatVector4::convertX10Y10Z10( v ) )
+ {
+ xyzw[3] = ( !(v & 0x80000000U) ? 1.0f : -1.0f );
+ }
+
+ inline operator std::uint32_t() const
+ {
+ std::uint32_t v = FloatVector4( *this ).convertToX10Y10Z10();
+ if ( xyzw[3] < 0.0f )
+ v = v | 0x80000000U;
+ return v;
+ }
+};
+
//! A quaternion
class Quat
{
@@ -1535,6 +1599,21 @@ class ByteColor4 : public Color4
}
};
+class ByteColor4BGRA : public Color4
+{
+public:
+ //! Default constructor
+ ByteColor4BGRA() { rgba[0] = rgba[1] = rgba[2] = rgba[3] = 1.0; }
+ ByteColor4BGRA( const std::uint32_t & c )
+ : Color4( FloatVector4(c).shuffleValues(0xC6) / 255.0f )
+ {
+ }
+ inline operator std::uint32_t() const
+ {
+ return std::uint32_t( FloatVector4(*this).shuffleValues(0xC6) * 255.0f );
+ }
+};
+
inline Color3::Color3( const Color4 & c4 )
{
@@ -1971,12 +2050,16 @@ inline QDataStream & operator>>( QDataStream & ds, BSVertexDesc & d )
Q_DECLARE_TYPEINFO( Vector2, Q_MOVABLE_TYPE );
Q_DECLARE_TYPEINFO( Vector3, Q_MOVABLE_TYPE );
Q_DECLARE_TYPEINFO( HalfVector3, Q_MOVABLE_TYPE );
+Q_DECLARE_TYPEINFO( ShortVector3, Q_MOVABLE_TYPE );
Q_DECLARE_TYPEINFO( UshortVector3, Q_MOVABLE_TYPE );
Q_DECLARE_TYPEINFO( ByteVector3, Q_MOVABLE_TYPE );
Q_DECLARE_TYPEINFO( Vector4, Q_MOVABLE_TYPE );
+Q_DECLARE_TYPEINFO( ByteVector4, Q_MOVABLE_TYPE );
+Q_DECLARE_TYPEINFO( UDecVector4, Q_MOVABLE_TYPE );
Q_DECLARE_TYPEINFO( Color3, Q_MOVABLE_TYPE );
Q_DECLARE_TYPEINFO( Color4, Q_MOVABLE_TYPE );
Q_DECLARE_TYPEINFO( ByteColor4, Q_MOVABLE_TYPE );
+Q_DECLARE_TYPEINFO( ByteColor4BGRA, Q_MOVABLE_TYPE );
Q_DECLARE_TYPEINFO( Triangle, Q_MOVABLE_TYPE );
Q_DECLARE_TYPEINFO( Quat, Q_MOVABLE_TYPE );
Q_DECLARE_TYPEINFO( Matrix, Q_MOVABLE_TYPE );
diff --git a/src/data/nifvalue.cpp b/src/data/nifvalue.cpp
index 984a30e7..8d2aa822 100644
--- a/src/data/nifvalue.cpp
+++ b/src/data/nifvalue.cpp
@@ -96,6 +96,8 @@ void NifValue::initialize()
typeMap.insert( "Color3", NifValue::tColor3 );
typeMap.insert( "Color4", NifValue::tColor4 );
typeMap.insert( "Vector4", NifValue::tVector4 );
+ typeMap.insert( "ByteVector4", NifValue::tByteVector4 );
+ typeMap.insert( "UDecVector4", NifValue::tUDecVector4 );
typeMap.insert( "Vector3", NifValue::tVector3 );
typeMap.insert( "TBC", NifValue::tVector3 );
typeMap.insert( "Quaternion", NifValue::tQuat );
@@ -122,11 +124,13 @@ void NifValue::initialize()
typeMap.insert( "blob", NifValue::tBlob );
typeMap.insert( "hfloat", NifValue::tHfloat );
typeMap.insert( "HalfVector3", NifValue::tHalfVector3 );
+ typeMap.insert( "ShortVector3", NifValue::tShortVector3 );
typeMap.insert( "UshortVector3", NifValue::tUshortVector3 );
typeMap.insert( "ByteVector3", NifValue::tByteVector3 );
typeMap.insert( "HalfVector2", NifValue::tHalfVector2 );
typeMap.insert( "HalfTexCoord", NifValue::tHalfVector2 );
typeMap.insert( "ByteColor4", NifValue::tByteColor4 );
+ typeMap.insert( "ByteColor4BGRA", NifValue::tByteColor4BGRA );
//typeMap.insert( "BSVertexDesc", NifValue::tBSVertexDesc );
enumMap.clear();
@@ -361,10 +365,13 @@ void NifValue::clear()
{
switch ( typ ) {
case tVector4:
+ case tByteVector4:
+ case tUDecVector4:
delete static_cast( val.data );
break;
case tVector3:
case tHalfVector3:
+ case tShortVector3:
case tUshortVector3:
case tByteVector3:
delete static_cast( val.data );
@@ -408,6 +415,7 @@ void NifValue::clear()
break;
case tColor4:
case tByteColor4:
+ case tByteColor4BGRA:
delete static_cast( val.data );
break;
case tBSVertexDesc:
@@ -439,6 +447,7 @@ void NifValue::changeType( Type t )
return;
case tVector3:
case tHalfVector3:
+ case tShortVector3:
case tUshortVector3:
case tByteVector3:
val.data = new Vector3();
@@ -446,6 +455,10 @@ void NifValue::changeType( Type t )
case tVector4:
val.data = new Vector4();
return;
+ case tByteVector4:
+ case tUDecVector4:
+ val.data = new ByteVector4();
+ return;
case tMatrix:
val.data = new Matrix();
return;
@@ -478,6 +491,7 @@ void NifValue::changeType( Type t )
return;
case tColor4:
case tByteColor4:
+ case tByteColor4BGRA:
val.data = new Color4();
return;
case tByteArray:
@@ -511,11 +525,14 @@ void NifValue::operator=( const NifValue & other )
switch ( typ ) {
case tVector3:
case tHalfVector3:
+ case tShortVector3:
case tUshortVector3:
case tByteVector3:
*static_cast( val.data ) = *static_cast( other.val.data );
return;
case tVector4:
+ case tByteVector4:
+ case tUDecVector4:
*static_cast( val.data ) = *static_cast( other.val.data );
return;
case tMatrix:
@@ -547,6 +564,7 @@ void NifValue::operator=( const NifValue & other )
return;
case tColor4:
case tByteColor4:
+ case tByteColor4BGRA:
*static_cast( val.data ) = *static_cast( other.val.data );
return;
case tByteArray:
@@ -637,6 +655,7 @@ bool NifValue::operator==( const NifValue & other ) const
case tColor4:
case tByteColor4:
+ case tByteColor4BGRA:
{
Color4 * c1 = static_cast(val.data);
Color4 * c2 = static_cast(other.val.data);
@@ -661,6 +680,7 @@ bool NifValue::operator==( const NifValue & other ) const
case tVector3:
case tHalfVector3:
+ case tShortVector3:
case tUshortVector3:
case tByteVector3:
{
@@ -674,6 +694,8 @@ bool NifValue::operator==( const NifValue & other ) const
}
case tVector4:
+ case tByteVector4:
+ case tUDecVector4:
{
Vector4 * vec1 = static_cast(val.data);
Vector4 * vec2 = static_cast(other.val.data);
@@ -865,6 +887,7 @@ bool NifValue::setFromString( const QString & s, const BaseModel * model, const
break;
case tColor4:
case tByteColor4:
+ case tByteColor4BGRA:
static_cast( val.data )->fromQColor( QColor( s ) );
ok = true;
break;
@@ -882,6 +905,8 @@ bool NifValue::setFromString( const QString & s, const BaseModel * model, const
ok = true;
break;
case tVector4:
+ case tByteVector4:
+ case tUDecVector4:
static_cast( val.data )->fromString( s );
ok = true;
break;
@@ -957,6 +982,7 @@ QString NifValue::toString() const
}
case tColor4:
case tByteColor4:
+ case tByteColor4BGRA:
{
Color4 * col = static_cast( val.data );
float r = col->red(), g = col->green(), b = col->blue(), a = col->alpha();
@@ -985,6 +1011,7 @@ QString NifValue::toString() const
}
case tVector3:
case tHalfVector3:
+ case tShortVector3:
case tUshortVector3:
case tByteVector3:
{
@@ -996,6 +1023,8 @@ QString NifValue::toString() const
.arg( NumOrMinMax( (*v)[2], 'f', VECTOR_DECIMALS ) );
}
case tVector4:
+ case tByteVector4:
+ case tUDecVector4:
{
Vector4 * v = static_cast( val.data );
@@ -1151,6 +1180,8 @@ QString NifValue::getTypeDebugStr( NifValue::Type t )
case tMatrix4: typeStr = "Matrix4"; break;
case tVector2: typeStr = "Vector2"; break;
case tVector4: typeStr = "Vector4"; break;
+ case tByteVector4: typeStr = "ByteVector4"; break;
+ case tUDecVector4: typeStr = "UDecVector4"; break;
case tTriangle: typeStr = "Triangle"; break;
case tFileVersion: typeStr = "FileVersion"; break;
case tByteArray: typeStr = "ByteArray"; break;
@@ -1161,10 +1192,12 @@ QString NifValue::getTypeDebugStr( NifValue::Type t )
case tBlob: typeStr = "Blob"; break;
case tHfloat: typeStr = "Hfloat"; break;
case tHalfVector3: typeStr = "HalfVector3"; break;
+ case tShortVector3: typeStr = "ShortVector3"; break;
case tUshortVector3: typeStr = "UshortVector3"; break;
case tByteVector3: typeStr = "ByteVector3"; break;
case tHalfVector2: typeStr = "HalfVector2"; break;
case tByteColor4: typeStr = "ByteColor4"; break;
+ case tByteColor4BGRA: typeStr = "ByteColor4BGRA"; break;
case tBSVertexDesc: typeStr = "BSVertexDesc"; break;
case tNone: typeStr = "None"; break;
default: typeStr = "UNKNOWN"; break;
@@ -1180,6 +1213,7 @@ QColor NifValue::toColor( const BaseModel * model, const NifItem * item ) const
return static_cast( val.data )->toQColor();
case tColor4:
case tByteColor4:
+ case tByteColor4BGRA:
return static_cast( val.data )->toQColor();
default:
if ( model )
diff --git a/src/data/nifvalue.h b/src/data/nifvalue.h
index 3813450c..37a20062 100644
--- a/src/data/nifvalue.h
+++ b/src/data/nifvalue.h
@@ -108,15 +108,26 @@ class NifValue
tLineString,
tChar8String,
// all string types should come between tSizedString and tChar8String
+ // all color types should come between tColor3 and tByteColor4BGRA
tColor3,
tColor4,
+ tByteColor4,
+ tByteColor4BGRA,
+ // all vector3 types should come between tVector3 and tByteVector3
tVector3,
+ tHalfVector3,
+ tShortVector3,
+ tUshortVector3,
+ tByteVector3,
tQuat,
tQuatXYZW,
tMatrix,
tMatrix4,
tVector2,
+ // all vector4 types should come between tVector4 and tUDecVector4
tVector4,
+ tByteVector4,
+ tUDecVector4,
tTriangle,
tFileVersion,
tByteArray,
@@ -126,11 +137,7 @@ class NifValue
tByteMatrix,
tBlob,
tHfloat,
- tHalfVector3,
- tUshortVector3,
- tByteVector3,
tHalfVector2,
- tByteColor4,
tBSVertexDesc,
tNormbyte,
tNone= 0xff
@@ -227,7 +234,7 @@ class NifValue
static QString enumOptionText( const QString & eid, quint32 oval );
/*! Get an option from an option string.
- *
+ *
* @param eid The name of the enum type.
* @param oid The name of the option.
* @param ok Is set to true if successful, is set to false if the option string was not found.
@@ -250,7 +257,7 @@ class NifValue
//! Check if the type of the data is not tNone.
bool isValid() const { return typ != tNone; }
//! Check if the type of the data is a color type (Color3 or Color4 in xml).
- bool isColor() const { return typ == tColor3 || typ == tColor4 || typ == tByteColor4; }
+ bool isColor() const { return typ >= tColor3 && typ <= tByteColor4BGRA; }
//! Check if the type of the data is a count.
bool isCount() const { return (typ >= tBool && typ <= tUInt); }
//! Check if the type of the data is a flag type (Flags in xml).
@@ -269,12 +276,18 @@ class NifValue
bool isString() const { return (typ >= tSizedString && typ <= tChar8String) || typ == tString; }
//! Check if the type of the data is a Vector 4.
bool isVector4() const { return typ == tVector4; }
+ //! Check if the type of the data is ByteVector4.
+ bool isByteVector4() const { return typ == tByteVector4; }
+ //! Check if the type of the data is UDecVector4.
+ bool isUDecVector4() const { return typ == tUDecVector4; }
+ bool isAVector4() const { return typ >= tVector4 && typ <= tUDecVector4; }
//! Check if the type of the data is a Vector 3.
bool isVector3() const { return typ == tVector3; }
//! Check if the type of the data is a Half Vector3.
bool isHalfVector3() const { return typ == tHalfVector3; }
//! Check if the type of the data is a Byte Vector3.
bool isByteVector3() const { return typ == tByteVector3; }
+ bool isAVector3() const { return typ >= tVector3 && typ <= tByteVector3; }
//! Check if the type of the data is a HalfVector2.
bool isHalfVector2() const { return typ == tHalfVector2; }
//! Check if the type of the data is a Vector 2.
@@ -580,11 +593,24 @@ template <> inline Matrix4 NifValue::get( const BaseModel * model, const NifItem
}
template <> inline Vector4 NifValue::get( const BaseModel * model, const NifItem * item ) const
{
- return getType( tVector4, model, item );
+ if ( typ >= tVector4 && typ <= tUDecVector4 )
+ return *static_cast(val.data);
+
+ if ( model )
+ reportConvertToError( model, item, "a Vector4" );
+ return Vector4();
+}
+template <> inline ByteVector4 NifValue::get( const BaseModel * model, const NifItem * item ) const
+{
+ return getType( tByteVector4, model, item );
+}
+template <> inline UDecVector4 NifValue::get( const BaseModel * model, const NifItem * item ) const
+{
+ return getType( tUDecVector4, model, item );
}
template <> inline Vector3 NifValue::get( const BaseModel * model, const NifItem * item ) const
{
- if ( typ == tVector3 || typ == tHalfVector3 )
+ if ( typ >= tVector3 && typ <= tByteVector3 )
return *static_cast(val.data);
if ( model )
@@ -595,6 +621,10 @@ template <> inline HalfVector3 NifValue::get( const BaseModel * model, const Nif
{
return getType( tHalfVector3, model, item );
}
+template <> inline ShortVector3 NifValue::get( const BaseModel * model, const NifItem * item ) const
+{
+ return getType( tShortVector3, model, item );
+}
template <> inline UshortVector3 NifValue::get( const BaseModel * model, const NifItem * item ) const
{
return getType( tUshortVector3, model, item );
@@ -624,9 +654,18 @@ template <> inline ByteColor4 NifValue::get( const BaseModel * model, const NifI
{
return getType( tByteColor4, model, item );
}
+template <> inline ByteColor4BGRA NifValue::get( const BaseModel * model, const NifItem * item ) const
+{
+ return getType( tByteColor4BGRA, model, item );
+}
template <> inline Color4 NifValue::get( const BaseModel * model, const NifItem * item ) const
{
- return getType( tColor4, model, item );
+ if ( typ >= tColor4 && typ <= tByteColor4BGRA )
+ return *static_cast(val.data);
+
+ if ( model )
+ reportConvertToError( model, item, "a Color4" );
+ return Color4();
}
template <> inline Triangle NifValue::get( const BaseModel * model, const NifItem * item ) const
{
@@ -737,6 +776,16 @@ template <> inline bool NifValue::set( const Vector4 & x, const BaseModel * mode
{
return setType( tVector4, x, model, item );
}
+//! Set the data from a ByteVector4. Return true if successful.
+template <> inline bool NifValue::set( const ByteVector4 & x, const BaseModel * model, const NifItem * item )
+{
+ return setType( tByteVector4, x, model, item );
+}
+//! Set the data from a UDecVector4. Return true if successful.
+template <> inline bool NifValue::set( const UDecVector4 & x, const BaseModel * model, const NifItem * item )
+{
+ return setType( tUDecVector4, x, model, item );
+}
//! Set the data from a Vector3. Return true if successful.
template <> inline bool NifValue::set( const Vector3 & x, const BaseModel * model, const NifItem * item )
{
@@ -747,6 +796,11 @@ template <> inline bool NifValue::set( const HalfVector3 & x, const BaseModel *
{
return setType( tHalfVector3, x, model, item );
}
+//! Set the data from a ShortVector3. Return true if successful.
+template <> inline bool NifValue::set( const ShortVector3 & x, const BaseModel * model, const NifItem * item )
+{
+ return setType( tShortVector3, x, model, item );
+}
//! Set the data from a UshortVector3. Return true if successful.
template <> inline bool NifValue::set( const UshortVector3 & x, const BaseModel * model, const NifItem * item )
{
@@ -777,6 +831,11 @@ template <> inline bool NifValue::set( const ByteColor4 & x, const BaseModel * m
{
return setType( tByteColor4, x, model, item );
}
+//! Set the data from a ByteColor4BGRA. Return true if successful.
+template <> inline bool NifValue::set( const ByteColor4BGRA & x, const BaseModel * model, const NifItem * item )
+{
+ return setType( tByteColor4BGRA, x, model, item );
+}
//! Set the data from a Color4. Return true if successful.
template <> inline bool NifValue::set( const Color4 & x, const BaseModel * model, const NifItem * item )
{
diff --git a/src/gl/BSMesh.cpp b/src/gl/BSMesh.cpp
index 8712ae40..44f16448 100644
--- a/src/gl/BSMesh.cpp
+++ b/src/gl/BSMesh.cpp
@@ -194,25 +194,6 @@ QString BSMesh::textStats() const
return QString();
}
-void BSMesh::forMeshIndex(const NifModel* nif, std::function& f)
-{
- for ( int i = 0; i < 4; i++ ) {
- auto meshArray = QModelIndex_child( iMeshes, i );
- bool hasMesh = nif->get( QModelIndex_child( meshArray ) );
- auto mesh = QModelIndex_child( meshArray, 1 );
- if ( hasMesh ) {
- auto meshPath = nif->get(mesh, "Mesh Path");
- if ( !meshPath.startsWith("geometries", Qt::CaseInsensitive) ) {
- meshPath = "geometries\\" + meshPath;
- }
- if ( !meshPath.endsWith(".mesh") ) {
- meshPath += ".mesh";
- }
- f(meshPath, i);
- }
- }
-}
-
int BSMesh::meshCount()
{
return meshes.size();
@@ -250,16 +231,18 @@ void BSMesh::updateImpl(const NifModel* nif, const QModelIndex& index)
iData = index;
iMeshes = nif->getIndex(index, "Meshes");
meshes.clear();
- std::function createMeshFile = [&](const QString& meshPath, int lodLevel) {
- auto mesh = std::make_shared(meshPath, nif);
- if ( mesh->isValid() ) {
- meshes.append(mesh);
- if ( lodLevel > 0 || mesh->lods.size() > 0 )
- emit nif->lodSliderChanged(true);
+ for ( int i = 0; i < 4; i++ ) {
+ auto meshArray = QModelIndex_child( iMeshes, i );
+ bool hasMesh = nif->get( QModelIndex_child( meshArray ) );
+ if ( hasMesh ) {
+ auto mesh = std::make_shared( nif, QModelIndex_child( meshArray, 1 ) );
+ if ( mesh->isValid() ) {
+ meshes.append(mesh);
+ if ( lodLevel > 0 || mesh->lods.size() > 0 )
+ emit nif->lodSliderChanged(true);
+ }
}
- };
-
- forMeshIndex(nif, createMeshFile);
+ }
}
void BSMesh::updateData(const NifModel* nif)
@@ -310,8 +293,8 @@ void BSMesh::updateData(const NifModel* nif)
transColors = mesh->colors;
hasVertexColors = !transColors.empty();
transNorms = mesh->normals;
- transTangents = mesh->bitangents;
transBitangents = mesh->tangents;
+ mesh->calculateBitangents( transTangents );
weightsUNORM = mesh->weights;
gpuLODs = mesh->lods;
diff --git a/src/gl/BSMesh.h b/src/gl/BSMesh.h
index 16158867..da984ee3 100644
--- a/src/gl/BSMesh.h
+++ b/src/gl/BSMesh.h
@@ -32,7 +32,6 @@ class BSMesh : public Shape
QString textStats() const override; // TODO (Gavrant): move to Shape
- void forMeshIndex(const NifModel* nif, std::function& f);
int meshCount();
// end Node
diff --git a/src/io/MeshFile.cpp b/src/io/MeshFile.cpp
index 9b89c041..dfec79fd 100644
--- a/src/io/MeshFile.cpp
+++ b/src/io/MeshFile.cpp
@@ -1,9 +1,10 @@
#include "io/MeshFile.h"
#include "model/nifmodel.h"
+#include "qtcompat.h"
+#include
+#include
#include
-#include
-#include
#include "fp32vec4.hpp"
#if 0
@@ -11,187 +12,401 @@
double snormToDouble(int16_t x) { return x < 0 ? x / double(32768) : x / double(32767); }
#endif
-MeshFile::MeshFile( const QString & filepath, const NifModel * nif )
+MeshFile::MeshFile( const void * data, size_t size )
{
- QString path( QDir::fromNativeSeparators(filepath.toLower()) );
- if ( path.isEmpty() || !nif )
- return;
+ update( data, size );
+}
- if ( nif->getResourceFile( data, path, "geometries", ".mesh" ) && readMesh() > 0 ) {
- qDebug() << "MeshFile created for" << filepath;
- } else {
- qWarning() << "MeshFile creation failed for" << filepath;
- }
+MeshFile::MeshFile( const NifModel * nif, const QString & path )
+{
+ update( nif, path );
}
-bool MeshFile::isValid()
+MeshFile::MeshFile( const NifModel * nif, const QModelIndex & index )
{
- return !data.isNull();
+ update( nif, index );
}
-quint32 MeshFile::readMesh()
+void MeshFile::clear()
{
- if ( data.isEmpty() )
- return 0;
+ if ( !haveData )
+ return;
- QBuffer f(&data);
- if ( f.open(QIODevice::ReadOnly) ) {
- in.setDevice(&f);
- in.setByteOrder(QDataStream::LittleEndian);
- in.setFloatingPointPrecision(QDataStream::SinglePrecision);
+ positions.clear();
+ normals.clear();
+ colors.clear();
+ tangents.clear();
+ bitangentsBasis.clear();
+ haveTexCoord2 = false;
+ coords.clear();
+ weights.clear();
+ weightsPerVertex = 0;
+ triangles.clear();
+ lods.clear();
+
+ haveData = false;
+}
- quint32 magic;
- in >> magic;
- if ( magic > 2U )
- return 0;
+void MeshFile::update( const void * data, size_t size )
+{
+ clear();
+ if ( !( data && size > 0 ) )
+ return;
- quint32 indicesSize;
- in >> indicesSize;
- triangles.resize(indicesSize / 3);
+ QBuffer f;
+ f.setData( reinterpret_cast< const char * >( data ), qsizetype( size ) );
+ if ( !f.open(QIODevice::ReadOnly) )
+ return;
- for ( quint32 i = 0; i < indicesSize / 3; i++ ) {
- Triangle tri;
- in >> tri;
- triangles[i] = tri;
+ QDataStream in;
+ in.setDevice(&f);
+ in.setByteOrder(QDataStream::LittleEndian);
+ in.setFloatingPointPrecision(QDataStream::SinglePrecision);
- }
+ quint32 magic;
+ in >> magic;
+ if ( magic > 2U )
+ return;
- float scale;
- in >> scale;
- if ( scale <= 0.0 )
- return 0; // From RE
+ quint32 indicesSize;
+ in >> indicesSize;
+ triangles.resize( indicesSize / 3 );
+ haveData = true;
- quint32 numWeightsPerVertex;
- in >> numWeightsPerVertex;
- weightsPerVertex = numWeightsPerVertex;
+ for ( quint32 i = 0; i < indicesSize / 3; i++ ) {
+ Triangle tri;
+ in >> tri;
+ triangles[i] = tri;
+ }
- quint32 numPositions;
- in >> numPositions;
- positions.resize(numPositions + positions.count());
+ float scale;
+ in >> scale;
+ if ( scale <= 0.0f ) {
+ clear();
+ return; // From RE
+ }
- for ( int i = 0; i < positions.count(); i++ ) {
- uint32_t xy;
- uint16_t z;
+ quint32 numWeightsPerVertex;
+ in >> numWeightsPerVertex;
+ weightsPerVertex = numWeightsPerVertex;
- in >> xy;
- in >> z;
- FloatVector4 xyz(FloatVector4::convertInt16((std::uint64_t(z) << 32) | xy));
- xyz /= 32767.0f;
- xyz *= scale;
+ quint32 numPositions;
+ in >> numPositions;
+ if ( !numPositions ) {
+ clear();
+ return;
+ }
+ positions.resize( numPositions );
- positions[i] = Vector3(xyz[0], xyz[1], xyz[2]);
- }
+ for ( int i = 0; i < int(numPositions); i++ ) {
+ uint32_t xy;
+ uint16_t z;
- quint32 numCoord1;
- in >> numCoord1;
- coords.resize(numCoord1);
+ in >> xy;
+ in >> z;
+ FloatVector4 xyz(FloatVector4::convertInt16((std::uint64_t(z) << 32) | xy));
+ xyz /= 32767.0f;
+ xyz *= scale;
- for ( int i = 0; i < coords.count(); i++ ) {
- uint32_t uv;
+ positions[i] = Vector3(xyz[0], xyz[1], xyz[2]);
+ }
- in >> uv;
+ quint32 numCoord1;
+ in >> numCoord1;
+ coords.resize( numCoord1 );
- coords[i] = Vector4(FloatVector4::convertFloat16(uv));
- }
+ for ( int i = 0; i < int(numCoord1); i++ ) {
+ uint32_t uv;
- quint32 numCoord2;
- in >> numCoord2;
- numCoord2 = std::min(numCoord2, numCoord1);
- haveTexCoord2 = bool(numCoord2);
+ in >> uv;
- for ( quint32 i = 0; i < numCoord2; i++ ) {
- uint32_t uv;
+ coords[i] = Vector4(FloatVector4::convertFloat16(uv));
+ }
- in >> uv;
- FloatVector4 uv_f(FloatVector4::convertFloat16(uv));
+ quint32 numCoord2;
+ in >> numCoord2;
+ numCoord2 = std::min( numCoord2, numCoord1 );
+ haveTexCoord2 = bool( numCoord2 );
- coords[i][2] = uv_f[0];
- coords[i][3] = uv_f[1];
- }
+ for ( quint32 i = 0; i < numCoord2; i++ ) {
+ uint32_t uv;
- quint32 numColor;
- in >> numColor;
- if ( numColor > 0 ) {
- colors.resize(numColor + colors.count());
- }
- for ( quint32 i = 0; i < numColor; i++ ) {
- uint32_t bgra;
- in >> bgra;
- colors[i] = Color4( ( FloatVector4(bgra) / 255.0f ).shuffleValues( 0xC6 ) ); // 2, 1, 0, 3
- }
+ in >> uv;
+ FloatVector4 uv_f(FloatVector4::convertFloat16(uv));
- quint32 numNormal;
- in >> numNormal;
- if ( numNormal > 0 ) {
- normals.resize(numNormal + normals.count());
- }
- for ( int i = 0; i < normals.count(); i++ ) {
- quint32 n;
- in >> n;
- FloatVector4 v(FloatVector4::convertX10Y10Z10(n));
- normals[i] = Vector3(v[0], v[1], v[2]);
- }
+ coords[i][2] = uv_f[0];
+ coords[i][3] = uv_f[1];
+ }
- quint32 numTangent;
- in >> numTangent;
- if ( numTangent > 0 ) {
- tangents.resize(numTangent + tangents.count());
- tangentsBasis.resize(numTangent + tangentsBasis.count());
- bitangents.resize(numTangent + bitangents.count());
- }
- for ( int i = 0; i < tangents.count(); i++ ) {
- quint32 n;
- in >> n;
- bool b = bool(n & 0x80000000U);
- FloatVector4 v(FloatVector4::convertX10Y10Z10(n));
- tangents[i] = Vector3(v[0], v[1], v[2]);
- // For export
- tangentsBasis[i] = Vector4(v[0], v[1], v[2], (b) ? 1.0 : -1.0);
- if (b)
- v *= -1.0f;
- v = v.crossProduct3(FloatVector4(normals[i][0], normals[i][1], normals[i][2], 0.0f));
- bitangents[i] = Vector3(v[0], v[1], v[2]);
- }
+ quint32 numColor;
+ in >> numColor;
+ if ( numColor > 0 ) {
+ colors.resize( numColor );
+ }
+ for ( quint32 i = 0; i < numColor; i++ ) {
+ uint32_t bgra;
+ in >> bgra;
+ colors[i] = Color4( ( FloatVector4(bgra) / 255.0f ).shuffleValues( 0xC6 ) ); // 2, 1, 0, 3
+ }
+
+ quint32 numNormal;
+ in >> numNormal;
+ if ( numNormal > 0 ) {
+ normals.resize( numNormal );
+ }
+ for ( int i = 0; i < int(numNormal); i++ ) {
+ std::uint32_t n;
+ in >> n;
+ normals[i] = Vector3( UDecVector4(n) );
+ }
- quint32 numWeights;
- in >> numWeights;
- if ( numWeights > 0 && numWeightsPerVertex > 0 ) {
- weights.resize(numWeights / numWeightsPerVertex);
+ quint32 numTangent;
+ in >> numTangent;
+ if ( numTangent > 0 ) {
+ tangents.resize( numTangent );
+ bitangentsBasis.resize( numTangent );
+ }
+ for ( int i = 0; i < int(numTangent); i++ ) {
+ std::uint32_t n;
+ in >> n;
+ UDecVector4 v( n );
+ tangents[i] = Vector3( v );
+ bitangentsBasis[i] = v[3];
+ }
+
+ quint32 numWeights;
+ in >> numWeights;
+ if ( numWeights > 0 && numWeightsPerVertex > 0 ) {
+ weights.resize(numWeights / numWeightsPerVertex);
+ }
+ for ( int i = 0; i < weights.count(); i++ ) {
+ QVector> weightsUNORM;
+ for ( quint32 j = 0; j < 8; j++ ) {
+ if ( j < numWeightsPerVertex ) {
+ quint16 b, w;
+ in >> b;
+ in >> w;
+ weightsUNORM.append({ b, w });
+ } else {
+ weightsUNORM.append({0, 0});
+ }
}
- for ( int i = 0; i < weights.count(); i++ ) {
- QVector> weightsUNORM;
- for ( quint32 j = 0; j < 8; j++ ) {
- if ( j < numWeightsPerVertex ) {
- quint16 b, w;
- in >> b;
- in >> w;
- weightsUNORM.append({ b, w });
- } else {
- weightsUNORM.append({0, 0});
- }
+ weights[i] = BoneWeightsUNorm(weightsUNORM, i);
+ }
+
+ if ( magic ) {
+ quint32 numLODs;
+ in >> numLODs;
+ lods.resize(numLODs);
+ for ( quint32 i = 0; i < numLODs; i++ ) {
+ quint32 indicesSize2;
+ in >> indicesSize2;
+ lods[i].resize(indicesSize2 / 3);
+
+ for ( quint32 j = 0; j < indicesSize2 / 3; j++ ) {
+ Triangle tri;
+ in >> tri;
+ lods[i][j] = tri;
}
- weights[i] = BoneWeightsUNorm(weightsUNORM, i);
}
+ }
+}
- if ( magic ) {
- quint32 numLODs;
- in >> numLODs;
- lods.resize(numLODs);
- for ( quint32 i = 0; i < numLODs; i++ ) {
- quint32 indicesSize2;
- in >> indicesSize2;
- lods[i].resize(indicesSize2 / 3);
-
- for ( quint32 j = 0; j < indicesSize2 / 3; j++ ) {
- Triangle tri;
- in >> tri;
- lods[i][j] = tri;
- }
+void MeshFile::update( const NifModel * nif, const QString & path )
+{
+ clear();
+
+ if ( path.isEmpty() || !nif )
+ return;
+
+ QByteArray data;
+ if ( nif->getResourceFile( data, path, "geometries", ".mesh" ) )
+ update( data.data(), size_t(data.size()) );
+ if ( haveData )
+ qDebug() << "MeshFile created for" << path;
+ else
+ qWarning() << "MeshFile creation failed for" << path;
+}
+
+void MeshFile::update( const NifModel * nif, const QModelIndex & index )
+{
+ clear();
+ if ( !( nif && index.isValid() ) )
+ return;
+ auto meshPath = nif->getIndex( index, "Mesh Path" );
+ if ( meshPath.isValid() ) {
+ update( nif, nif->get( meshPath ) );
+ return;
+ }
+ auto meshData = nif->getIndex( index, "Mesh Data" );
+ if ( !meshData.isValid() )
+ return;
+
+ quint32 magic = nif->get( meshData, "Version" );
+ if ( magic > 2U )
+ return;
+
+ quint32 indicesSize = nif->get( meshData, "Indices Size" );
+ auto trianglesIndex = nif->getIndex( meshData, "Triangles" );
+ if ( !trianglesIndex.isValid() )
+ indicesSize = 0;
+ triangles.resize( indicesSize / 3 );
+ haveData = true;
+
+ for ( int i = 0; i < int(indicesSize / 3); i++ )
+ triangles[i] = nif->get( QModelIndex_child( trianglesIndex, i ) );
+
+ float scale = nif->get( meshData, "Scale" );
+ if ( scale <= 0.0f ) {
+ clear();
+ return; // From RE
+ }
+
+ quint32 numWeightsPerVertex = nif->get( meshData, "Weights Per Vertex" );
+ weightsPerVertex = numWeightsPerVertex;
+
+ quint32 numPositions = nif->get( meshData, "Num Verts" );
+ auto verticesIndex = nif->getIndex( meshData, "Vertices" );
+ if ( !( numPositions && verticesIndex.isValid() ) ) {
+ clear();
+ return;
+ }
+ positions.resize( numPositions );
+
+ for ( int i = 0; i < int(numPositions); i++ ) {
+ Vector3 xyz = nif->get( QModelIndex_child( verticesIndex, i ) );
+ xyz /= 32767.0f;
+ xyz *= scale;
+
+ positions[i] = xyz;
+ }
+
+ quint32 numCoord1 = nif->get( meshData, "Num UVs" );
+ auto uvIndex1 = nif->getIndex( meshData, "UVs" );
+ if ( !uvIndex1.isValid() )
+ numCoord1 = 0;
+ coords.resize( numCoord1 );
+
+ for ( int i = 0; i < int(numCoord1); i++ ) {
+ HalfVector2 uv = nif->get( QModelIndex_child( uvIndex1, i ) );
+
+ coords[i] = Vector4( uv[0], uv[1], 0.0f, 0.0f );
+ }
+
+ quint32 numCoord2 = nif->get( meshData, "Num UVs 2" );
+ auto uvIndex2 = nif->getIndex( meshData, "UVs 2" );
+ if ( !uvIndex2.isValid() )
+ numCoord2 = 0;
+ numCoord2 = std::min( numCoord2, numCoord1 );
+ haveTexCoord2 = bool( numCoord2 );
+
+ for ( quint32 i = 0; i < numCoord2; i++ ) {
+ HalfVector2 uv = nif->get( QModelIndex_child( uvIndex2, i ) );
+
+ coords[i][2] = uv[0];
+ coords[i][3] = uv[1];
+ }
+
+ quint32 numColor = nif->get( meshData, "Num Vertex Colors" );
+ auto colorsIndex = nif->getIndex( meshData, "Vertex Colors" );
+ if ( !colorsIndex.isValid() )
+ numColor = 0;
+ if ( numColor > 0 )
+ colors.resize( numColor );
+ for ( int i = 0; i < int(numColor); i++ )
+ colors[i] = nif->get( QModelIndex_child( colorsIndex, i ) );
+
+ quint32 numNormal = nif->get( meshData, "Num Normals" );
+ auto normalsIndex = nif->getIndex( meshData, "Normals" );
+ if ( !normalsIndex.isValid() )
+ numNormal = 0;
+ if ( numNormal > 0 )
+ normals.resize( numNormal );
+ for ( int i = 0; i < int(numNormal); i++ )
+ normals[i] = Vector3( nif->get( QModelIndex_child( normalsIndex, i ) ) );
+
+ quint32 numTangent = nif->get( meshData, "Num Tangents" );
+ auto tangentsIndex = nif->getIndex( meshData, "Tangents" );
+ if ( !tangentsIndex.isValid() )
+ numTangent = 0;
+ if ( numTangent > 0 ) {
+ tangents.resize( numTangent );
+ bitangentsBasis.resize( numTangent );
+ }
+ for ( int i = 0; i < int(numTangent); i++ ) {
+ Vector4 v = nif->get( QModelIndex_child( tangentsIndex, i ) );
+ tangents[i] = Vector3( v );
+ bitangentsBasis[i] = v[3];
+ }
+
+ quint32 numWeights = nif->get( meshData, "Num Weights" );
+ auto weightsIndex = nif->getIndex( meshData, "Weights" );
+ if ( !weightsIndex.isValid() )
+ numWeights = 0;
+ if ( numWeights > 0 && numWeightsPerVertex > 0 ) {
+ weights.resize(numWeights / numWeightsPerVertex);
+ }
+ int k = 0;
+ for ( int i = 0; i < weights.count(); i++ ) {
+ QVector> weightsUNORM;
+ for ( quint32 j = 0; j < 8; j++ ) {
+ if ( j < numWeightsPerVertex ) {
+ quint16 b = nif->get( QModelIndex_child( weightsIndex, k, 0 ) );
+ quint16 w = nif->get( QModelIndex_child( weightsIndex, k, 1 ) );
+ weightsUNORM.append({ b, w });
+ k++;
+ } else {
+ weightsUNORM.append({0, 0});
}
}
+ weights[i] = BoneWeightsUNorm(weightsUNORM, i);
+ }
- return numPositions;
+ quint32 numLODs = 0;
+ auto lodsIndex = nif->getIndex( meshData, "LODs" );
+ if ( lodsIndex.isValid() )
+ numLODs = nif->get( meshData, "Num LODs" );
+ lods.resize(numLODs);
+ for ( quint32 i = 0; i < numLODs; i++ ) {
+ auto lodIndex = QModelIndex_child( lodsIndex, int(i) );
+ if ( !lodIndex.isValid() )
+ continue;
+ auto trianglesIndex2 = nif->getIndex( lodIndex, "Triangles" );
+ if ( !trianglesIndex2.isValid() )
+ continue;
+ quint32 indicesSize2 = nif->get( lodIndex, "Indices Size" );
+ lods[i].resize(indicesSize2 / 3);
+
+ for ( quint32 j = 0; j < indicesSize2 / 3; j++ )
+ lods[i][j] = nif->get( QModelIndex_child( trianglesIndex2, int(j) ) );
}
+}
- return 0;
+void MeshFile::calculateBitangents( QVector & bitangents ) const
+{
+ bitangents.clear();
+ qsizetype n = tangents.size();
+ bitangents.resize( n );
+ const Vector3 * srcN = normals.data();
+ const Vector3 * srcT = tangents.data();
+ const float * srcB = bitangentsBasis.data();
+ Vector3 * dstB = bitangents.data();
+ qsizetype m = std::min< qsizetype >( n, normals.size() );
+ m = std::min< qsizetype >( m, bitangentsBasis.size() );
+ qsizetype i = 0;
+ for ( ; (i + 1) < m; i++ ) {
+ FloatVector4 t( &(srcT[i][0]) );
+ t = ( t * srcB[i] ).crossProduct3( FloatVector4( &(srcN[i][0]) ) );
+ dstB[i] = Vector3( t[0], t[1], t[2] );
+ }
+ for ( ; i < n; i++ ) {
+ FloatVector4 t( srcT[i][0], srcT[i][1], srcT[i][2], 0.0f );
+ FloatVector4 normal( 0.0f, 0.0f, 1.0f, 0.0f );
+ if ( i < normals.size() )
+ normal = FloatVector4( srcN[i][0], srcN[i][1], srcN[i][2], 0.0f );
+ if ( i < bitangentsBasis.size() )
+ t *= srcB[i];
+ t = t.crossProduct3( normal );
+ dstB[i] = Vector3( t[0], t[1], t[2] );
+ }
}
diff --git a/src/io/MeshFile.h b/src/io/MeshFile.h
index c7eb3c82..e23857b2 100644
--- a/src/io/MeshFile.h
+++ b/src/io/MeshFile.h
@@ -1,10 +1,9 @@
-#pragma once
+#ifndef MESHFILE_H_INCLUDED
+#define MESHFILE_H_INCLUDED
#include "data/niftypes.h"
#include "gl/gltools.h"
-#include
-#include
#include
class NifModel;
@@ -12,11 +11,19 @@ class NifModel;
class MeshFile
{
-
public:
- MeshFile( const QString & path, const NifModel * nif );
+ MeshFile( const void * data, size_t size );
+ MeshFile( const NifModel * nif, const QString & path );
+ // construct from BSMesh structure index, can load .mesh file or internal geometry data
+ MeshFile( const NifModel * nif, const QModelIndex & index );
+
+ void clear();
+
+ void update( const void * data, size_t size );
+ void update( const NifModel * nif, const QString & path );
+ void update( const NifModel * nif, const QModelIndex & index );
- bool isValid();
+ void calculateBitangents( QVector & bitangents ) const;
//! Vertices
QVector positions;
@@ -26,23 +33,26 @@ class MeshFile
QVector colors;
//! Tangents
QVector tangents;
- //! Tangents with bitangent basis (1.0, -1.0)
- QVector tangentsBasis;
- //! Bitangents
- QVector bitangents;
+ //! Bitangents basis (bitangents[i] = cross(tangents[i] * bitangentsBasis[i], normals[i]))
+ QVector bitangentsBasis;
//! UV coordinate sets
bool haveTexCoord2 = false;
QVector coords;
//! Weights
QVector weights;
- quint8 weightsPerVertex;
+ quint8 weightsPerVertex = 0;
//! Triangles
QVector triangles;
//! Skeletal Mesh LOD
QVector> lods;
private:
- QByteArray data;
- QDataStream in;
- quint32 readMesh();
+ bool haveData = false;
+public:
+ inline bool isValid() const
+ {
+ return haveData;
+ }
};
+
+#endif
diff --git a/src/io/nifstream.cpp b/src/io/nifstream.cpp
index 60bef5a6..e1c287d8 100644
--- a/src/io/nifstream.cpp
+++ b/src/io/nifstream.cpp
@@ -176,6 +176,24 @@ bool NifIStream::read( NifValue & val )
Vector3 * v = static_cast(val.val.data);
v->xyz[0] = xf; v->xyz[1] = yf; v->xyz[2] = zf;
+ return (dataStream->status() == QDataStream::Ok);
+ }
+ case NifValue::tShortVector3:
+ {
+ int16_t x, y, z;
+ float xf, yf, zf;
+
+ *dataStream >> x;
+ *dataStream >> y;
+ *dataStream >> z;
+
+ xf = (float) x;
+ yf = (float) y;
+ zf = (float) z;
+
+ Vector3 * v = static_cast(val.val.data);
+ v->xyz[0] = xf; v->xyz[1] = yf; v->xyz[2] = zf;
+
return (dataStream->status() == QDataStream::Ok);
}
case NifValue::tUshortVector3:
@@ -265,6 +283,20 @@ bool NifIStream::read( NifValue & val )
*dataStream >> *v;
return (dataStream->status() == QDataStream::Ok);
}
+ case NifValue::tByteVector4:
+ {
+ std::uint32_t v;
+ *dataStream >> v;
+ (void) new( static_cast(val.val.data) ) ByteVector4( v );
+ return (dataStream->status() == QDataStream::Ok);
+ }
+ case NifValue::tUDecVector4:
+ {
+ std::uint32_t v;
+ *dataStream >> v;
+ (void) new( static_cast(val.val.data) ) UDecVector4( v );
+ return (dataStream->status() == QDataStream::Ok);
+ }
case NifValue::tTriangle:
{
Triangle * t = static_cast(val.val.data);
@@ -301,6 +333,13 @@ bool NifIStream::read( NifValue & val )
(void) new( static_cast(val.val.data) ) ByteColor4( rgba );
return (dataStream->status() == QDataStream::Ok);
}
+ case NifValue::tByteColor4BGRA:
+ {
+ std::uint32_t bgra;
+ *dataStream >> bgra;
+ (void) new( static_cast(val.val.data) ) ByteColor4BGRA( bgra );
+ return (dataStream->status() == QDataStream::Ok);
+ }
case NifValue::tColor4:
{
Color4 * c = static_cast(val.val.data);
@@ -659,6 +698,19 @@ bool NifOStream::write( const NifValue & val )
return device->write( (char*)v, 3 ) == 3;
}
+ case NifValue::tShortVector3:
+ {
+ Vector3 * vec = static_cast(val.val.data);
+ if ( !vec )
+ return false;
+
+ int16_t v[3];
+ v[0] = (int16_t) round(vec->xyz[0]);
+ v[1] = (int16_t) round(vec->xyz[1]);
+ v[2] = (int16_t) round(vec->xyz[2]);
+
+ return device->write( (char*)v, 6 ) == 6;
+ }
case NifValue::tUshortVector3:
{
Vector3 * vec = static_cast(val.val.data);
@@ -720,6 +772,24 @@ bool NifOStream::write( const NifValue & val )
return device->write( (char *)static_cast(val.val.data)->xyz, 12 ) == 12;
case NifValue::tVector4:
return device->write( (char *)static_cast(val.val.data)->xyzw, 16 ) == 16;
+ case NifValue::tByteVector4:
+ {
+ ByteVector4 * vec = static_cast(val.val.data);
+ if ( !vec )
+ return false;
+ char v[4];
+ FileBuffer::writeUInt32Fast( v, std::uint32_t( *vec ) );
+ return device->write( v, 4 ) == 4;
+ }
+ case NifValue::tUDecVector4:
+ {
+ UDecVector4 * vec = static_cast(val.val.data);
+ if ( !vec )
+ return false;
+ char v[4];
+ FileBuffer::writeUInt32Fast( v, std::uint32_t( *vec ) );
+ return device->write( v, 4 ) == 4;
+ }
case NifValue::tTriangle:
return device->write( (char *)static_cast(val.val.data)->v, 6 ) == 6;
case NifValue::tQuat:
@@ -746,6 +816,15 @@ bool NifOStream::write( const NifValue & val )
FileBuffer::writeUInt32Fast( c, std::uint32_t(*color) );
return device->write( c, 4 ) == 4;
}
+ case NifValue::tByteColor4BGRA:
+ {
+ ByteColor4BGRA * color = static_cast(val.val.data);
+ if ( !color )
+ return false;
+ char c[4];
+ FileBuffer::writeUInt32Fast( c, std::uint32_t(*color) );
+ return device->write( c, 4 ) == 4;
+ }
case NifValue::tColor4:
return device->write( (char *)static_cast(val.val.data)->rgba, 16 ) == 16;
case NifValue::tSizedString:
diff --git a/src/lib/importex/gltf.cpp b/src/lib/importex/gltf.cpp
index 92fe1319..f07c9fd9 100644
--- a/src/lib/importex/gltf.cpp
+++ b/src/lib/importex/gltf.cpp
@@ -7,6 +7,7 @@
#include "io/MeshFile.h"
#include "model/nifmodel.h"
#include "message.h"
+#include "libfo76utils/src/filebuf.hpp"
#define TINYGLTF_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION
@@ -285,6 +286,32 @@ bool exportCreateNodes(const NifModel* nif, const Scene* scene, tinygltf::Model&
return true;
}
+static inline void exportFloats( QByteArray & bin, const float * data, size_t n )
+{
+ char buf[16];
+ qsizetype nBytes = 0;
+
+ switch ( n ) {
+ case 4:
+ FileBuffer::writeUInt32Fast( &(buf[12]), std::bit_cast< std::uint32_t >(data[3]) );
+ nBytes += 4;
+ [[fallthrough]];
+ case 3:
+ FileBuffer::writeUInt32Fast( &(buf[8]), std::bit_cast< std::uint32_t >(data[2]) );
+ nBytes += 4;
+ [[fallthrough]];
+ case 2:
+ FileBuffer::writeUInt32Fast( &(buf[4]), std::bit_cast< std::uint32_t >(data[1]) );
+ nBytes += 4;
+ [[fallthrough]];
+ case 1:
+ FileBuffer::writeUInt32Fast( &(buf[0]), std::bit_cast< std::uint32_t >(data[0]) );
+ nBytes += 4;
+ break;
+ }
+
+ bin.append( buf, nBytes );
+}
void exportCreatePrimitive(tinygltf::Model& model, QByteArray& bin, std::shared_ptr mesh, tinygltf::Primitive& prim, std::string attr,
int count, int componentType, int type, quint32& attributeIndex, GltfStore& gltf)
@@ -363,39 +390,29 @@ void exportCreatePrimitive(tinygltf::Model& model, QByteArray& bin, std::shared_
// So, do this for now.
if ( attr == "POSITION" ) {
for ( const auto& v : mesh->positions ) {
- bin.append(reinterpret_cast(&v[0]), sizeof(v[0]));
- bin.append(reinterpret_cast(&v[1]), sizeof(v[1]));
- bin.append(reinterpret_cast(&v[2]), sizeof(v[2]));
+ exportFloats( bin, &(v[0]), 3 );
}
} else if ( attr == "NORMAL" ) {
for ( const auto& v : mesh->normals ) {
- bin.append(reinterpret_cast(&v[0]), sizeof(v[0]));
- bin.append(reinterpret_cast(&v[1]), sizeof(v[1]));
- bin.append(reinterpret_cast(&v[2]), sizeof(v[2]));
+ exportFloats( bin, &(v[0]), 3 );
}
} else if ( attr == "TANGENT" ) {
- for ( const auto& v : mesh->tangentsBasis ) {
- bin.append(reinterpret_cast(&v[0]), sizeof(v[0]));
- bin.append(reinterpret_cast(&v[1]), sizeof(v[1]));
- bin.append(reinterpret_cast(&v[2]), sizeof(v[2]));
- bin.append(reinterpret_cast(&v[3]), sizeof(v[3]));
+ for ( const auto& v : mesh->tangents ) {
+ Vector4 tmp( v );
+ tmp[3] = mesh->bitangentsBasis.at( qsizetype(&v - mesh->tangents.data()) ) * -1.0f;
+ exportFloats( bin, &(tmp[0]), 4 );
}
} else if ( attr == "TEXCOORD_0" ) {
for ( const auto& v : mesh->coords ) {
- bin.append(reinterpret_cast(&v[0]), sizeof(v[0]));
- bin.append(reinterpret_cast(&v[1]), sizeof(v[1]));
+ exportFloats( bin, &(v[0]), 2 );
}
} else if ( attr == "TEXCOORD_1" && mesh->haveTexCoord2 ) {
for ( const auto& v : mesh->coords ) {
- bin.append(reinterpret_cast(&v[2]), sizeof(v[2]));
- bin.append(reinterpret_cast(&v[3]), sizeof(v[3]));
+ exportFloats( bin, &(v[2]), 2 );
}
} else if ( attr == "COLOR_0" ) {
for ( const auto& v : mesh->colors ) {
- bin.append(reinterpret_cast(&v[0]), sizeof(v[0]));
- bin.append(reinterpret_cast(&v[1]), sizeof(v[1]));
- bin.append(reinterpret_cast(&v[2]), sizeof(v[2]));
- bin.append(reinterpret_cast(&v[3]), sizeof(v[3]));
+ exportFloats( bin, &(v[0]), 4 );
}
} else if ( attr == "WEIGHTS_0" ) {
for ( const auto& v : mesh->weights ) {
@@ -450,7 +467,7 @@ bool exportCreatePrimitives(tinygltf::Model& model, QByteArray& bin, const BSMes
exportCreatePrimitive(model, bin, mesh, prim, "POSITION", mesh->positions.size(), TINYGLTF_COMPONENT_TYPE_FLOAT, TINYGLTF_TYPE_VEC3, attributeIndex, gltf);
exportCreatePrimitive(model, bin, mesh, prim, "NORMAL", mesh->normals.size(), TINYGLTF_COMPONENT_TYPE_FLOAT, TINYGLTF_TYPE_VEC3, attributeIndex, gltf);
- exportCreatePrimitive(model, bin, mesh, prim, "TANGENT", mesh->tangentsBasis.size(), TINYGLTF_COMPONENT_TYPE_FLOAT, TINYGLTF_TYPE_VEC4, attributeIndex, gltf);
+ exportCreatePrimitive(model, bin, mesh, prim, "TANGENT", mesh->tangents.size(), TINYGLTF_COMPONENT_TYPE_FLOAT, TINYGLTF_TYPE_VEC4, attributeIndex, gltf);
if ( mesh->coords.size() > 0 ) {
exportCreatePrimitive(model, bin, mesh, prim, "TEXCOORD_0", mesh->coords.size(), TINYGLTF_COMPONENT_TYPE_FLOAT, TINYGLTF_TYPE_VEC2, attributeIndex, gltf);
}
diff --git a/src/model/basemodel.cpp b/src/model/basemodel.cpp
index eecf9a5b..5d496b1f 100644
--- a/src/model/basemodel.cpp
+++ b/src/model/basemodel.cpp
@@ -383,13 +383,11 @@ QVariant BaseModel::data( const QModelIndex & index, int role ) const
return QString( "dec: %1
hex: 0x%2
bin: 0b%3" ).arg( f ).arg( f, 4, 16, QChar( '0' ) ).arg( f, 16, 2, QChar( '0' ) );
}
case NifValue::tVector3:
- return item->get().toHtml();
+ case NifValue::tShortVector3:
case NifValue::tUshortVector3:
- return item->get().toHtml();
case NifValue::tHalfVector3:
- return item->get().toHtml();
case NifValue::tByteVector3:
- return item->get().toHtml();
+ return item->get().toHtml();
case NifValue::tMatrix:
return item->get().toHtml();
case NifValue::tQuat:
@@ -400,12 +398,9 @@ QVariant BaseModel::data( const QModelIndex & index, int role ) const
Color3 c = item->get();
return QString( "R %1
G %2
B %3" ).arg( c[0] ).arg( c[1] ).arg( c[2] );
}
- case NifValue::tByteColor4:
- {
- Color4 c = item->get();
- return QString( "R %1
G %2
B %3
A %4" ).arg( c[0] ).arg( c[1] ).arg( c[2] ).arg( c[3] );
- }
case NifValue::tColor4:
+ case NifValue::tByteColor4:
+ case NifValue::tByteColor4BGRA:
{
Color4 c = item->get();
return QString( "R %1
G %2
B %3
A %4" ).arg( c[0] ).arg( c[1] ).arg( c[2] ).arg( c[3] );
diff --git a/src/model/nifmodel.cpp b/src/model/nifmodel.cpp
index 85301c2d..990593d4 100644
--- a/src/model/nifmodel.cpp
+++ b/src/model/nifmodel.cpp
@@ -1476,13 +1476,11 @@ QVariant NifModel::data( const QModelIndex & index, int role ) const
return QString( "0x%1" )
.arg( item->getCountValue(), 8, 16, QChar( '0' ) );
case NifValue::tVector3:
- return item->get().toHtml();
case NifValue::tHalfVector3:
- return item->get().toHtml();
+ case NifValue::tShortVector3:
case NifValue::tUshortVector3:
- return item->get().toHtml();
case NifValue::tByteVector3:
- return item->get().toHtml();
+ return item->get().toHtml();
case NifValue::tMatrix:
return item->get().toHtml();
case NifValue::tMatrix4:
@@ -1498,23 +1496,16 @@ QVariant NifModel::data( const QModelIndex & index, int role ) const
.arg( c[1] )
.arg( c[2] );
}
- case NifValue::tByteColor4:
- {
- Color4 c = item->get();
- return QString( "R %1\nG %2\nB %3\nA %4" )
- .arg( c[0] )
- .arg( c[1] )
- .arg( c[2] )
- .arg( c[3] );
- }
case NifValue::tColor4:
+ case NifValue::tByteColor4:
+ case NifValue::tByteColor4BGRA:
{
Color4 c = item->get();
return QString( "R %1\nG %2\nB %3\nA %4" )
- .arg( c[0] )
- .arg( c[1] )
- .arg( c[2] )
- .arg( c[3] );
+ .arg( c[0] )
+ .arg( c[1] )
+ .arg( c[2] )
+ .arg( c[3] );
}
default:
break;
diff --git a/src/spells/color.cpp b/src/spells/color.cpp
index 18d24348..8557d91c 100644
--- a/src/spells/color.cpp
+++ b/src/spells/color.cpp
@@ -35,6 +35,9 @@ class spChooseColor final : public Spell
} else if ( typ == NifValue::tByteColor4 ) {
auto col = ColorWheel::choose( nif->get( index ) );
nif->set( index, *static_cast(&col) );
+ } else if ( typ == NifValue::tByteColor4BGRA ) {
+ auto col = ColorWheel::choose( nif->get( index ) );
+ nif->set( index, *static_cast(&col) );
}
diff --git a/src/spells/mesh.cpp b/src/spells/mesh.cpp
index e2bf30f5..6d64a7a7 100644
--- a/src/spells/mesh.cpp
+++ b/src/spells/mesh.cpp
@@ -883,10 +883,7 @@ QModelIndex spUpdateBounds::cast_Starfield( NifModel * nif, const QModelIndex &
mesh = nif->getIndex( mesh, "Mesh" );
if ( !mesh.isValid() )
continue;
- QString meshPath( nif->get( mesh, "Mesh Path" ) );
- if ( meshPath.isEmpty() )
- continue;
- MeshFile meshFile( meshPath, nif );
+ MeshFile meshFile( nif, mesh );
quint32 indicesSize = 0;
quint32 numVerts = 0;
if ( meshFile.isValid() ) {
@@ -901,6 +898,7 @@ QModelIndex spUpdateBounds::cast_Starfield( NifModel * nif, const QModelIndex &
bounds = BoundSphere( meshFile.positions, true );
calculateBoundingBox( bndCenter, bndDims, meshFile.positions );
boundsCalculated = true;
+ // TODO: update bounds in internal geometry data
}
}
diff --git a/src/ui/nifskope.ui b/src/ui/nifskope.ui
index 15749e97..f858a242 100644
--- a/src/ui/nifskope.ui
+++ b/src/ui/nifskope.ui
@@ -92,9 +92,6 @@
-
- false
-
Animation
diff --git a/src/ui/widgets/uvedit.cpp b/src/ui/widgets/uvedit.cpp
index 3c07dd3c..183c6aab 100644
--- a/src/ui/widgets/uvedit.cpp
+++ b/src/ui/widgets/uvedit.cpp
@@ -974,6 +974,7 @@ bool UVWidget::setNifData( NifModel * nifModel, const QModelIndex & nifIndex )
if ( !meshes.isValid() )
return false;
+ QModelIndex sfMeshIndex;
int lodDiff = 255;
for ( int i = 0; i <= 3; i++ ) {
auto mesh = QModelIndex_child( meshes, i );
@@ -985,17 +986,16 @@ bool UVWidget::setNifData( NifModel * nifModel, const QModelIndex & nifIndex )
mesh = nif->getIndex( mesh, "Mesh" );
if ( !mesh.isValid() )
continue;
- QString meshPath( nif->findResourceFile( nif->get( mesh, "Mesh Path" ), "geometries/", ".mesh" ) );
- if ( meshPath.isEmpty() )
- continue;
if ( std::abs( i - sfMeshLOD ) < lodDiff ) {
lodDiff = std::abs( i - sfMeshLOD );
- sfMeshPath = meshPath;
+ if ( nif->getIndex( mesh, "Mesh Path" ).isValid() )
+ sfMeshPath = nif->findResourceFile( nif->get( mesh, "Mesh Path" ), "geometries/", ".mesh" );
+ sfMeshIndex = mesh;
}
}
- if ( sfMeshPath.isEmpty() )
+ if ( !sfMeshIndex.isValid() )
return false;
- MeshFile meshFile( sfMeshPath, nif );
+ MeshFile meshFile( nif, sfMeshIndex );
if ( !( meshFile.isValid() && meshFile.coords.size() > 0 && meshFile.triangles.size() > 0 ) )
return false;
for ( qsizetype i = 0; i < meshFile.coords.size(); i++ )
diff --git a/src/ui/widgets/valueedit.cpp b/src/ui/widgets/valueedit.cpp
index ed578170..81b10369 100644
--- a/src/ui/widgets/valueedit.cpp
+++ b/src/ui/widgets/valueedit.cpp
@@ -56,16 +56,50 @@ ValueEdit::ValueEdit( QWidget * parent ) : QWidget( parent ), typ( NifValue::tNo
bool ValueEdit::canEdit( NifValue::Type t )
{
- return t == NifValue::tByte || t == NifValue::tWord || t == NifValue::tInt || t == NifValue::tFlags
- || t == NifValue::tLink || t == NifValue::tUpLink || t == NifValue::tFloat || t == NifValue::tText
- || t == NifValue::tSizedString || t == NifValue::tSizedString16 || t == NifValue::tLineString
- || t == NifValue::tChar8String || t == NifValue::tShortString || t == NifValue::tStringIndex
- || t == NifValue::tString || t == NifValue::tVector4 || t == NifValue::tVector3 || t == NifValue::tVector2
- || t == NifValue::tColor3 || t == NifValue::tColor4 || t == NifValue::tByteColor4
- || t == NifValue::tMatrix || t == NifValue::tQuat || t == NifValue::tQuatXYZW
- || t == NifValue::tTriangle || t == NifValue::tShort || t == NifValue::tUInt || t == NifValue::tULittle32
- || t == NifValue::tHfloat || t == NifValue::tHalfVector3 || t == NifValue::tByteVector3
- || t == NifValue::tHalfVector2 || t == NifValue::tNormbyte;
+ switch ( t ) {
+ case NifValue::tByte:
+ case NifValue::tWord:
+ case NifValue::tInt:
+ case NifValue::tFlags:
+ case NifValue::tLink:
+ case NifValue::tUpLink:
+ case NifValue::tFloat:
+ case NifValue::tText:
+ case NifValue::tSizedString:
+ case NifValue::tSizedString16:
+ case NifValue::tLineString:
+ case NifValue::tChar8String:
+ case NifValue::tShortString:
+ case NifValue::tStringIndex:
+ case NifValue::tString:
+ case NifValue::tVector4:
+ case NifValue::tByteVector4:
+ case NifValue::tUDecVector4:
+ case NifValue::tVector3:
+ case NifValue::tVector2:
+ case NifValue::tColor3:
+ case NifValue::tColor4:
+ case NifValue::tByteColor4:
+ case NifValue::tByteColor4BGRA:
+ case NifValue::tMatrix:
+ case NifValue::tQuat:
+ case NifValue::tQuatXYZW:
+ case NifValue::tTriangle:
+ case NifValue::tShort:
+ case NifValue::tUInt:
+ case NifValue::tULittle32:
+ case NifValue::tHfloat:
+ case NifValue::tShortVector3:
+ case NifValue::tUshortVector3:
+ case NifValue::tHalfVector3:
+ case NifValue::tByteVector3:
+ case NifValue::tHalfVector2:
+ case NifValue::tNormbyte:
+ return true;
+ default:
+ break;
+ }
+ return false;
}
class CenterLabel final : public QLabel
@@ -258,14 +292,9 @@ void ValueEdit::setValue( const NifValue & v )
// te->setBaseSize( width(), height() * 5);
// edit = te;
//} break;
- case NifValue::tByteColor4:
- {
- ColorEdit * ce = new ColorEdit( this );
- ce->setColor4( v.get( nullptr, nullptr ) );
- edit = ce;
- }
- break;
case NifValue::tColor4:
+ case NifValue::tByteColor4:
+ case NifValue::tByteColor4BGRA:
{
ColorEdit * ce = new ColorEdit( this );
ce->setColor4( v.get( nullptr, nullptr ) );
@@ -280,27 +309,19 @@ void ValueEdit::setValue( const NifValue & v )
}
break;
case NifValue::tVector4:
+ case NifValue::tByteVector4:
+ case NifValue::tUDecVector4:
{
VectorEdit * ve = new VectorEdit( this );
ve->setVector4( v.get( nullptr, nullptr ) );
edit = ve;
}
break;
- case NifValue::tByteVector3:
- {
- VectorEdit * ve = new VectorEdit( this );
- ve->setVector3( v.get( nullptr, nullptr ) );
- edit = ve;
- }
- break;
- case NifValue::tHalfVector3:
- {
- VectorEdit * ve = new VectorEdit( this );
- ve->setVector3( v.get( nullptr, nullptr ) );
- edit = ve;
- }
- break;
case NifValue::tVector3:
+ case NifValue::tHalfVector3:
+ case NifValue::tShortVector3:
+ case NifValue::tUshortVector3:
+ case NifValue::tByteVector3:
{
VectorEdit * ve = new VectorEdit( this );
ve->setVector3( v.get( nullptr, nullptr ) );
@@ -422,6 +443,12 @@ NifValue ValueEdit::getValue() const
case NifValue::tText:
val.setFromString( qobject_cast( edit )->toPlainText(), nullptr, nullptr );
break;
+ case NifValue::tByteColor4BGRA:
+ {
+ auto col = qobject_cast(edit)->getColor4();
+ val.set( *static_cast(&col), nullptr, nullptr );
+ break;
+ }
case NifValue::tByteColor4:
{
auto col = qobject_cast(edit)->getColor4();
@@ -437,6 +464,30 @@ NifValue ValueEdit::getValue() const
case NifValue::tVector4:
val.set( qobject_cast( edit )->getVector4(), nullptr, nullptr );
break;
+ case NifValue::tByteVector4:
+ {
+ auto vec = qobject_cast(edit)->getVector4();
+ val.set( *static_cast(&vec), nullptr, nullptr );
+ break;
+ }
+ case NifValue::tUDecVector4:
+ {
+ auto vec = qobject_cast(edit)->getVector4();
+ val.set( *static_cast(&vec), nullptr, nullptr );
+ break;
+ }
+ case NifValue::tShortVector3:
+ {
+ auto vec = qobject_cast(edit)->getVector3();
+ val.set( *static_cast(&vec), nullptr, nullptr );
+ break;
+ }
+ case NifValue::tUshortVector3:
+ {
+ auto vec = qobject_cast(edit)->getVector3();
+ val.set( *static_cast(&vec), nullptr, nullptr );
+ break;
+ }
case NifValue::tByteVector3:
{
auto vec = qobject_cast(edit)->getVector3();