mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-25 16:55:07 +02:00
Add FBXWriter for serializing FBXNode
This commit is contained in:
parent
c5f7c5d314
commit
aa1aad0a09
7 changed files with 641 additions and 317 deletions
10
libraries/fbx/src/FBX.cpp
Normal file
10
libraries/fbx/src/FBX.cpp
Normal file
|
@ -0,0 +1,10 @@
|
|||
//
|
||||
// FBX.cpp
|
||||
// libraries/fbx/src
|
||||
//
|
||||
// Created by Ryan Huffman on 9/5/17.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
333
libraries/fbx/src/FBX.h
Normal file
333
libraries/fbx/src/FBX.h
Normal file
|
@ -0,0 +1,333 @@
|
|||
//
|
||||
// FBX.h
|
||||
// libraries/fbx/src
|
||||
//
|
||||
// Created by Ryan Huffman on 9/5/17.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_FBX_h_
|
||||
#define hifi_FBX_h_
|
||||
|
||||
#include <QMetaType>
|
||||
#include <QSet>
|
||||
#include <QUrl>
|
||||
#include <QVarLengthArray>
|
||||
#include <QVariant>
|
||||
#include <QVector>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
#include <Extents.h>
|
||||
#include <Transform.h>
|
||||
|
||||
#include <model/Geometry.h>
|
||||
#include <model/Material.h>
|
||||
|
||||
static const QByteArray FBX_BINARY_PROLOG = "Kaydara FBX Binary ";
|
||||
static const int FBX_HEADER_BYTES_BEFORE_VERSION = 23;
|
||||
static const quint32 FBX_VERSION_2016 = 7500;
|
||||
|
||||
class FBXNode;
|
||||
using FBXNodeList = QList<FBXNode>;
|
||||
|
||||
|
||||
/// A node within an FBX document.
|
||||
class FBXNode {
|
||||
public:
|
||||
QByteArray name;
|
||||
QVariantList properties;
|
||||
FBXNodeList children;
|
||||
};
|
||||
|
||||
|
||||
/// A single blendshape extracted from an FBX document.
|
||||
class FBXBlendshape {
|
||||
public:
|
||||
QVector<int> indices;
|
||||
QVector<glm::vec3> vertices;
|
||||
QVector<glm::vec3> normals;
|
||||
};
|
||||
|
||||
struct FBXJointShapeInfo {
|
||||
// same units and frame as FBXJoint.translation
|
||||
glm::vec3 avgPoint;
|
||||
std::vector<float> dots;
|
||||
std::vector<glm::vec3> points;
|
||||
std::vector<glm::vec3> debugLines;
|
||||
};
|
||||
|
||||
/// A single joint (transformation node) extracted from an FBX document.
|
||||
class FBXJoint {
|
||||
public:
|
||||
|
||||
FBXJointShapeInfo shapeInfo;
|
||||
QVector<int> freeLineage;
|
||||
bool isFree;
|
||||
int parentIndex;
|
||||
float distanceToParent;
|
||||
|
||||
// http://download.autodesk.com/us/fbx/20112/FBX_SDK_HELP/SDKRef/a00209.html
|
||||
|
||||
glm::vec3 translation; // T
|
||||
glm::mat4 preTransform; // Roff * Rp
|
||||
glm::quat preRotation; // Rpre
|
||||
glm::quat rotation; // R
|
||||
glm::quat postRotation; // Rpost
|
||||
glm::mat4 postTransform; // Rp-1 * Soff * Sp * S * Sp-1
|
||||
|
||||
// World = ParentWorld * T * (Roff * Rp) * Rpre * R * Rpost * (Rp-1 * Soff * Sp * S * Sp-1)
|
||||
|
||||
glm::mat4 transform;
|
||||
glm::vec3 rotationMin; // radians
|
||||
glm::vec3 rotationMax; // radians
|
||||
glm::quat inverseDefaultRotation;
|
||||
glm::quat inverseBindRotation;
|
||||
glm::mat4 bindTransform;
|
||||
QString name;
|
||||
bool isSkeletonJoint;
|
||||
bool bindTransformFoundInCluster;
|
||||
|
||||
// geometric offset is applied in local space but does NOT affect children.
|
||||
bool hasGeometricOffset;
|
||||
glm::vec3 geometricTranslation;
|
||||
glm::quat geometricRotation;
|
||||
glm::vec3 geometricScaling;
|
||||
};
|
||||
|
||||
|
||||
/// A single binding to a joint in an FBX document.
|
||||
class FBXCluster {
|
||||
public:
|
||||
|
||||
int jointIndex;
|
||||
glm::mat4 inverseBindMatrix;
|
||||
};
|
||||
|
||||
const int MAX_NUM_PIXELS_FOR_FBX_TEXTURE = 2048 * 2048;
|
||||
|
||||
/// A texture map in an FBX document.
|
||||
class FBXTexture {
|
||||
public:
|
||||
QString name;
|
||||
QByteArray filename;
|
||||
QByteArray content;
|
||||
|
||||
Transform transform;
|
||||
int maxNumPixels { MAX_NUM_PIXELS_FOR_FBX_TEXTURE };
|
||||
int texcoordSet;
|
||||
QString texcoordSetName;
|
||||
|
||||
bool isBumpmap{ false };
|
||||
|
||||
bool isNull() const { return name.isEmpty() && filename.isEmpty() && content.isEmpty(); }
|
||||
};
|
||||
|
||||
/// A single part of a mesh (with the same material).
|
||||
class FBXMeshPart {
|
||||
public:
|
||||
|
||||
QVector<int> quadIndices; // original indices from the FBX mesh
|
||||
QVector<int> quadTrianglesIndices; // original indices from the FBX mesh of the quad converted as triangles
|
||||
QVector<int> triangleIndices; // original indices from the FBX mesh
|
||||
|
||||
QString materialID;
|
||||
};
|
||||
|
||||
class FBXMaterial {
|
||||
public:
|
||||
FBXMaterial() {};
|
||||
FBXMaterial(const glm::vec3& diffuseColor, const glm::vec3& specularColor, const glm::vec3& emissiveColor,
|
||||
float shininess, float opacity) :
|
||||
diffuseColor(diffuseColor),
|
||||
specularColor(specularColor),
|
||||
emissiveColor(emissiveColor),
|
||||
shininess(shininess),
|
||||
opacity(opacity) {}
|
||||
|
||||
void getTextureNames(QSet<QString>& textureList) const;
|
||||
void setMaxNumPixelsPerTexture(int maxNumPixels);
|
||||
|
||||
glm::vec3 diffuseColor{ 1.0f };
|
||||
float diffuseFactor{ 1.0f };
|
||||
glm::vec3 specularColor{ 0.02f };
|
||||
float specularFactor{ 1.0f };
|
||||
|
||||
glm::vec3 emissiveColor{ 0.0f };
|
||||
float emissiveFactor{ 0.0f };
|
||||
|
||||
float shininess{ 23.0f };
|
||||
float opacity{ 1.0f };
|
||||
|
||||
float metallic{ 0.0f };
|
||||
float roughness{ 1.0f };
|
||||
float emissiveIntensity{ 1.0f };
|
||||
float ambientFactor{ 1.0f };
|
||||
|
||||
QString materialID;
|
||||
QString name;
|
||||
QString shadingModel;
|
||||
model::MaterialPointer _material;
|
||||
|
||||
FBXTexture normalTexture;
|
||||
FBXTexture albedoTexture;
|
||||
FBXTexture opacityTexture;
|
||||
FBXTexture glossTexture;
|
||||
FBXTexture roughnessTexture;
|
||||
FBXTexture specularTexture;
|
||||
FBXTexture metallicTexture;
|
||||
FBXTexture emissiveTexture;
|
||||
FBXTexture occlusionTexture;
|
||||
FBXTexture scatteringTexture;
|
||||
FBXTexture lightmapTexture;
|
||||
glm::vec2 lightmapParams{ 0.0f, 1.0f };
|
||||
|
||||
|
||||
bool isPBSMaterial{ false };
|
||||
// THe use XXXMap are not really used to drive which map are going or not, debug only
|
||||
bool useNormalMap{ false };
|
||||
bool useAlbedoMap{ false };
|
||||
bool useOpacityMap{ false };
|
||||
bool useRoughnessMap{ false };
|
||||
bool useSpecularMap{ false };
|
||||
bool useMetallicMap{ false };
|
||||
bool useEmissiveMap{ false };
|
||||
bool useOcclusionMap{ false };
|
||||
|
||||
bool needTangentSpace() const;
|
||||
};
|
||||
|
||||
/// A single mesh (with optional blendshapes) extracted from an FBX document.
|
||||
class FBXMesh {
|
||||
public:
|
||||
|
||||
QVector<FBXMeshPart> parts;
|
||||
|
||||
QVector<glm::vec3> vertices;
|
||||
QVector<glm::vec3> normals;
|
||||
QVector<glm::vec3> tangents;
|
||||
QVector<glm::vec3> colors;
|
||||
QVector<glm::vec2> texCoords;
|
||||
QVector<glm::vec2> texCoords1;
|
||||
QVector<uint16_t> clusterIndices;
|
||||
QVector<uint8_t> clusterWeights;
|
||||
|
||||
QVector<FBXCluster> clusters;
|
||||
|
||||
Extents meshExtents;
|
||||
glm::mat4 modelTransform;
|
||||
|
||||
QVector<FBXBlendshape> blendshapes;
|
||||
|
||||
unsigned int meshIndex; // the order the meshes appeared in the object file
|
||||
|
||||
model::MeshPointer _mesh;
|
||||
};
|
||||
|
||||
class ExtractedMesh {
|
||||
public:
|
||||
FBXMesh mesh;
|
||||
QMultiHash<int, int> newIndices;
|
||||
QVector<QHash<int, int> > blendshapeIndexMaps;
|
||||
QVector<QPair<int, int> > partMaterialTextures;
|
||||
QHash<QString, size_t> texcoordSetMap;
|
||||
};
|
||||
|
||||
/// A single animation frame extracted from an FBX document.
|
||||
class FBXAnimationFrame {
|
||||
public:
|
||||
QVector<glm::quat> rotations;
|
||||
QVector<glm::vec3> translations;
|
||||
};
|
||||
|
||||
/// A light in an FBX document.
|
||||
class FBXLight {
|
||||
public:
|
||||
QString name;
|
||||
Transform transform;
|
||||
float intensity;
|
||||
float fogValue;
|
||||
glm::vec3 color;
|
||||
|
||||
FBXLight() :
|
||||
name(),
|
||||
transform(),
|
||||
intensity(1.0f),
|
||||
fogValue(0.0f),
|
||||
color(1.0f)
|
||||
{}
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(FBXAnimationFrame)
|
||||
Q_DECLARE_METATYPE(QVector<FBXAnimationFrame>)
|
||||
|
||||
/// A set of meshes extracted from an FBX document.
|
||||
class FBXGeometry {
|
||||
public:
|
||||
using Pointer = std::shared_ptr<FBXGeometry>;
|
||||
|
||||
QString originalURL;
|
||||
QString author;
|
||||
QString applicationName; ///< the name of the application that generated the model
|
||||
|
||||
QVector<FBXJoint> joints;
|
||||
QHash<QString, int> jointIndices; ///< 1-based, so as to more easily detect missing indices
|
||||
bool hasSkeletonJoints;
|
||||
|
||||
QVector<FBXMesh> meshes;
|
||||
|
||||
QHash<QString, FBXMaterial> materials;
|
||||
|
||||
glm::mat4 offset; // This includes offset, rotation, and scale as specified by the FST file
|
||||
|
||||
int leftEyeJointIndex = -1;
|
||||
int rightEyeJointIndex = -1;
|
||||
int neckJointIndex = -1;
|
||||
int rootJointIndex = -1;
|
||||
int leanJointIndex = -1;
|
||||
int headJointIndex = -1;
|
||||
int leftHandJointIndex = -1;
|
||||
int rightHandJointIndex = -1;
|
||||
int leftToeJointIndex = -1;
|
||||
int rightToeJointIndex = -1;
|
||||
|
||||
float leftEyeSize = 0.0f; // Maximum mesh extents dimension
|
||||
float rightEyeSize = 0.0f;
|
||||
|
||||
QVector<int> humanIKJointIndices;
|
||||
|
||||
glm::vec3 palmDirection;
|
||||
|
||||
glm::vec3 neckPivot;
|
||||
|
||||
Extents bindExtents;
|
||||
Extents meshExtents;
|
||||
|
||||
QVector<FBXAnimationFrame> animationFrames;
|
||||
|
||||
int getJointIndex(const QString& name) const { return jointIndices.value(name) - 1; }
|
||||
QStringList getJointNames() const;
|
||||
|
||||
bool hasBlendedMeshes() const;
|
||||
|
||||
/// Returns the unscaled extents of the model's mesh
|
||||
Extents getUnscaledMeshExtents() const;
|
||||
|
||||
bool convexHullContains(const glm::vec3& point) const;
|
||||
|
||||
QHash<int, QString> meshIndicesToModelNames;
|
||||
|
||||
/// given a meshIndex this will return the name of the model that mesh belongs to if known
|
||||
QString getModelNameOfMesh(int meshIndex) const;
|
||||
|
||||
QList<QString> blendshapeChannelNames;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(FBXGeometry)
|
||||
Q_DECLARE_METATYPE(FBXGeometry::Pointer)
|
||||
|
||||
#endif // hifi_FBX_h_
|
|
@ -168,7 +168,8 @@ QString getID(const QVariantList& properties, int index = 0) {
|
|||
return processID(properties.at(index).toString());
|
||||
}
|
||||
|
||||
const char* HUMANIK_JOINTS[] = {
|
||||
/// The names of the joints in the Maya HumanIK rig
|
||||
static const std::array<char*, 16> HUMANIK_JOINTS = {
|
||||
"RightHand",
|
||||
"RightForeArm",
|
||||
"RightArm",
|
||||
|
@ -184,8 +185,7 @@ const char* HUMANIK_JOINTS[] = {
|
|||
"RightLeg",
|
||||
"LeftLeg",
|
||||
"RightFoot",
|
||||
"LeftFoot",
|
||||
""
|
||||
"LeftFoot"
|
||||
};
|
||||
|
||||
class FBXModel {
|
||||
|
@ -512,11 +512,8 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
|
|||
|
||||
|
||||
QVector<QString> humanIKJointNames;
|
||||
for (int i = 0;; i++) {
|
||||
for (int i = 0; i < HUMANIK_JOINTS.size(); i++) {
|
||||
QByteArray jointName = HUMANIK_JOINTS[i];
|
||||
if (jointName.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
humanIKJointNames.append(processID(getString(joints.value(jointName, jointName))));
|
||||
}
|
||||
QVector<QString> humanIKJointIDs(humanIKJointNames.size());
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#ifndef hifi_FBXReader_h
|
||||
#define hifi_FBXReader_h
|
||||
|
||||
#include "FBX.h"
|
||||
|
||||
#include <QMetaType>
|
||||
#include <QSet>
|
||||
#include <QUrl>
|
||||
|
@ -31,305 +33,6 @@
|
|||
class QIODevice;
|
||||
class FBXNode;
|
||||
|
||||
typedef QList<FBXNode> FBXNodeList;
|
||||
|
||||
/// The names of the joints in the Maya HumanIK rig, terminated with an empty string.
|
||||
extern const char* HUMANIK_JOINTS[];
|
||||
|
||||
/// A node within an FBX document.
|
||||
class FBXNode {
|
||||
public:
|
||||
|
||||
QByteArray name;
|
||||
QVariantList properties;
|
||||
FBXNodeList children;
|
||||
};
|
||||
|
||||
/// A single blendshape extracted from an FBX document.
|
||||
class FBXBlendshape {
|
||||
public:
|
||||
|
||||
QVector<int> indices;
|
||||
QVector<glm::vec3> vertices;
|
||||
QVector<glm::vec3> normals;
|
||||
};
|
||||
|
||||
struct FBXJointShapeInfo {
|
||||
// same units and frame as FBXJoint.translation
|
||||
glm::vec3 avgPoint;
|
||||
std::vector<float> dots;
|
||||
std::vector<glm::vec3> points;
|
||||
std::vector<glm::vec3> debugLines;
|
||||
};
|
||||
|
||||
/// A single joint (transformation node) extracted from an FBX document.
|
||||
class FBXJoint {
|
||||
public:
|
||||
|
||||
FBXJointShapeInfo shapeInfo;
|
||||
QVector<int> freeLineage;
|
||||
bool isFree;
|
||||
int parentIndex;
|
||||
float distanceToParent;
|
||||
|
||||
// http://download.autodesk.com/us/fbx/20112/FBX_SDK_HELP/SDKRef/a00209.html
|
||||
|
||||
glm::vec3 translation; // T
|
||||
glm::mat4 preTransform; // Roff * Rp
|
||||
glm::quat preRotation; // Rpre
|
||||
glm::quat rotation; // R
|
||||
glm::quat postRotation; // Rpost
|
||||
glm::mat4 postTransform; // Rp-1 * Soff * Sp * S * Sp-1
|
||||
|
||||
// World = ParentWorld * T * (Roff * Rp) * Rpre * R * Rpost * (Rp-1 * Soff * Sp * S * Sp-1)
|
||||
|
||||
glm::mat4 transform;
|
||||
glm::vec3 rotationMin; // radians
|
||||
glm::vec3 rotationMax; // radians
|
||||
glm::quat inverseDefaultRotation;
|
||||
glm::quat inverseBindRotation;
|
||||
glm::mat4 bindTransform;
|
||||
QString name;
|
||||
bool isSkeletonJoint;
|
||||
bool bindTransformFoundInCluster;
|
||||
|
||||
// geometric offset is applied in local space but does NOT affect children.
|
||||
bool hasGeometricOffset;
|
||||
glm::vec3 geometricTranslation;
|
||||
glm::quat geometricRotation;
|
||||
glm::vec3 geometricScaling;
|
||||
};
|
||||
|
||||
|
||||
/// A single binding to a joint in an FBX document.
|
||||
class FBXCluster {
|
||||
public:
|
||||
|
||||
int jointIndex;
|
||||
glm::mat4 inverseBindMatrix;
|
||||
};
|
||||
|
||||
const int MAX_NUM_PIXELS_FOR_FBX_TEXTURE = 2048 * 2048;
|
||||
|
||||
/// A texture map in an FBX document.
|
||||
class FBXTexture {
|
||||
public:
|
||||
QString name;
|
||||
QByteArray filename;
|
||||
QByteArray content;
|
||||
|
||||
Transform transform;
|
||||
int maxNumPixels { MAX_NUM_PIXELS_FOR_FBX_TEXTURE };
|
||||
int texcoordSet;
|
||||
QString texcoordSetName;
|
||||
|
||||
bool isBumpmap{ false };
|
||||
|
||||
bool isNull() const { return name.isEmpty() && filename.isEmpty() && content.isEmpty(); }
|
||||
};
|
||||
|
||||
/// A single part of a mesh (with the same material).
|
||||
class FBXMeshPart {
|
||||
public:
|
||||
|
||||
QVector<int> quadIndices; // original indices from the FBX mesh
|
||||
QVector<int> quadTrianglesIndices; // original indices from the FBX mesh of the quad converted as triangles
|
||||
QVector<int> triangleIndices; // original indices from the FBX mesh
|
||||
|
||||
QString materialID;
|
||||
};
|
||||
|
||||
class FBXMaterial {
|
||||
public:
|
||||
FBXMaterial() {};
|
||||
FBXMaterial(const glm::vec3& diffuseColor, const glm::vec3& specularColor, const glm::vec3& emissiveColor,
|
||||
float shininess, float opacity) :
|
||||
diffuseColor(diffuseColor),
|
||||
specularColor(specularColor),
|
||||
emissiveColor(emissiveColor),
|
||||
shininess(shininess),
|
||||
opacity(opacity) {}
|
||||
|
||||
void getTextureNames(QSet<QString>& textureList) const;
|
||||
void setMaxNumPixelsPerTexture(int maxNumPixels);
|
||||
|
||||
glm::vec3 diffuseColor{ 1.0f };
|
||||
float diffuseFactor{ 1.0f };
|
||||
glm::vec3 specularColor{ 0.02f };
|
||||
float specularFactor{ 1.0f };
|
||||
|
||||
glm::vec3 emissiveColor{ 0.0f };
|
||||
float emissiveFactor{ 0.0f };
|
||||
|
||||
float shininess{ 23.0f };
|
||||
float opacity{ 1.0f };
|
||||
|
||||
float metallic{ 0.0f };
|
||||
float roughness{ 1.0f };
|
||||
float emissiveIntensity{ 1.0f };
|
||||
float ambientFactor{ 1.0f };
|
||||
|
||||
QString materialID;
|
||||
QString name;
|
||||
QString shadingModel;
|
||||
model::MaterialPointer _material;
|
||||
|
||||
FBXTexture normalTexture;
|
||||
FBXTexture albedoTexture;
|
||||
FBXTexture opacityTexture;
|
||||
FBXTexture glossTexture;
|
||||
FBXTexture roughnessTexture;
|
||||
FBXTexture specularTexture;
|
||||
FBXTexture metallicTexture;
|
||||
FBXTexture emissiveTexture;
|
||||
FBXTexture occlusionTexture;
|
||||
FBXTexture scatteringTexture;
|
||||
FBXTexture lightmapTexture;
|
||||
glm::vec2 lightmapParams{ 0.0f, 1.0f };
|
||||
|
||||
|
||||
bool isPBSMaterial{ false };
|
||||
// THe use XXXMap are not really used to drive which map are going or not, debug only
|
||||
bool useNormalMap{ false };
|
||||
bool useAlbedoMap{ false };
|
||||
bool useOpacityMap{ false };
|
||||
bool useRoughnessMap{ false };
|
||||
bool useSpecularMap{ false };
|
||||
bool useMetallicMap{ false };
|
||||
bool useEmissiveMap{ false };
|
||||
bool useOcclusionMap{ false };
|
||||
|
||||
bool needTangentSpace() const;
|
||||
};
|
||||
|
||||
/// A single mesh (with optional blendshapes) extracted from an FBX document.
|
||||
class FBXMesh {
|
||||
public:
|
||||
|
||||
QVector<FBXMeshPart> parts;
|
||||
|
||||
QVector<glm::vec3> vertices;
|
||||
QVector<glm::vec3> normals;
|
||||
QVector<glm::vec3> tangents;
|
||||
QVector<glm::vec3> colors;
|
||||
QVector<glm::vec2> texCoords;
|
||||
QVector<glm::vec2> texCoords1;
|
||||
QVector<uint16_t> clusterIndices;
|
||||
QVector<uint8_t> clusterWeights;
|
||||
|
||||
QVector<FBXCluster> clusters;
|
||||
|
||||
Extents meshExtents;
|
||||
glm::mat4 modelTransform;
|
||||
|
||||
QVector<FBXBlendshape> blendshapes;
|
||||
|
||||
unsigned int meshIndex; // the order the meshes appeared in the object file
|
||||
|
||||
model::MeshPointer _mesh;
|
||||
};
|
||||
|
||||
class ExtractedMesh {
|
||||
public:
|
||||
FBXMesh mesh;
|
||||
QMultiHash<int, int> newIndices;
|
||||
QVector<QHash<int, int> > blendshapeIndexMaps;
|
||||
QVector<QPair<int, int> > partMaterialTextures;
|
||||
QHash<QString, size_t> texcoordSetMap;
|
||||
};
|
||||
|
||||
/// A single animation frame extracted from an FBX document.
|
||||
class FBXAnimationFrame {
|
||||
public:
|
||||
QVector<glm::quat> rotations;
|
||||
QVector<glm::vec3> translations;
|
||||
};
|
||||
|
||||
/// A light in an FBX document.
|
||||
class FBXLight {
|
||||
public:
|
||||
QString name;
|
||||
Transform transform;
|
||||
float intensity;
|
||||
float fogValue;
|
||||
glm::vec3 color;
|
||||
|
||||
FBXLight() :
|
||||
name(),
|
||||
transform(),
|
||||
intensity(1.0f),
|
||||
fogValue(0.0f),
|
||||
color(1.0f)
|
||||
{}
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(FBXAnimationFrame)
|
||||
Q_DECLARE_METATYPE(QVector<FBXAnimationFrame>)
|
||||
|
||||
/// A set of meshes extracted from an FBX document.
|
||||
class FBXGeometry {
|
||||
public:
|
||||
using Pointer = std::shared_ptr<FBXGeometry>;
|
||||
|
||||
QString originalURL;
|
||||
QString author;
|
||||
QString applicationName; ///< the name of the application that generated the model
|
||||
|
||||
QVector<FBXJoint> joints;
|
||||
QHash<QString, int> jointIndices; ///< 1-based, so as to more easily detect missing indices
|
||||
bool hasSkeletonJoints;
|
||||
|
||||
QVector<FBXMesh> meshes;
|
||||
|
||||
QHash<QString, FBXMaterial> materials;
|
||||
|
||||
glm::mat4 offset; // This includes offset, rotation, and scale as specified by the FST file
|
||||
|
||||
int leftEyeJointIndex = -1;
|
||||
int rightEyeJointIndex = -1;
|
||||
int neckJointIndex = -1;
|
||||
int rootJointIndex = -1;
|
||||
int leanJointIndex = -1;
|
||||
int headJointIndex = -1;
|
||||
int leftHandJointIndex = -1;
|
||||
int rightHandJointIndex = -1;
|
||||
int leftToeJointIndex = -1;
|
||||
int rightToeJointIndex = -1;
|
||||
|
||||
float leftEyeSize = 0.0f; // Maximum mesh extents dimension
|
||||
float rightEyeSize = 0.0f;
|
||||
|
||||
QVector<int> humanIKJointIndices;
|
||||
|
||||
glm::vec3 palmDirection;
|
||||
|
||||
glm::vec3 neckPivot;
|
||||
|
||||
Extents bindExtents;
|
||||
Extents meshExtents;
|
||||
|
||||
QVector<FBXAnimationFrame> animationFrames;
|
||||
|
||||
int getJointIndex(const QString& name) const { return jointIndices.value(name) - 1; }
|
||||
QStringList getJointNames() const;
|
||||
|
||||
bool hasBlendedMeshes() const;
|
||||
|
||||
/// Returns the unscaled extents of the model's mesh
|
||||
Extents getUnscaledMeshExtents() const;
|
||||
|
||||
bool convexHullContains(const glm::vec3& point) const;
|
||||
|
||||
QHash<int, QString> meshIndicesToModelNames;
|
||||
|
||||
/// given a meshIndex this will return the name of the model that mesh belongs to if known
|
||||
QString getModelNameOfMesh(int meshIndex) const;
|
||||
|
||||
QList<QString> blendshapeChannelNames;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(FBXGeometry)
|
||||
Q_DECLARE_METATYPE(FBXGeometry::Pointer)
|
||||
|
||||
/// Reads FBX geometry from the supplied model and mapping data.
|
||||
/// \exception QString if an error occurs in parsing
|
||||
|
@ -402,7 +105,7 @@ class FBXReader {
|
|||
public:
|
||||
FBXGeometry* _fbxGeometry;
|
||||
|
||||
FBXNode _fbxNode;
|
||||
FBXNode _rootNode;
|
||||
static FBXNode parseFBX(QIODevice* device);
|
||||
|
||||
FBXGeometry* extractFBXGeometry(const QVariantHash& mapping, const QString& url);
|
||||
|
|
|
@ -24,15 +24,18 @@
|
|||
#include <shared/NsightHelpers.h>
|
||||
#include "ModelFormatLogging.h"
|
||||
|
||||
template<class T> int streamSize() {
|
||||
template<class T>
|
||||
int streamSize() {
|
||||
return sizeof(T);
|
||||
}
|
||||
|
||||
template<bool> int streamSize() {
|
||||
template<bool>
|
||||
int streamSize() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
template<class T> QVariant readBinaryArray(QDataStream& in, int& position) {
|
||||
template<class T>
|
||||
QVariant readBinaryArray(QDataStream& in, int& position) {
|
||||
quint32 arrayLength;
|
||||
quint32 encoding;
|
||||
quint32 compressedLength;
|
||||
|
@ -350,8 +353,7 @@ FBXNode parseTextFBXNode(Tokenizer& tokenizer) {
|
|||
FBXNode FBXReader::parseFBX(QIODevice* device) {
|
||||
PROFILE_RANGE_EX(resource_parse, __FUNCTION__, 0xff0000ff, device);
|
||||
// verify the prolog
|
||||
const QByteArray BINARY_PROLOG = "Kaydara FBX Binary ";
|
||||
if (device->peek(BINARY_PROLOG.size()) != BINARY_PROLOG) {
|
||||
if (device->peek(FBX_BINARY_PROLOG.size()) != FBX_BINARY_PROLOG) {
|
||||
// parse as a text file
|
||||
FBXNode top;
|
||||
Tokenizer tokenizer(device);
|
||||
|
@ -377,15 +379,13 @@ FBXNode FBXReader::parseFBX(QIODevice* device) {
|
|||
// Bytes 0 - 20: Kaydara FBX Binary \x00(file - magic, with 2 spaces at the end, then a NULL terminator).
|
||||
// Bytes 21 - 22: [0x1A, 0x00](unknown but all observed files show these bytes).
|
||||
// Bytes 23 - 26 : unsigned int, the version number. 7300 for version 7.3 for example.
|
||||
const int HEADER_BEFORE_VERSION = 23;
|
||||
const quint32 VERSION_FBX2016 = 7500;
|
||||
in.skipRawData(HEADER_BEFORE_VERSION);
|
||||
int position = HEADER_BEFORE_VERSION;
|
||||
in.skipRawData(FBX_HEADER_BYTES_BEFORE_VERSION);
|
||||
int position = FBX_HEADER_BYTES_BEFORE_VERSION;
|
||||
quint32 fileVersion;
|
||||
in >> fileVersion;
|
||||
position += sizeof(fileVersion);
|
||||
qCDebug(modelformat) << "fileVersion:" << fileVersion;
|
||||
bool has64BitPositions = (fileVersion >= VERSION_FBX2016);
|
||||
bool has64BitPositions = (fileVersion >= FBX_VERSION_2016);
|
||||
|
||||
// parse the top-level node
|
||||
FBXNode top;
|
||||
|
|
253
libraries/fbx/src/FBXWriter.cpp
Normal file
253
libraries/fbx/src/FBXWriter.cpp
Normal file
|
@ -0,0 +1,253 @@
|
|||
//
|
||||
// FBXWriter.cpp
|
||||
// libraries/fbx/src
|
||||
//
|
||||
// Created by Ryan Huffman on 9/5/17.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "FBXWriter.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
|
||||
QByteArray FBXWriter::encodeFBX(const FBXNode& root) {
|
||||
QByteArray data;
|
||||
QDataStream out(&data, QIODevice::WriteOnly);
|
||||
out.setByteOrder(QDataStream::LittleEndian);
|
||||
out.setVersion(QDataStream::Qt_4_5);
|
||||
|
||||
out.writeRawData(FBX_BINARY_PROLOG, FBX_BINARY_PROLOG.size());
|
||||
auto bytes = QByteArray(FBX_HEADER_BYTES_BEFORE_VERSION - FBX_BINARY_PROLOG.size(), '\0');
|
||||
out.writeRawData(bytes, bytes.size());
|
||||
|
||||
out << FBX_VERSION_2016;
|
||||
|
||||
for (auto& child : root.children) {
|
||||
encodeNode(out, child);
|
||||
}
|
||||
encodeNode(out, FBXNode());
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void FBXWriter::encodeNode(QDataStream& out, const FBXNode& node) {
|
||||
qDebug() << "Encoding " << node.name;
|
||||
|
||||
auto device = out.device();
|
||||
auto nodeStartPos = device->pos();
|
||||
|
||||
// endOffset (temporary, updated later)
|
||||
out << (qint64)0;
|
||||
|
||||
// Property count
|
||||
out << (quint64)node.properties.size();
|
||||
|
||||
// Property list length (temporary, updated later)
|
||||
out << (quint64)0;
|
||||
|
||||
out << (quint8)node.name.size();
|
||||
out.writeRawData(node.name, node.name.size());
|
||||
|
||||
if (node.name == "Vertices") {
|
||||
for (auto& prop : node.properties) {
|
||||
qDebug() << "Properties: " << prop;
|
||||
}
|
||||
}
|
||||
|
||||
auto nodePropertiesStartPos = device->pos();
|
||||
|
||||
for (const auto& prop : node.properties) {
|
||||
encodeFBXProperty(out, prop);
|
||||
}
|
||||
|
||||
// Go back and write property list length
|
||||
auto nodePropertiesEndPos = device->pos();
|
||||
device->seek(nodeStartPos + sizeof(qint64) + sizeof(quint64));
|
||||
out << (quint64)(nodePropertiesEndPos - nodePropertiesStartPos);
|
||||
|
||||
device->seek(nodePropertiesEndPos);
|
||||
|
||||
for (auto& child : node.children) {
|
||||
encodeNode(out, child);
|
||||
}
|
||||
|
||||
if (node.children.length() > 0) {
|
||||
encodeNode(out, FBXNode());
|
||||
}
|
||||
|
||||
// Go back and write actual endOffset
|
||||
auto nodeEndPos = device->pos();
|
||||
device->seek(nodeStartPos);
|
||||
out << (qint64)(nodeEndPos);
|
||||
|
||||
device->seek(nodeEndPos);
|
||||
}
|
||||
|
||||
void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) {
|
||||
auto type = prop.userType();
|
||||
switch (type) {
|
||||
case QVariant::Type::Bool:
|
||||
|
||||
out.device()->write("C", 1);
|
||||
out << prop.toBool();
|
||||
break;
|
||||
|
||||
case QMetaType::Int:
|
||||
out.device()->write("I", 1);
|
||||
out << prop.toInt();
|
||||
break;
|
||||
|
||||
encodeNode(out, FBXNode());
|
||||
case QMetaType::Float:
|
||||
out.device()->write("F", 1);
|
||||
out << prop.toFloat();
|
||||
break;
|
||||
|
||||
case QMetaType::Double:
|
||||
out.device()->write("D", 1);
|
||||
out << prop.toDouble();
|
||||
break;
|
||||
|
||||
case QMetaType::LongLong:
|
||||
out.device()->write("L", 1);
|
||||
out << prop.toLongLong();
|
||||
break;
|
||||
|
||||
case QMetaType::QString:
|
||||
{
|
||||
auto& bytes = prop.toString().toUtf8();
|
||||
out << 'S';
|
||||
out << bytes.length();
|
||||
out << bytes;
|
||||
out << (int32_t)bytes.size();
|
||||
out.writeRawData(bytes, bytes.size());
|
||||
break;
|
||||
}
|
||||
|
||||
case QMetaType::QByteArray:
|
||||
{
|
||||
auto& bytes = prop.toByteArray();
|
||||
out.device()->write("S", 1);
|
||||
out << (int32_t)bytes.size();
|
||||
out.writeRawData(bytes, bytes.size());
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO Delete? Do we ever use QList instead of QVector?
|
||||
case QVariant::Type::List:
|
||||
{
|
||||
auto& list = prop.toList();
|
||||
auto listType = prop.userType();
|
||||
|
||||
switch (listType) {
|
||||
case QMetaType::Float:
|
||||
out.device()->write("f", 1);
|
||||
out << (int32_t)list.length();
|
||||
out << (int32_t)0;
|
||||
out << (int32_t)0;
|
||||
for (auto& innerProp : list) {
|
||||
out << prop.toFloat();
|
||||
}
|
||||
break;
|
||||
|
||||
case QMetaType::Double:
|
||||
out.device()->write("d", 1);
|
||||
out << (int32_t)list.length();
|
||||
out << (int32_t)0;
|
||||
out << (int32_t)0;
|
||||
for (auto& innerProp : list) {
|
||||
out << prop.toDouble();
|
||||
}
|
||||
break;
|
||||
|
||||
case QMetaType::LongLong:
|
||||
out.device()->write("l", 1);
|
||||
out << (int32_t)list.length();
|
||||
out << (int32_t)0;
|
||||
out << (int32_t)0;
|
||||
for (auto& innerProp : list) {
|
||||
out << prop.toLongLong();
|
||||
}
|
||||
break;
|
||||
|
||||
case QMetaType::Int:
|
||||
out.device()->write("i", 1);
|
||||
out << (int32_t)list.length();
|
||||
out << (int32_t)0;
|
||||
out << (int32_t)0;
|
||||
for (auto& innerProp : list) {
|
||||
out << prop.toInt();
|
||||
}
|
||||
break;
|
||||
|
||||
case QMetaType::Bool:
|
||||
out.device()->write("b", 1);
|
||||
out << (int32_t)list.length();
|
||||
out << (int32_t)0;
|
||||
out << (int32_t)0;
|
||||
for (auto& innerProp : list) {
|
||||
out << prop.toBool();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
if (prop.canConvert<QVector<float>>()) {
|
||||
auto list = prop.value<QVector<float>>();
|
||||
out.device()->write("f", 1);
|
||||
out << (int32_t)list.length();
|
||||
out << (int32_t)0;
|
||||
out << (int32_t)0;
|
||||
for (auto& value : list) {
|
||||
out << value;
|
||||
}
|
||||
} else if (prop.canConvert<QVector<double>>()) {
|
||||
auto list = prop.value<QVector<double>>();
|
||||
out.device()->write("d", 1);
|
||||
out << (int32_t)list.length();
|
||||
out << (int32_t)0;
|
||||
out << (int32_t)0;
|
||||
for (auto& value : list) {
|
||||
out << value;
|
||||
}
|
||||
} else if (prop.canConvert<QVector<qint64>>()) {
|
||||
auto list = prop.value<QVector<qint64>>();
|
||||
out.device()->write("l", 1);
|
||||
out << (int32_t)list.length();
|
||||
out << (int32_t)0;
|
||||
out << (int32_t)0;
|
||||
for (auto& value : list) {
|
||||
out << value;
|
||||
}
|
||||
} else if (prop.canConvert<QVector<qint32>>()) {
|
||||
auto list = prop.value<QVector<qint32>>();
|
||||
out.device()->write("i", 1);
|
||||
out << (int32_t)list.length();
|
||||
out << (int32_t)0;
|
||||
out << (int32_t)0;
|
||||
for (auto& value : list) {
|
||||
out << value;
|
||||
}
|
||||
} else if (prop.canConvert<QVector<bool>>()) {
|
||||
auto list = prop.value<QVector<bool>>();
|
||||
out.device()->write("b", 1);
|
||||
out << (int32_t)list.length();
|
||||
out << (int32_t)0;
|
||||
out << (int32_t)0;
|
||||
for (auto& value : list) {
|
||||
out << value;
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Unsupported property type in FBXWriter::encodeNode: " << type << prop;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
28
libraries/fbx/src/FBXWriter.h
Normal file
28
libraries/fbx/src/FBXWriter.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
//
|
||||
// FBXWriter.h
|
||||
// libraries/fbx/src
|
||||
//
|
||||
// Created by Ryan Huffman on 9/5/17.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_FBXWriter_h
|
||||
#define hifi_FBXWriter_h
|
||||
|
||||
#include "FBX.h"
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QDataStream>
|
||||
|
||||
class FBXWriter {
|
||||
public:
|
||||
static QByteArray encodeFBX(const FBXNode& root);
|
||||
|
||||
static void encodeNode(QDataStream& out, const FBXNode& node);
|
||||
static void encodeFBXProperty(QDataStream& out, const QVariant& property);
|
||||
};
|
||||
|
||||
#endif // hifi_FBXWriter_h
|
Loading…
Reference in a new issue