2018-04-30 11:31:09 +00:00
# include <QFile>
# include <QQuaternion>
# include <QByteArray>
# include <QDataStream>
2018-05-07 16:08:19 +00:00
# include <QFileInfo>
# include <QDir>
2020-03-18 14:17:09 +00:00
# include <QtCore/qbuffer.h>
2018-10-25 09:18:04 +00:00
# include "glbfile.h"
2018-04-30 11:31:09 +00:00
# include "version.h"
2018-10-25 00:19:38 +00:00
# include "util.h"
2018-09-18 03:17:35 +00:00
# include "jointnodetree.h"
2020-04-07 23:15:20 +00:00
# include "model.h"
2018-04-30 11:31:09 +00:00
// Play with glTF online:
// https://gltf-viewer.donmccurdy.com/
// https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_020_Skins.md
// https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_004_ScenesNodes.md#global-transforms-of-nodes
// http://quaternions.online/
// https://en.m.wikipedia.org/wiki/Rotation_formalisms_in_three_dimensions?wprov=sfla1
2018-10-26 23:04:45 +00:00
bool GlbFileWriter : : m_enableComment = false ;
2018-06-01 04:38:16 +00:00
2020-11-17 14:31:51 +00:00
GlbFileWriter : : GlbFileWriter ( Object & object ,
2020-11-22 07:42:33 +00:00
const std : : vector < RigBone > * resultRigBones ,
const std : : map < int , RigVertexWeights > * resultRigWeights ,
2018-10-25 09:18:04 +00:00
const QString & filename ,
2018-10-27 02:29:51 +00:00
QImage * textureImage ,
2018-11-18 00:16:26 +00:00
QImage * normalImage ,
QImage * ormImage ,
2018-10-27 02:29:51 +00:00
const std : : vector < std : : pair < QString , std : : vector < std : : pair < float , JointNodeTree > > > > * motions ) :
2020-12-19 08:12:50 +00:00
m_filename ( filename )
2018-04-30 11:31:09 +00:00
{
2020-11-17 14:31:51 +00:00
const std : : vector < std : : vector < QVector3D > > * triangleVertexNormals = object . triangleVertexNormals ( ) ;
2018-10-26 23:04:45 +00:00
if ( m_outputNormal ) {
m_outputNormal = nullptr ! = triangleVertexNormals ;
}
2020-11-17 14:31:51 +00:00
const std : : vector < std : : vector < QVector2D > > * triangleVertexUvs = object . triangleVertexUvs ( ) ;
2018-10-26 23:04:45 +00:00
if ( m_outputUv ) {
m_outputUv = nullptr ! = triangleVertexUvs ;
}
2018-10-27 02:29:51 +00:00
if ( m_outputAnimation ) {
m_outputAnimation = nullptr ! = motions & & ! motions - > empty ( ) ;
}
2018-10-26 23:04:45 +00:00
2018-10-25 09:18:04 +00:00
QDataStream binStream ( & m_binByteArray , QIODevice : : WriteOnly ) ;
binStream . setFloatingPointPrecision ( QDataStream : : SinglePrecision ) ;
binStream . setByteOrder ( QDataStream : : LittleEndian ) ;
2018-04-30 11:31:09 +00:00
2018-10-25 09:18:04 +00:00
auto alignBin = [ this , & binStream ] {
while ( 0 ! = this - > m_binByteArray . size ( ) % 4 ) {
binStream < < ( quint8 ) 0 ;
}
} ;
QDataStream jsonStream ( & m_jsonByteArray , QIODevice : : WriteOnly ) ;
jsonStream . setFloatingPointPrecision ( QDataStream : : SinglePrecision ) ;
jsonStream . setByteOrder ( QDataStream : : LittleEndian ) ;
2018-04-30 11:31:09 +00:00
2018-10-25 09:18:04 +00:00
auto alignJson = [ this , & jsonStream ] {
while ( 0 ! = this - > m_jsonByteArray . size ( ) % 4 ) {
jsonStream < < ( quint8 ) ' ' ;
2018-04-30 11:31:09 +00:00
}
} ;
int bufferViewIndex = 0 ;
2018-06-15 05:34:41 +00:00
int bufferViewFromOffset ;
2018-04-30 11:31:09 +00:00
2018-10-21 23:29:01 +00:00
JointNodeTree jointNodeTree ( resultRigBones ) ;
const auto & boneNodes = jointNodeTree . nodes ( ) ;
2018-09-18 03:17:35 +00:00
2018-09-14 09:45:05 +00:00
m_json [ " asset " ] [ " version " ] = " 2.0 " ;
m_json [ " asset " ] [ " generator " ] = APP_NAME " " APP_HUMAN_VER ;
m_json [ " scenes " ] [ 0 ] [ " nodes " ] = { 0 } ;
2018-10-27 02:29:51 +00:00
constexpr int skeletonNodeStartIndex = 2 ;
2018-09-14 09:45:05 +00:00
if ( resultRigBones & & resultRigWeights & & ! resultRigBones - > empty ( ) ) {
m_json [ " nodes " ] [ 0 ] [ " children " ] = {
1 ,
skeletonNodeStartIndex
} ;
m_json [ " nodes " ] [ 1 ] [ " mesh " ] = 0 ;
m_json [ " nodes " ] [ 1 ] [ " skin " ] = 0 ;
m_json [ " skins " ] [ 0 ] [ " joints " ] = { } ;
2020-11-09 10:46:06 +00:00
const QQuaternion noneRotation ;
2018-09-18 03:17:35 +00:00
for ( size_t i = 0 ; i < boneNodes . size ( ) ; i + + ) {
2020-11-09 10:46:06 +00:00
const auto & bone = ( * resultRigBones ) [ i ] ;
2018-09-14 09:45:05 +00:00
m_json [ " skins " ] [ 0 ] [ " joints " ] + = skeletonNodeStartIndex + i ;
2018-09-18 03:17:35 +00:00
m_json [ " nodes " ] [ skeletonNodeStartIndex + i ] [ " name " ] = boneNodes [ i ] . name . toUtf8 ( ) . constData ( ) ;
2018-09-14 09:45:05 +00:00
m_json [ " nodes " ] [ skeletonNodeStartIndex + i ] [ " translation " ] = {
2020-11-09 10:46:06 +00:00
boneNodes [ i ] . bindTranslation . x ( ) ,
boneNodes [ i ] . bindTranslation . y ( ) ,
boneNodes [ i ] . bindTranslation . z ( )
2018-09-14 09:45:05 +00:00
} ;
m_json [ " nodes " ] [ skeletonNodeStartIndex + i ] [ " rotation " ] = {
2020-11-09 10:46:06 +00:00
noneRotation . x ( ) ,
noneRotation . y ( ) ,
noneRotation . z ( ) ,
noneRotation . scalar ( )
2018-09-14 09:45:05 +00:00
} ;
2018-09-18 03:17:35 +00:00
if ( ! boneNodes [ i ] . children . empty ( ) ) {
2018-09-14 09:45:05 +00:00
m_json [ " nodes " ] [ skeletonNodeStartIndex + i ] [ " children " ] = { } ;
2018-09-18 03:17:35 +00:00
for ( const auto & it : boneNodes [ i ] . children ) {
2018-09-14 09:45:05 +00:00
m_json [ " nodes " ] [ skeletonNodeStartIndex + i ] [ " children " ] + = skeletonNodeStartIndex + it ;
}
}
}
m_json [ " skins " ] [ 0 ] [ " skeleton " ] = skeletonNodeStartIndex ;
m_json [ " skins " ] [ 0 ] [ " inverseBindMatrices " ] = bufferViewIndex ;
2018-10-25 09:18:04 +00:00
bufferViewFromOffset = ( int ) m_binByteArray . size ( ) ;
2018-09-14 09:45:05 +00:00
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " buffer " ] = 0 ;
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " byteOffset " ] = bufferViewFromOffset ;
2018-09-18 03:17:35 +00:00
for ( auto i = 0u ; i < boneNodes . size ( ) ; i + + ) {
const float * floatArray = boneNodes [ i ] . inverseBindMatrix . constData ( ) ;
2018-09-14 09:45:05 +00:00
for ( auto j = 0u ; j < 16 ; j + + ) {
2018-10-25 09:18:04 +00:00
binStream < < ( float ) floatArray [ j ] ;
2018-09-14 09:45:05 +00:00
}
}
2018-10-25 09:18:04 +00:00
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " byteLength " ] = m_binByteArray . size ( ) - bufferViewFromOffset ;
Q_ASSERT ( ( int ) boneNodes . size ( ) * 16 * sizeof ( float ) = = m_binByteArray . size ( ) - bufferViewFromOffset ) ;
alignBin ( ) ;
2018-09-14 09:45:05 +00:00
if ( m_enableComment )
m_json [ " accessors " ] [ bufferViewIndex ] [ " __comment " ] = QString ( " /accessors/%1: mat " ) . arg ( QString : : number ( bufferViewIndex ) ) . toUtf8 ( ) . constData ( ) ;
m_json [ " accessors " ] [ bufferViewIndex ] [ " bufferView " ] = bufferViewIndex ;
m_json [ " accessors " ] [ bufferViewIndex ] [ " byteOffset " ] = 0 ;
m_json [ " accessors " ] [ bufferViewIndex ] [ " componentType " ] = 5126 ;
2018-09-18 03:17:35 +00:00
m_json [ " accessors " ] [ bufferViewIndex ] [ " count " ] = boneNodes . size ( ) ;
2018-09-14 09:45:05 +00:00
m_json [ " accessors " ] [ bufferViewIndex ] [ " type " ] = " MAT4 " ;
bufferViewIndex + + ;
} else {
m_json [ " nodes " ] [ 0 ] [ " mesh " ] = 0 ;
}
2018-10-26 23:04:45 +00:00
std : : vector < QVector3D > triangleVertexPositions ;
2018-11-17 23:02:12 +00:00
std : : vector < size_t > triangleVertexOldIndices ;
2020-11-17 14:31:51 +00:00
for ( const auto & triangleIndices : object . triangles ) {
2018-10-26 23:04:45 +00:00
for ( size_t j = 0 ; j < 3 ; + + j ) {
2018-11-17 23:02:12 +00:00
triangleVertexOldIndices . push_back ( triangleIndices [ j ] ) ;
2020-11-17 14:31:51 +00:00
triangleVertexPositions . push_back ( object . vertices [ triangleIndices [ j ] ] ) ;
2018-10-26 23:04:45 +00:00
}
}
2018-04-30 11:31:09 +00:00
int primitiveIndex = 0 ;
2018-10-26 23:04:45 +00:00
if ( ! triangleVertexPositions . empty ( ) ) {
2018-04-30 11:31:09 +00:00
m_json [ " meshes " ] [ 0 ] [ " primitives " ] [ primitiveIndex ] [ " indices " ] = bufferViewIndex ;
m_json [ " meshes " ] [ 0 ] [ " primitives " ] [ primitiveIndex ] [ " material " ] = primitiveIndex ;
2018-06-01 04:38:16 +00:00
int attributeIndex = 0 ;
m_json [ " meshes " ] [ 0 ] [ " primitives " ] [ primitiveIndex ] [ " attributes " ] [ " POSITION " ] = bufferViewIndex + ( + + attributeIndex ) ;
if ( m_outputNormal )
m_json [ " meshes " ] [ 0 ] [ " primitives " ] [ primitiveIndex ] [ " attributes " ] [ " NORMAL " ] = bufferViewIndex + ( + + attributeIndex ) ;
2018-06-15 05:34:41 +00:00
if ( m_outputUv )
m_json [ " meshes " ] [ 0 ] [ " primitives " ] [ primitiveIndex ] [ " attributes " ] [ " TEXCOORD_0 " ] = bufferViewIndex + ( + + attributeIndex ) ;
2018-09-14 09:45:05 +00:00
if ( resultRigWeights & & ! resultRigWeights - > empty ( ) ) {
m_json [ " meshes " ] [ 0 ] [ " primitives " ] [ primitiveIndex ] [ " attributes " ] [ " JOINTS_0 " ] = bufferViewIndex + ( + + attributeIndex ) ;
m_json [ " meshes " ] [ 0 ] [ " primitives " ] [ primitiveIndex ] [ " attributes " ] [ " WEIGHTS_0 " ] = bufferViewIndex + ( + + attributeIndex ) ;
}
2018-11-18 00:16:26 +00:00
int textureIndex = 0 ;
m_json [ " materials " ] [ primitiveIndex ] [ " pbrMetallicRoughness " ] [ " baseColorTexture " ] [ " index " ] = textureIndex + + ;
2020-04-07 23:15:20 +00:00
m_json [ " materials " ] [ primitiveIndex ] [ " pbrMetallicRoughness " ] [ " metallicFactor " ] = Model : : m_defaultMetalness ;
m_json [ " materials " ] [ primitiveIndex ] [ " pbrMetallicRoughness " ] [ " roughnessFactor " ] = Model : : m_defaultRoughness ;
2020-11-18 10:26:46 +00:00
if ( object . alphaEnabled )
2020-02-27 09:57:09 +00:00
m_json [ " materials " ] [ primitiveIndex ] [ " alphaMode " ] = " BLEND " ;
2018-11-18 00:16:26 +00:00
if ( normalImage ) {
m_json [ " materials " ] [ primitiveIndex ] [ " normalTexture " ] [ " index " ] = textureIndex + + ;
}
if ( ormImage ) {
m_json [ " materials " ] [ primitiveIndex ] [ " occlusionTexture " ] [ " index " ] = textureIndex ;
m_json [ " materials " ] [ primitiveIndex ] [ " pbrMetallicRoughness " ] [ " metallicRoughnessTexture " ] [ " index " ] = textureIndex ;
m_json [ " materials " ] [ primitiveIndex ] [ " pbrMetallicRoughness " ] [ " metallicFactor " ] = 1.0 ;
m_json [ " materials " ] [ primitiveIndex ] [ " pbrMetallicRoughness " ] [ " roughnessFactor " ] = 1.0 ;
textureIndex + + ;
}
2018-04-30 11:31:09 +00:00
primitiveIndex + + ;
2018-10-25 09:18:04 +00:00
bufferViewFromOffset = ( int ) m_binByteArray . size ( ) ;
2018-10-26 23:04:45 +00:00
for ( size_t index = 0 ; index < triangleVertexPositions . size ( ) ; index + = 3 ) {
binStream < < ( quint16 ) index < < ( quint16 ) ( index + 1 ) < < ( quint16 ) ( index + 2 ) ;
2018-04-30 11:31:09 +00:00
}
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " buffer " ] = 0 ;
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " byteOffset " ] = bufferViewFromOffset ;
2018-10-26 23:04:45 +00:00
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " byteLength " ] = ( int ) triangleVertexPositions . size ( ) * sizeof ( quint16 ) ;
2018-04-30 11:31:09 +00:00
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " target " ] = 34963 ;
2018-10-26 23:04:45 +00:00
Q_ASSERT ( ( int ) triangleVertexPositions . size ( ) * sizeof ( quint16 ) = = m_binByteArray . size ( ) - bufferViewFromOffset ) ;
2018-10-25 09:18:04 +00:00
alignBin ( ) ;
2018-06-01 04:38:16 +00:00
if ( m_enableComment )
2018-11-17 23:02:12 +00:00
m_json [ " accessors " ] [ bufferViewIndex ] [ " __comment " ] = QString ( " /accessors/%1: triangle indices " ) . arg ( QString : : number ( bufferViewIndex ) ) . toUtf8 ( ) . constData ( ) ;
2018-04-30 11:31:09 +00:00
m_json [ " accessors " ] [ bufferViewIndex ] [ " bufferView " ] = bufferViewIndex ;
m_json [ " accessors " ] [ bufferViewIndex ] [ " byteOffset " ] = 0 ;
m_json [ " accessors " ] [ bufferViewIndex ] [ " componentType " ] = 5123 ;
2018-10-26 23:04:45 +00:00
m_json [ " accessors " ] [ bufferViewIndex ] [ " count " ] = triangleVertexPositions . size ( ) ;
2018-04-30 11:31:09 +00:00
m_json [ " accessors " ] [ bufferViewIndex ] [ " type " ] = " SCALAR " ;
bufferViewIndex + + ;
2018-10-25 09:18:04 +00:00
bufferViewFromOffset = ( int ) m_binByteArray . size ( ) ;
2018-04-30 11:31:09 +00:00
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " buffer " ] = 0 ;
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " byteOffset " ] = bufferViewFromOffset ;
float minX = 100 ;
float maxX = - 100 ;
float minY = 100 ;
float maxY = - 100 ;
float minZ = 100 ;
float maxZ = - 100 ;
2018-10-26 23:04:45 +00:00
for ( const auto & position : triangleVertexPositions ) {
if ( position . x ( ) < minX )
minX = position . x ( ) ;
if ( position . x ( ) > maxX )
maxX = position . x ( ) ;
if ( position . y ( ) < minY )
minY = position . y ( ) ;
if ( position . y ( ) > maxY )
maxY = position . y ( ) ;
if ( position . z ( ) < minZ )
minZ = position . z ( ) ;
if ( position . z ( ) > maxZ )
maxZ = position . z ( ) ;
binStream < < ( float ) position . x ( ) < < ( float ) position . y ( ) < < ( float ) position . z ( ) ;
2018-04-30 11:31:09 +00:00
}
2018-10-26 23:04:45 +00:00
Q_ASSERT ( ( int ) triangleVertexPositions . size ( ) * 3 * sizeof ( float ) = = m_binByteArray . size ( ) - bufferViewFromOffset ) ;
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " byteLength " ] = triangleVertexPositions . size ( ) * 3 * sizeof ( float ) ;
2018-04-30 11:31:09 +00:00
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " target " ] = 34962 ;
2018-10-25 09:18:04 +00:00
alignBin ( ) ;
2018-06-01 04:38:16 +00:00
if ( m_enableComment )
m_json [ " accessors " ] [ bufferViewIndex ] [ " __comment " ] = QString ( " /accessors/%1: xyz " ) . arg ( QString : : number ( bufferViewIndex ) ) . toUtf8 ( ) . constData ( ) ;
2018-04-30 11:31:09 +00:00
m_json [ " accessors " ] [ bufferViewIndex ] [ " bufferView " ] = bufferViewIndex ;
m_json [ " accessors " ] [ bufferViewIndex ] [ " byteOffset " ] = 0 ;
m_json [ " accessors " ] [ bufferViewIndex ] [ " componentType " ] = 5126 ;
2018-10-26 23:04:45 +00:00
m_json [ " accessors " ] [ bufferViewIndex ] [ " count " ] = triangleVertexPositions . size ( ) ;
2018-04-30 11:31:09 +00:00
m_json [ " accessors " ] [ bufferViewIndex ] [ " type " ] = " VEC3 " ;
m_json [ " accessors " ] [ bufferViewIndex ] [ " max " ] = { maxX , maxY , maxZ } ;
m_json [ " accessors " ] [ bufferViewIndex ] [ " min " ] = { minX , minY , minZ } ;
bufferViewIndex + + ;
2018-06-01 04:38:16 +00:00
if ( m_outputNormal ) {
2018-10-26 23:04:45 +00:00
bufferViewFromOffset = ( int ) m_binByteArray . size ( ) ;
2018-06-01 04:38:16 +00:00
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " buffer " ] = 0 ;
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " byteOffset " ] = bufferViewFromOffset ;
QStringList normalList ;
2018-10-26 23:04:45 +00:00
for ( const auto & normals : ( * triangleVertexNormals ) ) {
for ( const auto & it : normals ) {
binStream < < ( float ) it . x ( ) < < ( float ) it . y ( ) < < ( float ) it . z ( ) ;
if ( m_enableComment & & m_outputNormal )
normalList . append ( QString ( " <%1,%2,%3> " ) . arg ( QString : : number ( it . x ( ) ) ) . arg ( QString : : number ( it . y ( ) ) ) . arg ( QString : : number ( it . z ( ) ) ) ) ;
}
2018-06-01 04:38:16 +00:00
}
2018-10-26 23:04:45 +00:00
Q_ASSERT ( ( int ) triangleVertexNormals - > size ( ) * 3 * 3 * sizeof ( float ) = = m_binByteArray . size ( ) - bufferViewFromOffset ) ;
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " byteLength " ] = triangleVertexNormals - > size ( ) * 3 * 3 * sizeof ( float ) ;
2018-06-01 04:38:16 +00:00
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " target " ] = 34962 ;
2018-10-26 23:04:45 +00:00
alignBin ( ) ;
2018-06-01 04:38:16 +00:00
if ( m_enableComment )
m_json [ " accessors " ] [ bufferViewIndex ] [ " __comment " ] = QString ( " /accessors/%1: normal %2 " ) . arg ( QString : : number ( bufferViewIndex ) ) . arg ( normalList . join ( " " ) ) . toUtf8 ( ) . constData ( ) ;
m_json [ " accessors " ] [ bufferViewIndex ] [ " bufferView " ] = bufferViewIndex ;
m_json [ " accessors " ] [ bufferViewIndex ] [ " byteOffset " ] = 0 ;
m_json [ " accessors " ] [ bufferViewIndex ] [ " componentType " ] = 5126 ;
2018-10-26 23:04:45 +00:00
m_json [ " accessors " ] [ bufferViewIndex ] [ " count " ] = triangleVertexNormals - > size ( ) * 3 ;
2018-06-01 04:38:16 +00:00
m_json [ " accessors " ] [ bufferViewIndex ] [ " type " ] = " VEC3 " ;
bufferViewIndex + + ;
2018-10-26 23:04:45 +00:00
}
2018-05-10 09:16:22 +00:00
2018-06-15 05:34:41 +00:00
if ( m_outputUv ) {
2018-10-25 09:18:04 +00:00
bufferViewFromOffset = ( int ) m_binByteArray . size ( ) ;
2018-06-15 05:34:41 +00:00
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " buffer " ] = 0 ;
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " byteOffset " ] = bufferViewFromOffset ;
2018-10-26 23:04:45 +00:00
for ( const auto & uvs : ( * triangleVertexUvs ) ) {
for ( const auto & it : uvs )
binStream < < ( float ) it . x ( ) < < ( float ) it . y ( ) ;
2018-06-15 05:34:41 +00:00
}
2018-10-25 09:18:04 +00:00
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " byteLength " ] = m_binByteArray . size ( ) - bufferViewFromOffset ;
alignBin ( ) ;
2018-06-15 05:34:41 +00:00
if ( m_enableComment )
m_json [ " accessors " ] [ bufferViewIndex ] [ " __comment " ] = QString ( " /accessors/%1: uv " ) . arg ( QString : : number ( bufferViewIndex ) ) . toUtf8 ( ) . constData ( ) ;
m_json [ " accessors " ] [ bufferViewIndex ] [ " bufferView " ] = bufferViewIndex ;
m_json [ " accessors " ] [ bufferViewIndex ] [ " byteOffset " ] = 0 ;
m_json [ " accessors " ] [ bufferViewIndex ] [ " componentType " ] = 5126 ;
2018-10-26 23:04:45 +00:00
m_json [ " accessors " ] [ bufferViewIndex ] [ " count " ] = triangleVertexUvs - > size ( ) * 3 ;
2018-06-15 05:34:41 +00:00
m_json [ " accessors " ] [ bufferViewIndex ] [ " type " ] = " VEC2 " ;
bufferViewIndex + + ;
}
2018-09-14 09:45:05 +00:00
if ( resultRigWeights & & ! resultRigWeights - > empty ( ) ) {
2018-10-25 09:18:04 +00:00
bufferViewFromOffset = ( int ) m_binByteArray . size ( ) ;
2018-09-14 09:45:05 +00:00
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " buffer " ] = 0 ;
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " byteOffset " ] = bufferViewFromOffset ;
QStringList boneList ;
int weightItIndex = 0 ;
2018-11-17 23:02:12 +00:00
for ( const auto & oldIndex : triangleVertexOldIndices ) {
2018-09-14 09:45:05 +00:00
auto i = 0u ;
if ( m_enableComment )
boneList . append ( QString ( " %1:< " ) . arg ( QString : : number ( weightItIndex ) ) ) ;
auto findWeight = resultRigWeights - > find ( oldIndex ) ;
if ( findWeight ! = resultRigWeights - > end ( ) ) {
for ( ; i < MAX_WEIGHT_NUM ; i + + ) {
2018-11-17 23:02:12 +00:00
quint16 nodeIndex = ( quint16 ) findWeight - > second . boneIndices [ i ] ;
2018-10-25 09:18:04 +00:00
binStream < < ( quint16 ) nodeIndex ;
2018-09-14 09:45:05 +00:00
if ( m_enableComment )
boneList . append ( QString ( " %1 " ) . arg ( nodeIndex ) ) ;
}
}
for ( ; i < MAX_WEIGHT_NUM ; i + + ) {
2018-10-25 09:18:04 +00:00
binStream < < ( quint16 ) 0 ;
2018-09-14 09:45:05 +00:00
if ( m_enableComment )
boneList . append ( QString ( " %1 " ) . arg ( 0 ) ) ;
}
if ( m_enableComment )
boneList . append ( QString ( " > " ) ) ;
weightItIndex + + ;
}
2018-10-25 09:18:04 +00:00
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " byteLength " ] = m_binByteArray . size ( ) - bufferViewFromOffset ;
alignBin ( ) ;
2018-09-14 09:45:05 +00:00
if ( m_enableComment )
2018-11-17 23:02:12 +00:00
m_json [ " accessors " ] [ bufferViewIndex ] [ " __comment " ] = QString ( " /accessors/%1: bone indices %2 " ) . arg ( QString : : number ( bufferViewIndex ) ) . arg ( boneList . join ( " " ) ) . toUtf8 ( ) . constData ( ) ;
2018-09-14 09:45:05 +00:00
m_json [ " accessors " ] [ bufferViewIndex ] [ " bufferView " ] = bufferViewIndex ;
m_json [ " accessors " ] [ bufferViewIndex ] [ " byteOffset " ] = 0 ;
m_json [ " accessors " ] [ bufferViewIndex ] [ " componentType " ] = 5123 ;
2018-11-17 23:02:12 +00:00
m_json [ " accessors " ] [ bufferViewIndex ] [ " count " ] = triangleVertexOldIndices . size ( ) ;
2018-09-14 09:45:05 +00:00
m_json [ " accessors " ] [ bufferViewIndex ] [ " type " ] = " VEC4 " ;
bufferViewIndex + + ;
2018-10-25 09:18:04 +00:00
bufferViewFromOffset = ( int ) m_binByteArray . size ( ) ;
2018-09-14 09:45:05 +00:00
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " buffer " ] = 0 ;
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " byteOffset " ] = bufferViewFromOffset ;
QStringList weightList ;
weightItIndex = 0 ;
2018-11-17 23:02:12 +00:00
for ( const auto & oldIndex : triangleVertexOldIndices ) {
2018-09-14 09:45:05 +00:00
auto i = 0u ;
if ( m_enableComment )
weightList . append ( QString ( " %1:< " ) . arg ( QString : : number ( weightItIndex ) ) ) ;
auto findWeight = resultRigWeights - > find ( oldIndex ) ;
if ( findWeight ! = resultRigWeights - > end ( ) ) {
for ( ; i < MAX_WEIGHT_NUM ; i + + ) {
2018-09-18 03:17:35 +00:00
float weight = ( float ) findWeight - > second . boneWeights [ i ] ;
2018-10-25 09:18:04 +00:00
binStream < < ( float ) weight ;
2018-09-14 09:45:05 +00:00
if ( m_enableComment )
weightList . append ( QString ( " %1 " ) . arg ( QString : : number ( ( float ) weight ) ) ) ;
}
}
for ( ; i < MAX_WEIGHT_NUM ; i + + ) {
2018-10-25 09:18:04 +00:00
binStream < < ( float ) 0.0 ;
2018-09-14 09:45:05 +00:00
if ( m_enableComment )
weightList . append ( QString ( " %1 " ) . arg ( QString : : number ( 0.0 ) ) ) ;
}
if ( m_enableComment )
weightList . append ( QString ( " > " ) ) ;
weightItIndex + + ;
}
2018-10-25 09:18:04 +00:00
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " byteLength " ] = m_binByteArray . size ( ) - bufferViewFromOffset ;
alignBin ( ) ;
2018-09-14 09:45:05 +00:00
if ( m_enableComment )
m_json [ " accessors " ] [ bufferViewIndex ] [ " __comment " ] = QString ( " /accessors/%1: bone weights %2 " ) . arg ( QString : : number ( bufferViewIndex ) ) . arg ( weightList . join ( " " ) ) . toUtf8 ( ) . constData ( ) ;
m_json [ " accessors " ] [ bufferViewIndex ] [ " bufferView " ] = bufferViewIndex ;
m_json [ " accessors " ] [ bufferViewIndex ] [ " byteOffset " ] = 0 ;
m_json [ " accessors " ] [ bufferViewIndex ] [ " componentType " ] = 5126 ;
2018-11-17 23:02:12 +00:00
m_json [ " accessors " ] [ bufferViewIndex ] [ " count " ] = triangleVertexOldIndices . size ( ) ;
2018-09-14 09:45:05 +00:00
m_json [ " accessors " ] [ bufferViewIndex ] [ " type " ] = " VEC4 " ;
bufferViewIndex + + ;
}
2018-06-15 05:34:41 +00:00
}
2018-10-27 02:29:51 +00:00
if ( m_outputAnimation ) {
for ( int animationIndex = 0 ; animationIndex < ( int ) motions - > size ( ) ; + + animationIndex ) {
const auto & motion = ( * motions ) [ animationIndex ] ;
m_json [ " animations " ] [ animationIndex ] [ " name " ] = motion . first . toUtf8 ( ) . constData ( ) ;
int input = bufferViewIndex ;
bufferViewFromOffset = ( int ) m_binByteArray . size ( ) ;
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " buffer " ] = 0 ;
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " byteOffset " ] = bufferViewFromOffset ;
float minTime = 1000000.0 ;
float maxTime = 0.0 ;
QStringList timeList ;
float timePoint = 0 ;
for ( const auto & keyframe : motion . second ) {
binStream < < ( float ) timePoint ;
if ( timePoint < minTime )
minTime = timePoint ;
if ( timePoint > maxTime )
maxTime = timePoint ;
if ( m_enableComment )
timeList . append ( QString : : number ( timePoint ) ) ;
timePoint + = keyframe . first ;
}
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " byteLength " ] = m_binByteArray . size ( ) - bufferViewFromOffset ;
alignBin ( ) ;
if ( m_enableComment )
m_json [ " accessors " ] [ bufferViewIndex ] [ " __comment " ] = QString ( " /accessors/%1: times %2 " ) . arg ( QString : : number ( bufferViewIndex ) ) . arg ( timeList . join ( " " ) ) . toUtf8 ( ) . constData ( ) ;
m_json [ " accessors " ] [ bufferViewIndex ] [ " bufferView " ] = bufferViewIndex ;
m_json [ " accessors " ] [ bufferViewIndex ] [ " byteOffset " ] = 0 ;
m_json [ " accessors " ] [ bufferViewIndex ] [ " componentType " ] = 5126 ;
m_json [ " accessors " ] [ bufferViewIndex ] [ " count " ] = motion . second . size ( ) ;
m_json [ " accessors " ] [ bufferViewIndex ] [ " type " ] = " SCALAR " ;
m_json [ " accessors " ] [ bufferViewIndex ] [ " max " ] [ 0 ] = maxTime ;
m_json [ " accessors " ] [ bufferViewIndex ] [ " min " ] [ 0 ] = minTime ;
bufferViewIndex + + ;
std : : set < int > rotatedJoints ;
std : : set < int > translatedJoints ;
for ( const auto & keyframe : motion . second ) {
for ( int i = 0 ; i < ( int ) keyframe . second . nodes ( ) . size ( ) & & i < ( int ) boneNodes . size ( ) ; + + i ) {
const auto & src = boneNodes [ i ] ;
const auto & dest = keyframe . second . nodes ( ) [ i ] ;
if ( ! qFuzzyCompare ( src . rotation , dest . rotation ) )
rotatedJoints . insert ( i ) ;
if ( ! qFuzzyCompare ( src . translation , dest . translation ) )
translatedJoints . insert ( i ) ;
}
}
int sampler = 0 ;
int channel = 0 ;
for ( const auto & jointIndex : rotatedJoints ) {
int output = bufferViewIndex ;
bufferViewFromOffset = ( int ) m_binByteArray . size ( ) ;
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " buffer " ] = 0 ;
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " byteOffset " ] = bufferViewFromOffset ;
QStringList rotationList ;
for ( int frame = 0 ; frame < ( int ) motion . second . size ( ) ; frame + + ) {
const auto & keyframe = motion . second [ frame ] ;
const auto & rotation = keyframe . second . nodes ( ) [ jointIndex ] . rotation ;
float x = rotation . x ( ) ;
float y = rotation . y ( ) ;
float z = rotation . z ( ) ;
float w = rotation . scalar ( ) ;
binStream < < ( float ) x < < ( float ) y < < ( float ) z < < ( float ) w ;
if ( m_enableComment )
rotationList . append ( QString ( " %1:<%2,%3,%4,%5> " ) . arg ( QString : : number ( frame ) ) . arg ( QString : : number ( x ) ) . arg ( QString : : number ( y ) ) . arg ( QString : : number ( z ) ) . arg ( QString : : number ( w ) ) ) ;
}
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " byteLength " ] = m_binByteArray . size ( ) - bufferViewFromOffset ;
alignBin ( ) ;
if ( m_enableComment )
m_json [ " accessors " ] [ bufferViewIndex ] [ " __comment " ] = QString ( " /accessors/%1: rotation %2 " ) . arg ( QString : : number ( bufferViewIndex ) ) . arg ( rotationList . join ( " " ) ) . toUtf8 ( ) . constData ( ) ;
m_json [ " accessors " ] [ bufferViewIndex ] [ " bufferView " ] = bufferViewIndex ;
m_json [ " accessors " ] [ bufferViewIndex ] [ " byteOffset " ] = 0 ;
m_json [ " accessors " ] [ bufferViewIndex ] [ " componentType " ] = 5126 ;
m_json [ " accessors " ] [ bufferViewIndex ] [ " count " ] = motion . second . size ( ) ;
m_json [ " accessors " ] [ bufferViewIndex ] [ " type " ] = " VEC4 " ;
bufferViewIndex + + ;
m_json [ " animations " ] [ animationIndex ] [ " samplers " ] [ sampler ] [ " input " ] = input ;
m_json [ " animations " ] [ animationIndex ] [ " samplers " ] [ sampler ] [ " interpolation " ] = " LINEAR " ;
m_json [ " animations " ] [ animationIndex ] [ " samplers " ] [ sampler ] [ " output " ] = output ;
m_json [ " animations " ] [ animationIndex ] [ " channels " ] [ channel ] [ " sampler " ] = sampler ;
m_json [ " animations " ] [ animationIndex ] [ " channels " ] [ channel ] [ " target " ] [ " node " ] = skeletonNodeStartIndex + jointIndex ;
m_json [ " animations " ] [ animationIndex ] [ " channels " ] [ channel ] [ " target " ] [ " path " ] = " rotation " ;
sampler + + ;
channel + + ;
for ( const auto & jointIndex : translatedJoints ) {
int output = bufferViewIndex ;
bufferViewFromOffset = ( int ) m_binByteArray . size ( ) ;
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " buffer " ] = 0 ;
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " byteOffset " ] = bufferViewFromOffset ;
for ( int frame = 0 ; frame < ( int ) motion . second . size ( ) ; frame + + ) {
const auto & keyframe = motion . second [ frame ] ;
const auto & translation = keyframe . second . nodes ( ) [ jointIndex ] . translation ;
binStream < < ( float ) translation . x ( ) < < ( float ) translation . y ( ) < < ( float ) translation . z ( ) ;
}
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " byteLength " ] = m_binByteArray . size ( ) - bufferViewFromOffset ;
alignBin ( ) ;
if ( m_enableComment )
m_json [ " accessors " ] [ bufferViewIndex ] [ " __comment " ] = QString ( " /accessors/%1: translation " ) . arg ( QString : : number ( bufferViewIndex ) ) . toUtf8 ( ) . constData ( ) ;
m_json [ " accessors " ] [ bufferViewIndex ] [ " bufferView " ] = bufferViewIndex ;
m_json [ " accessors " ] [ bufferViewIndex ] [ " byteOffset " ] = 0 ;
m_json [ " accessors " ] [ bufferViewIndex ] [ " componentType " ] = 5126 ;
m_json [ " accessors " ] [ bufferViewIndex ] [ " count " ] = motion . second . size ( ) ;
m_json [ " accessors " ] [ bufferViewIndex ] [ " type " ] = " VEC3 " ;
bufferViewIndex + + ;
m_json [ " animations " ] [ animationIndex ] [ " samplers " ] [ sampler ] [ " input " ] = input ;
m_json [ " animations " ] [ animationIndex ] [ " samplers " ] [ sampler ] [ " interpolation " ] = " LINEAR " ;
m_json [ " animations " ] [ animationIndex ] [ " samplers " ] [ sampler ] [ " output " ] = output ;
m_json [ " animations " ] [ animationIndex ] [ " channels " ] [ channel ] [ " sampler " ] = sampler ;
m_json [ " animations " ] [ animationIndex ] [ " channels " ] [ channel ] [ " target " ] [ " node " ] = skeletonNodeStartIndex + jointIndex ;
m_json [ " animations " ] [ animationIndex ] [ " channels " ] [ channel ] [ " target " ] [ " path " ] = " translation " ;
sampler + + ;
channel + + ;
}
}
}
}
2018-11-18 00:16:26 +00:00
m_json [ " samplers " ] [ 0 ] [ " magFilter " ] = 9729 ;
m_json [ " samplers " ] [ 0 ] [ " minFilter " ] = 9987 ;
m_json [ " samplers " ] [ 0 ] [ " wrapS " ] = 33648 ;
m_json [ " samplers " ] [ 0 ] [ " wrapT " ] = 33648 ;
int imageIndex = 0 ;
int textureIndex = 0 ;
2018-10-27 02:29:51 +00:00
// Images should be put in the end of the buffer, because we are not using accessors
2018-10-25 09:18:04 +00:00
if ( nullptr ! = textureImage ) {
2018-11-18 00:16:26 +00:00
m_json [ " textures " ] [ textureIndex ] [ " sampler " ] = 0 ;
m_json [ " textures " ] [ textureIndex ] [ " source " ] = imageIndex ;
2018-10-25 09:18:04 +00:00
bufferViewFromOffset = ( int ) m_binByteArray . size ( ) ;
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " buffer " ] = 0 ;
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " byteOffset " ] = bufferViewFromOffset ;
QByteArray pngByteArray ;
QBuffer buffer ( & pngByteArray ) ;
textureImage - > save ( & buffer , " PNG " ) ;
binStream . writeRawData ( pngByteArray . data ( ) , pngByteArray . size ( ) ) ;
alignBin ( ) ;
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " byteLength " ] = m_binByteArray . size ( ) - bufferViewFromOffset ;
2018-11-18 00:16:26 +00:00
m_json [ " images " ] [ imageIndex ] [ " bufferView " ] = bufferViewIndex ;
m_json [ " images " ] [ imageIndex ] [ " mimeType " ] = " image/png " ;
bufferViewIndex + + ;
imageIndex + + ;
textureIndex + + ;
}
if ( nullptr ! = normalImage ) {
m_json [ " textures " ] [ textureIndex ] [ " sampler " ] = 0 ;
m_json [ " textures " ] [ textureIndex ] [ " source " ] = imageIndex ;
bufferViewFromOffset = ( int ) m_binByteArray . size ( ) ;
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " buffer " ] = 0 ;
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " byteOffset " ] = bufferViewFromOffset ;
QByteArray pngByteArray ;
QBuffer buffer ( & pngByteArray ) ;
normalImage - > save ( & buffer , " PNG " ) ;
binStream . writeRawData ( pngByteArray . data ( ) , pngByteArray . size ( ) ) ;
alignBin ( ) ;
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " byteLength " ] = m_binByteArray . size ( ) - bufferViewFromOffset ;
m_json [ " images " ] [ imageIndex ] [ " bufferView " ] = bufferViewIndex ;
m_json [ " images " ] [ imageIndex ] [ " mimeType " ] = " image/png " ;
2018-10-25 09:18:04 +00:00
bufferViewIndex + + ;
2018-11-18 00:16:26 +00:00
imageIndex + + ;
textureIndex + + ;
}
if ( nullptr ! = ormImage ) {
m_json [ " textures " ] [ textureIndex ] [ " sampler " ] = 0 ;
m_json [ " textures " ] [ textureIndex ] [ " source " ] = imageIndex ;
bufferViewFromOffset = ( int ) m_binByteArray . size ( ) ;
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " buffer " ] = 0 ;
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " byteOffset " ] = bufferViewFromOffset ;
QByteArray pngByteArray ;
QBuffer buffer ( & pngByteArray ) ;
ormImage - > save ( & buffer , " PNG " ) ;
binStream . writeRawData ( pngByteArray . data ( ) , pngByteArray . size ( ) ) ;
alignBin ( ) ;
m_json [ " bufferViews " ] [ bufferViewIndex ] [ " byteLength " ] = m_binByteArray . size ( ) - bufferViewFromOffset ;
m_json [ " images " ] [ imageIndex ] [ " bufferView " ] = bufferViewIndex ;
m_json [ " images " ] [ imageIndex ] [ " mimeType " ] = " image/png " ;
bufferViewIndex + + ;
imageIndex + + ;
textureIndex + + ;
2018-10-25 09:18:04 +00:00
}
m_json [ " buffers " ] [ 0 ] [ " byteLength " ] = m_binByteArray . size ( ) ;
2018-10-27 02:29:51 +00:00
auto jsonString = m_enableComment ? m_json . dump ( 4 ) : m_json . dump ( ) ;
2018-10-25 09:18:04 +00:00
jsonStream . writeRawData ( jsonString . data ( ) , jsonString . size ( ) ) ;
alignJson ( ) ;
2018-05-07 16:08:19 +00:00
}
2018-10-25 09:18:04 +00:00
bool GlbFileWriter : : save ( )
2018-04-30 11:31:09 +00:00
{
2018-05-07 16:08:19 +00:00
QFile file ( m_filename ) ;
2018-04-30 11:31:09 +00:00
if ( ! file . open ( QIODevice : : WriteOnly ) ) {
return false ;
}
2018-10-25 09:18:04 +00:00
QDataStream output ( & file ) ;
output . setFloatingPointPrecision ( QDataStream : : SinglePrecision ) ;
output . setByteOrder ( QDataStream : : LittleEndian ) ;
uint32_t headerSize = 12 ;
uint32_t chunk0DescriptionSize = 8 ;
uint32_t chunk1DescriptionSize = 8 ;
uint32_t fileSize = headerSize +
chunk0DescriptionSize + m_jsonByteArray . size ( ) +
chunk1DescriptionSize + m_binByteArray . size ( ) ;
qDebug ( ) < < " Chunk 0 data size: " < < m_jsonByteArray . size ( ) ;
qDebug ( ) < < " Chunk 1 data size: " < < m_binByteArray . size ( ) ;
qDebug ( ) < < " File size: " < < fileSize ;
//////////// Header ////////////
// magic
output < < ( uint32_t ) 0x46546C67 ;
// version
output < < ( uint32_t ) 0x00000002 ;
// length
output < < ( uint32_t ) fileSize ;
//////////// Chunk 0 (Json) ////////////
// length
output < < ( uint32_t ) m_jsonByteArray . size ( ) ;
// type
output < < ( uint32_t ) 0x4E4F534A ;
// data
output . writeRawData ( m_jsonByteArray . data ( ) , m_jsonByteArray . size ( ) ) ;
//////////// Chunk 1 (Binary Buffer) ///
// length
output < < ( uint32_t ) m_binByteArray . size ( ) ;
// type
output < < ( uint32_t ) 0x004E4942 ;
// data
output . writeRawData ( m_binByteArray . data ( ) , m_binByteArray . size ( ) ) ;
2018-04-30 11:31:09 +00:00
return true ;
}