overte/libraries/fbx/src/FBXReader.h
Anthony J. Thibault e96676fea4 Support for the 3ds Max object offset feature
FBXReader now supports the GeometricTranslation, GeometricRotation and GeometricScaling FBX fields.
These offsets are applied directly to the mesh vertices, as they are never animated and the rest of our
rendering and animation system would have difficulty supporting them otherwise.
2016-11-30 18:28:55 -08:00

474 lines
13 KiB
C++

//
// FBXReader.h
// interface/src/renderer
//
// Created by Andrzej Kapolka on 9/18/13.
// Copyright 2013 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_FBXReader_h
#define hifi_FBXReader_h
#include <QMetaType>
#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>
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
QVector<glm::vec3> points;
};
/// 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;
};
/// A texture map in an FBX document.
class FBXTexture {
public:
QString name;
QByteArray filename;
QByteArray content;
Transform transform;
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) {}
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<glm::vec4> clusterIndices;
QVector<glm::vec4> clusterWeights;
QVector<FBXCluster> clusters;
Extents meshExtents;
glm::mat4 modelTransform;
bool isEye;
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 point where an avatar can sit
class SittingPoint {
public:
QString name;
glm::vec3 position; // relative postion
glm::quat rotation; // relative orientation
};
inline bool operator==(const SittingPoint& lhs, const SittingPoint& rhs)
{
return (lhs.name == rhs.name) && (lhs.position == rhs.position) && (lhs.rotation == rhs.rotation);
}
inline bool operator!=(const SittingPoint& lhs, const SittingPoint& rhs)
{
return (lhs.name != rhs.name) || (lhs.position != rhs.position) || (lhs.rotation != rhs.rotation);
}
/// A set of meshes extracted from an FBX document.
class FBXGeometry {
public:
using Pointer = std::shared_ptr<FBXGeometry>;
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;
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;
QVector<SittingPoint> sittingPoints;
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
FBXGeometry* readFBX(const QByteArray& model, const QVariantHash& mapping, const QString& url = "", bool loadLightmaps = true, float lightmapLevel = 1.0f);
/// Reads FBX geometry from the supplied model and mapping data.
/// \exception QString if an error occurs in parsing
FBXGeometry* readFBX(QIODevice* device, const QVariantHash& mapping, const QString& url = "", bool loadLightmaps = true, float lightmapLevel = 1.0f);
class TextureParam {
public:
glm::vec2 UVTranslation;
glm::vec2 UVScaling;
glm::vec4 cropping;
QString UVSet;
glm::vec3 translation;
glm::vec3 rotation;
glm::vec3 scaling;
uint8_t alphaSource;
uint8_t currentTextureBlendMode;
bool useMaterial;
template <typename T>
bool assign(T& ref, const T& v) {
if (ref == v) {
return false;
} else {
ref = v;
isDefault = false;
return true;
}
}
bool isDefault;
TextureParam() :
UVTranslation(0.0f),
UVScaling(1.0f),
cropping(0.0f),
UVSet("map1"),
translation(0.0f),
rotation(0.0f),
scaling(1.0f),
alphaSource(0),
currentTextureBlendMode(0),
useMaterial(true),
isDefault(true)
{}
TextureParam(const TextureParam& src) :
UVTranslation(src.UVTranslation),
UVScaling(src.UVScaling),
cropping(src.cropping),
UVSet(src.UVSet),
translation(src.translation),
rotation(src.rotation),
scaling(src.scaling),
alphaSource(src.alphaSource),
currentTextureBlendMode(src.currentTextureBlendMode),
useMaterial(src.useMaterial),
isDefault(src.isDefault)
{}
};
class ExtractedMesh;
class FBXReader {
public:
FBXGeometry* _fbxGeometry;
FBXNode _fbxNode;
static FBXNode parseFBX(QIODevice* device);
FBXGeometry* extractFBXGeometry(const QVariantHash& mapping, const QString& url);
ExtractedMesh extractMesh(const FBXNode& object, unsigned int& meshIndex);
QHash<QString, ExtractedMesh> meshes;
static void buildModelMesh(FBXMesh& extractedMesh, const QString& url);
FBXTexture getTexture(const QString& textureID);
QHash<QString, QString> _textureNames;
// Hashes the original RelativeFilename of textures
QHash<QString, QByteArray> _textureFilepaths;
// Hashes the place to look for textures, in case they are not inlined
QHash<QString, QByteArray> _textureFilenames;
// Hashes texture content by filepath, in case they are inlined
QHash<QByteArray, QByteArray> _textureContent;
QHash<QString, TextureParam> _textureParams;
QHash<QString, QString> diffuseTextures;
QHash<QString, QString> diffuseFactorTextures;
QHash<QString, QString> transparentTextures;
QHash<QString, QString> bumpTextures;
QHash<QString, QString> normalTextures;
QHash<QString, QString> specularTextures;
QHash<QString, QString> metallicTextures;
QHash<QString, QString> roughnessTextures;
QHash<QString, QString> shininessTextures;
QHash<QString, QString> emissiveTextures;
QHash<QString, QString> ambientTextures;
QHash<QString, QString> ambientFactorTextures;
QHash<QString, QString> occlusionTextures;
QHash<QString, FBXMaterial> _fbxMaterials;
void consolidateFBXMaterials(const QVariantHash& mapping);
bool _loadLightmaps = true;
float _lightmapOffset = 0.0f;
float _lightmapLevel;
QMultiMap<QString, QString> _connectionParentMap;
QMultiMap<QString, QString> _connectionChildMap;
static glm::vec3 getVec3(const QVariantList& properties, int index);
static QVector<glm::vec4> createVec4Vector(const QVector<double>& doubleVector);
static QVector<glm::vec4> createVec4VectorRGBA(const QVector<double>& doubleVector, glm::vec4& average);
static QVector<glm::vec3> createVec3Vector(const QVector<double>& doubleVector);
static QVector<glm::vec2> createVec2Vector(const QVector<double>& doubleVector);
static glm::mat4 createMat4(const QVector<double>& doubleVector);
static QVector<int> getIntVector(const FBXNode& node);
static QVector<float> getFloatVector(const FBXNode& node);
static QVector<double> getDoubleVector(const FBXNode& node);
};
#endif // hifi_FBXReader_h