mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Merge pull request #14348 from sabrina-shanman/hfm_library
(case 19302) Create the HFM library and namespace
This commit is contained in:
commit
153b8920be
38 changed files with 649 additions and 593 deletions
|
@ -1,6 +1,6 @@
|
|||
set(TARGET_NAME native-lib)
|
||||
setup_hifi_library()
|
||||
link_hifi_libraries(shared task networking gl gpu qml image fbx render-utils physics entities octree ${PLATFORM_GL_BACKEND})
|
||||
link_hifi_libraries(shared task networking gl gpu qml image fbx hfm render-utils physics entities octree ${PLATFORM_GL_BACKEND})
|
||||
target_opengl()
|
||||
target_bullet()
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ setup_memory_debugger()
|
|||
|
||||
# link in the shared libraries
|
||||
link_hifi_libraries(
|
||||
audio avatars octree gpu graphics fbx entities
|
||||
audio avatars octree gpu graphics fbx hfm entities
|
||||
networking animation recording shared script-engine embedded-webserver
|
||||
controllers physics plugins midi image
|
||||
)
|
||||
|
|
|
@ -206,7 +206,7 @@ endif()
|
|||
link_hifi_libraries(
|
||||
shared workload task octree ktx gpu gl procedural graphics graphics-scripting render
|
||||
pointers
|
||||
recording fbx networking model-networking entities avatars trackers
|
||||
recording hfm fbx networking model-networking entities avatars trackers
|
||||
audio audio-client animation script-engine physics
|
||||
render-utils entities-renderer avatars-renderer ui qml auto-updater midi
|
||||
controllers plugins image trackers
|
||||
|
|
|
@ -235,7 +235,7 @@ bool ModelPackager::zipModel() {
|
|||
return true;
|
||||
}
|
||||
|
||||
void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename, const HFMModel& hfmModel) {
|
||||
void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename, const hfm::Model& hfmModel) {
|
||||
|
||||
bool isBodyType = _modelType == FSTReader::BODY_ONLY_MODEL || _modelType == FSTReader::HEAD_AND_BODY_MODEL;
|
||||
|
||||
|
|
|
@ -19,7 +19,9 @@
|
|||
|
||||
#include "ui/ModelsBrowser.h"
|
||||
|
||||
class HFMModel;
|
||||
namespace hfm {
|
||||
class Model;
|
||||
};
|
||||
|
||||
class ModelPackager : public QObject {
|
||||
public:
|
||||
|
@ -32,7 +34,7 @@ private:
|
|||
bool editProperties();
|
||||
bool zipModel();
|
||||
|
||||
void populateBasicMapping(QVariantHash& mapping, QString filename, const HFMModel& hfmModel);
|
||||
void populateBasicMapping(QVariantHash& mapping, QString filename, const hfm::Model& hfmModel);
|
||||
|
||||
void listTextures();
|
||||
bool copyTextures(const QString& oldDir, const QDir& newDir);
|
||||
|
@ -44,7 +46,7 @@ private:
|
|||
QString _scriptDir;
|
||||
|
||||
QVariantHash _mapping;
|
||||
std::unique_ptr<HFMModel> _hfmModel;
|
||||
std::unique_ptr<hfm::Model> _hfmModel;
|
||||
QStringList _textures;
|
||||
QStringList _scripts;
|
||||
};
|
||||
|
|
|
@ -3,5 +3,6 @@ setup_hifi_library(Network Script)
|
|||
link_hifi_libraries(shared graphics fbx)
|
||||
include_hifi_library_headers(networking)
|
||||
include_hifi_library_headers(gpu)
|
||||
include_hifi_library_headers(hfm)
|
||||
|
||||
target_nsight()
|
||||
|
|
|
@ -3,6 +3,7 @@ setup_hifi_library(Network Script)
|
|||
link_hifi_libraries(shared shaders gpu graphics animation model-networking script-engine render render-utils image trackers entities-renderer)
|
||||
include_hifi_library_headers(avatars)
|
||||
include_hifi_library_headers(networking)
|
||||
include_hifi_library_headers(hfm)
|
||||
include_hifi_library_headers(fbx)
|
||||
include_hifi_library_headers(recording)
|
||||
include_hifi_library_headers(ktx)
|
||||
|
|
|
@ -3,5 +3,6 @@ setup_hifi_library(Concurrent)
|
|||
|
||||
link_hifi_libraries(shared graphics networking ktx image fbx)
|
||||
include_hifi_library_headers(gpu)
|
||||
include_hifi_library_headers(hfm)
|
||||
|
||||
target_draco()
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <gpu/Texture.h>
|
||||
|
||||
#include <FBX.h>
|
||||
#include <hfm/HFM.h>
|
||||
|
||||
using TextureBakerThreadGetter = std::function<QThread*()>;
|
||||
using GetMaterialIDCallback = std::function <int(int)>;
|
||||
|
|
|
@ -5,6 +5,7 @@ include_hifi_library_headers(gpu)
|
|||
include_hifi_library_headers(model-networking)
|
||||
include_hifi_library_headers(networking)
|
||||
include_hifi_library_headers(graphics)
|
||||
include_hifi_library_headers(hfm)
|
||||
include_hifi_library_headers(fbx)
|
||||
include_hifi_library_headers(image)
|
||||
include_hifi_library_headers(ktx)
|
||||
|
|
|
@ -8,6 +8,7 @@ include_hifi_library_headers(octree)
|
|||
include_hifi_library_headers(audio)
|
||||
include_hifi_library_headers(physics)
|
||||
include_hifi_library_headers(animation)
|
||||
include_hifi_library_headers(hfm)
|
||||
include_hifi_library_headers(fbx)
|
||||
include_hifi_library_headers(entities)
|
||||
include_hifi_library_headers(avatars)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
set(TARGET_NAME entities)
|
||||
setup_hifi_library(Network Script)
|
||||
include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}")
|
||||
include_hifi_library_headers(hfm)
|
||||
include_hifi_library_headers(fbx)
|
||||
include_hifi_library_headers(gpu)
|
||||
include_hifi_library_headers(image)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
set(TARGET_NAME fbx)
|
||||
setup_hifi_library()
|
||||
|
||||
link_hifi_libraries(shared graphics networking image)
|
||||
link_hifi_libraries(shared graphics networking image hfm)
|
||||
include_hifi_library_headers(gpu image)
|
||||
|
||||
target_draco()
|
||||
|
|
|
@ -13,20 +13,11 @@
|
|||
#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 <graphics/Geometry.h>
|
||||
#include <graphics/Material.h>
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
#define FBX_PACK_NORMALS 0
|
||||
|
@ -69,306 +60,4 @@ public:
|
|||
FBXNodeList children;
|
||||
};
|
||||
|
||||
|
||||
/// A single blendshape.
|
||||
class HFMBlendshape {
|
||||
public:
|
||||
QVector<int> indices;
|
||||
QVector<glm::vec3> vertices;
|
||||
QVector<glm::vec3> normals;
|
||||
QVector<glm::vec3> tangents;
|
||||
};
|
||||
|
||||
struct HFMJointShapeInfo {
|
||||
// same units and frame as HFMJoint.translation
|
||||
glm::vec3 avgPoint;
|
||||
std::vector<float> dots;
|
||||
std::vector<glm::vec3> points;
|
||||
std::vector<glm::vec3> debugLines;
|
||||
};
|
||||
|
||||
/// A single joint (transformation node).
|
||||
class HFMJoint {
|
||||
public:
|
||||
|
||||
HFMJointShapeInfo 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.
|
||||
class HFMCluster {
|
||||
public:
|
||||
|
||||
int jointIndex;
|
||||
glm::mat4 inverseBindMatrix;
|
||||
Transform inverseBindTransform;
|
||||
};
|
||||
|
||||
const int MAX_NUM_PIXELS_FOR_FBX_TEXTURE = 2048 * 2048;
|
||||
|
||||
/// A texture map.
|
||||
class HFMTexture {
|
||||
public:
|
||||
QString id;
|
||||
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 HFMMeshPart {
|
||||
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 HFMMaterial {
|
||||
public:
|
||||
HFMMaterial() {};
|
||||
HFMMaterial(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 };
|
||||
|
||||
float bumpMultiplier { 1.0f }; // TODO: to be implemented
|
||||
|
||||
QString materialID;
|
||||
QString name;
|
||||
QString shadingModel;
|
||||
graphics::MaterialPointer _material;
|
||||
|
||||
HFMTexture normalTexture;
|
||||
HFMTexture albedoTexture;
|
||||
HFMTexture opacityTexture;
|
||||
HFMTexture glossTexture;
|
||||
HFMTexture roughnessTexture;
|
||||
HFMTexture specularTexture;
|
||||
HFMTexture metallicTexture;
|
||||
HFMTexture emissiveTexture;
|
||||
HFMTexture occlusionTexture;
|
||||
HFMTexture scatteringTexture;
|
||||
HFMTexture 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).
|
||||
class HFMMesh {
|
||||
public:
|
||||
|
||||
QVector<HFMMeshPart> 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<uint16_t> clusterWeights;
|
||||
QVector<int32_t> originalIndices;
|
||||
|
||||
QVector<HFMCluster> clusters;
|
||||
|
||||
Extents meshExtents;
|
||||
glm::mat4 modelTransform;
|
||||
|
||||
QVector<HFMBlendshape> blendshapes;
|
||||
|
||||
unsigned int meshIndex; // the order the meshes appeared in the object file
|
||||
|
||||
graphics::MeshPointer _mesh;
|
||||
bool wasCompressed { false };
|
||||
|
||||
void createMeshTangents(bool generateFromTexCoords);
|
||||
void createBlendShapeTangents(bool generateTangents);
|
||||
};
|
||||
|
||||
class ExtractedMesh {
|
||||
public:
|
||||
HFMMesh mesh;
|
||||
QMultiHash<int, int> newIndices;
|
||||
QVector<QHash<int, int> > blendshapeIndexMaps;
|
||||
QVector<QPair<int, int> > partMaterialTextures;
|
||||
QHash<QString, size_t> texcoordSetMap;
|
||||
};
|
||||
|
||||
/**jsdoc
|
||||
* @typedef {object} FBXAnimationFrame
|
||||
* @property {Quat[]} rotations
|
||||
* @property {Vec3[]} translations
|
||||
*/
|
||||
/// A single animation frame.
|
||||
class HFMAnimationFrame {
|
||||
public:
|
||||
QVector<glm::quat> rotations;
|
||||
QVector<glm::vec3> translations;
|
||||
};
|
||||
|
||||
/// A light.
|
||||
class HFMLight {
|
||||
public:
|
||||
QString name;
|
||||
Transform transform;
|
||||
float intensity;
|
||||
float fogValue;
|
||||
glm::vec3 color;
|
||||
|
||||
HFMLight() :
|
||||
name(),
|
||||
transform(),
|
||||
intensity(1.0f),
|
||||
fogValue(0.0f),
|
||||
color(1.0f)
|
||||
{}
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(HFMAnimationFrame)
|
||||
Q_DECLARE_METATYPE(QVector<HFMAnimationFrame>)
|
||||
|
||||
/// The runtime model format.
|
||||
class HFMModel {
|
||||
public:
|
||||
using Pointer = std::shared_ptr<HFMModel>;
|
||||
|
||||
QString originalURL;
|
||||
QString author;
|
||||
QString applicationName; ///< the name of the application that generated the model
|
||||
|
||||
QVector<HFMJoint> joints;
|
||||
QHash<QString, int> jointIndices; ///< 1-based, so as to more easily detect missing indices
|
||||
bool hasSkeletonJoints;
|
||||
|
||||
QVector<HFMMesh> meshes;
|
||||
QVector<QString> scripts;
|
||||
|
||||
QHash<QString, HFMMaterial> 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<HFMAnimationFrame> 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(HFMModel)
|
||||
Q_DECLARE_METATYPE(HFMModel::Pointer)
|
||||
|
||||
#endif // hifi_FBX_h_
|
||||
|
|
|
@ -33,109 +33,13 @@
|
|||
#include <gpu/Format.h>
|
||||
#include <LogHandler.h>
|
||||
|
||||
#include "ModelFormatLogging.h"
|
||||
#include <hfm/ModelFormatLogging.h>
|
||||
|
||||
// TOOL: Uncomment the following line to enable the filtering of all the unkwnon fields of a node so we can break point easily while loading a model with problems...
|
||||
//#define DEBUG_FBXREADER
|
||||
|
||||
using namespace std;
|
||||
|
||||
int HFMModelPointerMetaTypeId = qRegisterMetaType<HFMModel::Pointer>();
|
||||
|
||||
QStringList HFMModel::getJointNames() const {
|
||||
QStringList names;
|
||||
foreach (const HFMJoint& joint, joints) {
|
||||
names.append(joint.name);
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
bool HFMModel::hasBlendedMeshes() const {
|
||||
if (!meshes.isEmpty()) {
|
||||
foreach (const HFMMesh& mesh, meshes) {
|
||||
if (!mesh.blendshapes.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Extents HFMModel::getUnscaledMeshExtents() const {
|
||||
const Extents& extents = meshExtents;
|
||||
|
||||
// even though our caller asked for "unscaled" we need to include any fst scaling, translation, and rotation, which
|
||||
// is captured in the offset matrix
|
||||
glm::vec3 minimum = glm::vec3(offset * glm::vec4(extents.minimum, 1.0f));
|
||||
glm::vec3 maximum = glm::vec3(offset * glm::vec4(extents.maximum, 1.0f));
|
||||
Extents scaledExtents = { minimum, maximum };
|
||||
|
||||
return scaledExtents;
|
||||
}
|
||||
|
||||
// TODO: Move to graphics::Mesh when Sam's ready
|
||||
bool HFMModel::convexHullContains(const glm::vec3& point) const {
|
||||
if (!getUnscaledMeshExtents().containsPoint(point)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto checkEachPrimitive = [=](HFMMesh& mesh, QVector<int> indices, int primitiveSize) -> bool {
|
||||
// Check whether the point is "behind" all the primitives.
|
||||
int verticesSize = mesh.vertices.size();
|
||||
for (int j = 0;
|
||||
j < indices.size() - 2; // -2 in case the vertices aren't the right size -- we access j + 2 below
|
||||
j += primitiveSize) {
|
||||
if (indices[j] < verticesSize &&
|
||||
indices[j + 1] < verticesSize &&
|
||||
indices[j + 2] < verticesSize &&
|
||||
!isPointBehindTrianglesPlane(point,
|
||||
mesh.vertices[indices[j]],
|
||||
mesh.vertices[indices[j + 1]],
|
||||
mesh.vertices[indices[j + 2]])) {
|
||||
// it's not behind at least one so we bail
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
// Check that the point is contained in at least one convex mesh.
|
||||
for (auto mesh : meshes) {
|
||||
bool insideMesh = true;
|
||||
|
||||
// To be considered inside a convex mesh,
|
||||
// the point needs to be "behind" all the primitives respective planes.
|
||||
for (auto part : mesh.parts) {
|
||||
// run through all the triangles and quads
|
||||
if (!checkEachPrimitive(mesh, part.triangleIndices, 3) ||
|
||||
!checkEachPrimitive(mesh, part.quadIndices, 4)) {
|
||||
// If not, the point is outside, bail for this mesh
|
||||
insideMesh = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (insideMesh) {
|
||||
// It's inside this mesh, return true.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// It wasn't in any mesh, return false.
|
||||
return false;
|
||||
}
|
||||
|
||||
QString HFMModel::getModelNameOfMesh(int meshIndex) const {
|
||||
if (meshIndicesToModelNames.contains(meshIndex)) {
|
||||
return meshIndicesToModelNames.value(meshIndex);
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
int hfmModelMetaTypeId = qRegisterMetaType<HFMModel>();
|
||||
int hfmAnimationFrameMetaTypeId = qRegisterMetaType<HFMAnimationFrame>();
|
||||
int hfmAnimationFrameVectorMetaTypeId = qRegisterMetaType<QVector<HFMAnimationFrame>>();
|
||||
|
||||
|
||||
glm::vec3 parseVec3(const QString& string) {
|
||||
QStringList elements = string.split(',');
|
||||
if (elements.isEmpty()) {
|
||||
|
@ -362,108 +266,6 @@ HFMBlendshape extractBlendshape(const FBXNode& object) {
|
|||
return blendshape;
|
||||
}
|
||||
|
||||
using IndexAccessor = std::function<glm::vec3*(const HFMMesh&, int, int, glm::vec3*, glm::vec3&)>;
|
||||
|
||||
static void setTangents(const HFMMesh& mesh, const IndexAccessor& vertexAccessor, int firstIndex, int secondIndex,
|
||||
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals, QVector<glm::vec3>& tangents) {
|
||||
glm::vec3 vertex[2];
|
||||
glm::vec3 normal;
|
||||
glm::vec3* tangent = vertexAccessor(mesh, firstIndex, secondIndex, vertex, normal);
|
||||
if (tangent) {
|
||||
glm::vec3 bitangent = glm::cross(normal, vertex[1] - vertex[0]);
|
||||
if (glm::length(bitangent) < EPSILON) {
|
||||
return;
|
||||
}
|
||||
glm::vec2 texCoordDelta = mesh.texCoords.at(secondIndex) - mesh.texCoords.at(firstIndex);
|
||||
glm::vec3 normalizedNormal = glm::normalize(normal);
|
||||
*tangent += glm::cross(glm::angleAxis(-atan2f(-texCoordDelta.t, texCoordDelta.s), normalizedNormal) *
|
||||
glm::normalize(bitangent), normalizedNormal);
|
||||
}
|
||||
}
|
||||
|
||||
static void createTangents(const HFMMesh& mesh, bool generateFromTexCoords,
|
||||
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals, QVector<glm::vec3>& tangents,
|
||||
IndexAccessor accessor) {
|
||||
// if we have a normal map (and texture coordinates), we must compute tangents
|
||||
if (generateFromTexCoords && !mesh.texCoords.isEmpty()) {
|
||||
tangents.resize(vertices.size());
|
||||
|
||||
foreach(const HFMMeshPart& part, mesh.parts) {
|
||||
for (int i = 0; i < part.quadIndices.size(); i += 4) {
|
||||
setTangents(mesh, accessor, part.quadIndices.at(i), part.quadIndices.at(i + 1), vertices, normals, tangents);
|
||||
setTangents(mesh, accessor, part.quadIndices.at(i + 1), part.quadIndices.at(i + 2), vertices, normals, tangents);
|
||||
setTangents(mesh, accessor, part.quadIndices.at(i + 2), part.quadIndices.at(i + 3), vertices, normals, tangents);
|
||||
setTangents(mesh, accessor, part.quadIndices.at(i + 3), part.quadIndices.at(i), vertices, normals, tangents);
|
||||
}
|
||||
// <= size - 3 in order to prevent overflowing triangleIndices when (i % 3) != 0
|
||||
// This is most likely evidence of a further problem in extractMesh()
|
||||
for (int i = 0; i <= part.triangleIndices.size() - 3; i += 3) {
|
||||
setTangents(mesh, accessor, part.triangleIndices.at(i), part.triangleIndices.at(i + 1), vertices, normals, tangents);
|
||||
setTangents(mesh, accessor, part.triangleIndices.at(i + 1), part.triangleIndices.at(i + 2), vertices, normals, tangents);
|
||||
setTangents(mesh, accessor, part.triangleIndices.at(i + 2), part.triangleIndices.at(i), vertices, normals, tangents);
|
||||
}
|
||||
if ((part.triangleIndices.size() % 3) != 0) {
|
||||
qCDebug(modelformat) << "Error in extractHFMModel part.triangleIndices.size() is not divisible by three ";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _createBlendShapeTangents(HFMMesh& mesh, bool generateFromTexCoords, HFMBlendshape& blendShape);
|
||||
|
||||
void HFMMesh::createBlendShapeTangents(bool generateTangents) {
|
||||
for (auto& blendShape : blendshapes) {
|
||||
_createBlendShapeTangents(*this, generateTangents, blendShape);
|
||||
}
|
||||
}
|
||||
|
||||
void HFMMesh::createMeshTangents(bool generateFromTexCoords) {
|
||||
HFMMesh& mesh = *this;
|
||||
// This is the only workaround I've found to trick the compiler into understanding that mesh.tangents isn't
|
||||
// const in the lambda function.
|
||||
auto& tangents = mesh.tangents;
|
||||
createTangents(mesh, generateFromTexCoords, mesh.vertices, mesh.normals, mesh.tangents,
|
||||
[&](const HFMMesh& mesh, int firstIndex, int secondIndex, glm::vec3* outVertices, glm::vec3& outNormal) {
|
||||
outVertices[0] = mesh.vertices[firstIndex];
|
||||
outVertices[1] = mesh.vertices[secondIndex];
|
||||
outNormal = mesh.normals[firstIndex];
|
||||
return &(tangents[firstIndex]);
|
||||
});
|
||||
}
|
||||
|
||||
static void _createBlendShapeTangents(HFMMesh& mesh, bool generateFromTexCoords, HFMBlendshape& blendShape) {
|
||||
// Create lookup to get index in blend shape from vertex index in mesh
|
||||
std::vector<int> reverseIndices;
|
||||
reverseIndices.resize(mesh.vertices.size());
|
||||
std::iota(reverseIndices.begin(), reverseIndices.end(), 0);
|
||||
|
||||
for (int indexInBlendShape = 0; indexInBlendShape < blendShape.indices.size(); ++indexInBlendShape) {
|
||||
auto indexInMesh = blendShape.indices[indexInBlendShape];
|
||||
reverseIndices[indexInMesh] = indexInBlendShape;
|
||||
}
|
||||
|
||||
createTangents(mesh, generateFromTexCoords, blendShape.vertices, blendShape.normals, blendShape.tangents,
|
||||
[&](const HFMMesh& mesh, int firstIndex, int secondIndex, glm::vec3* outVertices, glm::vec3& outNormal) {
|
||||
const auto index1 = reverseIndices[firstIndex];
|
||||
const auto index2 = reverseIndices[secondIndex];
|
||||
|
||||
if (index1 < blendShape.vertices.size()) {
|
||||
outVertices[0] = blendShape.vertices[index1];
|
||||
if (index2 < blendShape.vertices.size()) {
|
||||
outVertices[1] = blendShape.vertices[index2];
|
||||
} else {
|
||||
// Index isn't in the blend shape so return vertex from mesh
|
||||
outVertices[1] = mesh.vertices[secondIndex];
|
||||
}
|
||||
outNormal = blendShape.normals[index1];
|
||||
return &blendShape.tangents[index1];
|
||||
} else {
|
||||
// Index isn't in blend shape so return nullptr
|
||||
return (glm::vec3*)nullptr;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
QVector<int> getIndices(const QVector<QString> ids, QVector<QString> modelIDs) {
|
||||
QVector<int> indices;
|
||||
foreach (const QString& id, ids) {
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
#ifndef hifi_FBXReader_h
|
||||
#define hifi_FBXReader_h
|
||||
|
||||
#include "FBX.h"
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QMetaType>
|
||||
#include <QSet>
|
||||
|
@ -28,6 +26,9 @@
|
|||
#include <Extents.h>
|
||||
#include <Transform.h>
|
||||
|
||||
#include "FBX.h"
|
||||
#include <hfm/HFM.h>
|
||||
|
||||
#include <graphics/Geometry.h>
|
||||
#include <graphics/Material.h>
|
||||
|
||||
|
|
|
@ -25,61 +25,7 @@
|
|||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "ModelFormatLogging.h"
|
||||
|
||||
void HFMMaterial::getTextureNames(QSet<QString>& textureList) const {
|
||||
if (!normalTexture.isNull()) {
|
||||
textureList.insert(normalTexture.name);
|
||||
}
|
||||
if (!albedoTexture.isNull()) {
|
||||
textureList.insert(albedoTexture.name);
|
||||
}
|
||||
if (!opacityTexture.isNull()) {
|
||||
textureList.insert(opacityTexture.name);
|
||||
}
|
||||
if (!glossTexture.isNull()) {
|
||||
textureList.insert(glossTexture.name);
|
||||
}
|
||||
if (!roughnessTexture.isNull()) {
|
||||
textureList.insert(roughnessTexture.name);
|
||||
}
|
||||
if (!specularTexture.isNull()) {
|
||||
textureList.insert(specularTexture.name);
|
||||
}
|
||||
if (!metallicTexture.isNull()) {
|
||||
textureList.insert(metallicTexture.name);
|
||||
}
|
||||
if (!emissiveTexture.isNull()) {
|
||||
textureList.insert(emissiveTexture.name);
|
||||
}
|
||||
if (!occlusionTexture.isNull()) {
|
||||
textureList.insert(occlusionTexture.name);
|
||||
}
|
||||
if (!scatteringTexture.isNull()) {
|
||||
textureList.insert(scatteringTexture.name);
|
||||
}
|
||||
if (!lightmapTexture.isNull()) {
|
||||
textureList.insert(lightmapTexture.name);
|
||||
}
|
||||
}
|
||||
|
||||
void HFMMaterial::setMaxNumPixelsPerTexture(int maxNumPixels) {
|
||||
normalTexture.maxNumPixels = maxNumPixels;
|
||||
albedoTexture.maxNumPixels = maxNumPixels;
|
||||
opacityTexture.maxNumPixels = maxNumPixels;
|
||||
glossTexture.maxNumPixels = maxNumPixels;
|
||||
roughnessTexture.maxNumPixels = maxNumPixels;
|
||||
specularTexture.maxNumPixels = maxNumPixels;
|
||||
metallicTexture.maxNumPixels = maxNumPixels;
|
||||
emissiveTexture.maxNumPixels = maxNumPixels;
|
||||
occlusionTexture.maxNumPixels = maxNumPixels;
|
||||
scatteringTexture.maxNumPixels = maxNumPixels;
|
||||
lightmapTexture.maxNumPixels = maxNumPixels;
|
||||
}
|
||||
|
||||
bool HFMMaterial::needTangentSpace() const {
|
||||
return !normalTexture.isNull();
|
||||
}
|
||||
#include <hfm/ModelFormatLogging.h>
|
||||
|
||||
HFMTexture FBXReader::getTexture(const QString& textureID) {
|
||||
HFMTexture texture;
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
#include <QFileInfo>
|
||||
#include <QHash>
|
||||
#include <LogHandler.h>
|
||||
#include "ModelFormatLogging.h"
|
||||
#include <hfm/ModelFormatLogging.h>
|
||||
|
||||
#include "FBXReader.h"
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#include <QtCore/QFileInfo>
|
||||
|
||||
#include <shared/NsightHelpers.h>
|
||||
#include "ModelFormatLogging.h"
|
||||
#include <hfm/ModelFormatLogging.h>
|
||||
|
||||
template<class T>
|
||||
int streamSize() {
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
#include <memory.h>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
#include "ModelFormatLogging.h"
|
||||
#include <hfm/ModelFormatLogging.h>
|
||||
#include "FBXReader.h"
|
||||
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
#include <ResourceManager.h>
|
||||
|
||||
#include "FBXReader.h"
|
||||
#include "ModelFormatLogging.h"
|
||||
#include <hfm/ModelFormatLogging.h>
|
||||
#include <shared/PlatformHacks.h>
|
||||
|
||||
QHash<QString, float> COMMENT_SCALE_HINTS = {{"This file uses centimeters as units", 1.0f / 100.0f},
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#include <QFileInfo>
|
||||
#include <graphics/BufferViewHelpers.h>
|
||||
#include <graphics/Geometry.h>
|
||||
#include "ModelFormatLogging.h"
|
||||
#include <hfm/ModelFormatLogging.h>
|
||||
|
||||
static QString formatFloat(double n) {
|
||||
// limit precision to 6, but don't output trailing zeros.
|
||||
|
|
7
libraries/hfm/CMakeLists.txt
Normal file
7
libraries/hfm/CMakeLists.txt
Normal file
|
@ -0,0 +1,7 @@
|
|||
set(TARGET_NAME hfm)
|
||||
setup_hifi_library()
|
||||
|
||||
link_hifi_libraries(shared)
|
||||
|
||||
include_hifi_library_headers(gpu)
|
||||
include_hifi_library_headers(graphics)
|
259
libraries/hfm/src/hfm/HFM.cpp
Normal file
259
libraries/hfm/src/hfm/HFM.cpp
Normal file
|
@ -0,0 +1,259 @@
|
|||
//
|
||||
// HFM.cpp
|
||||
// libraries/hfm/src
|
||||
//
|
||||
// Created by Sabrina Shanman on 2018/11/06.
|
||||
// Copyright 2018 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 "HFM.h"
|
||||
|
||||
#include "ModelFormatLogging.h"
|
||||
|
||||
void HFMMaterial::getTextureNames(QSet<QString>& textureList) const {
|
||||
if (!normalTexture.isNull()) {
|
||||
textureList.insert(normalTexture.name);
|
||||
}
|
||||
if (!albedoTexture.isNull()) {
|
||||
textureList.insert(albedoTexture.name);
|
||||
}
|
||||
if (!opacityTexture.isNull()) {
|
||||
textureList.insert(opacityTexture.name);
|
||||
}
|
||||
if (!glossTexture.isNull()) {
|
||||
textureList.insert(glossTexture.name);
|
||||
}
|
||||
if (!roughnessTexture.isNull()) {
|
||||
textureList.insert(roughnessTexture.name);
|
||||
}
|
||||
if (!specularTexture.isNull()) {
|
||||
textureList.insert(specularTexture.name);
|
||||
}
|
||||
if (!metallicTexture.isNull()) {
|
||||
textureList.insert(metallicTexture.name);
|
||||
}
|
||||
if (!emissiveTexture.isNull()) {
|
||||
textureList.insert(emissiveTexture.name);
|
||||
}
|
||||
if (!occlusionTexture.isNull()) {
|
||||
textureList.insert(occlusionTexture.name);
|
||||
}
|
||||
if (!scatteringTexture.isNull()) {
|
||||
textureList.insert(scatteringTexture.name);
|
||||
}
|
||||
if (!lightmapTexture.isNull()) {
|
||||
textureList.insert(lightmapTexture.name);
|
||||
}
|
||||
}
|
||||
|
||||
void HFMMaterial::setMaxNumPixelsPerTexture(int maxNumPixels) {
|
||||
normalTexture.maxNumPixels = maxNumPixels;
|
||||
albedoTexture.maxNumPixels = maxNumPixels;
|
||||
opacityTexture.maxNumPixels = maxNumPixels;
|
||||
glossTexture.maxNumPixels = maxNumPixels;
|
||||
roughnessTexture.maxNumPixels = maxNumPixels;
|
||||
specularTexture.maxNumPixels = maxNumPixels;
|
||||
metallicTexture.maxNumPixels = maxNumPixels;
|
||||
emissiveTexture.maxNumPixels = maxNumPixels;
|
||||
occlusionTexture.maxNumPixels = maxNumPixels;
|
||||
scatteringTexture.maxNumPixels = maxNumPixels;
|
||||
lightmapTexture.maxNumPixels = maxNumPixels;
|
||||
}
|
||||
|
||||
bool HFMMaterial::needTangentSpace() const {
|
||||
return !normalTexture.isNull();
|
||||
}
|
||||
|
||||
static void _createBlendShapeTangents(HFMMesh& mesh, bool generateFromTexCoords, HFMBlendshape& blendShape);
|
||||
|
||||
void HFMMesh::createBlendShapeTangents(bool generateTangents) {
|
||||
for (auto& blendShape : blendshapes) {
|
||||
_createBlendShapeTangents(*this, generateTangents, blendShape);
|
||||
}
|
||||
}
|
||||
|
||||
using IndexAccessor = std::function<glm::vec3*(const HFMMesh&, int, int, glm::vec3*, glm::vec3&)>;
|
||||
|
||||
static void setTangents(const HFMMesh& mesh, const IndexAccessor& vertexAccessor, int firstIndex, int secondIndex,
|
||||
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals, QVector<glm::vec3>& tangents) {
|
||||
glm::vec3 vertex[2];
|
||||
glm::vec3 normal;
|
||||
glm::vec3* tangent = vertexAccessor(mesh, firstIndex, secondIndex, vertex, normal);
|
||||
if (tangent) {
|
||||
glm::vec3 bitangent = glm::cross(normal, vertex[1] - vertex[0]);
|
||||
if (glm::length(bitangent) < EPSILON) {
|
||||
return;
|
||||
}
|
||||
glm::vec2 texCoordDelta = mesh.texCoords.at(secondIndex) - mesh.texCoords.at(firstIndex);
|
||||
glm::vec3 normalizedNormal = glm::normalize(normal);
|
||||
*tangent += glm::cross(glm::angleAxis(-atan2f(-texCoordDelta.t, texCoordDelta.s), normalizedNormal) *
|
||||
glm::normalize(bitangent), normalizedNormal);
|
||||
}
|
||||
}
|
||||
|
||||
static void createTangents(const HFMMesh& mesh, bool generateFromTexCoords,
|
||||
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals, QVector<glm::vec3>& tangents,
|
||||
IndexAccessor accessor) {
|
||||
// if we have a normal map (and texture coordinates), we must compute tangents
|
||||
if (generateFromTexCoords && !mesh.texCoords.isEmpty()) {
|
||||
tangents.resize(vertices.size());
|
||||
|
||||
foreach(const HFMMeshPart& part, mesh.parts) {
|
||||
for (int i = 0; i < part.quadIndices.size(); i += 4) {
|
||||
setTangents(mesh, accessor, part.quadIndices.at(i), part.quadIndices.at(i + 1), vertices, normals, tangents);
|
||||
setTangents(mesh, accessor, part.quadIndices.at(i + 1), part.quadIndices.at(i + 2), vertices, normals, tangents);
|
||||
setTangents(mesh, accessor, part.quadIndices.at(i + 2), part.quadIndices.at(i + 3), vertices, normals, tangents);
|
||||
setTangents(mesh, accessor, part.quadIndices.at(i + 3), part.quadIndices.at(i), vertices, normals, tangents);
|
||||
}
|
||||
// <= size - 3 in order to prevent overflowing triangleIndices when (i % 3) != 0
|
||||
// This is most likely evidence of a further problem in extractMesh()
|
||||
for (int i = 0; i <= part.triangleIndices.size() - 3; i += 3) {
|
||||
setTangents(mesh, accessor, part.triangleIndices.at(i), part.triangleIndices.at(i + 1), vertices, normals, tangents);
|
||||
setTangents(mesh, accessor, part.triangleIndices.at(i + 1), part.triangleIndices.at(i + 2), vertices, normals, tangents);
|
||||
setTangents(mesh, accessor, part.triangleIndices.at(i + 2), part.triangleIndices.at(i), vertices, normals, tangents);
|
||||
}
|
||||
if ((part.triangleIndices.size() % 3) != 0) {
|
||||
qCDebug(modelformat) << "Error in extractHFMModel part.triangleIndices.size() is not divisible by three ";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HFMMesh::createMeshTangents(bool generateFromTexCoords) {
|
||||
HFMMesh& mesh = *this;
|
||||
// This is the only workaround I've found to trick the compiler into understanding that mesh.tangents isn't
|
||||
// const in the lambda function.
|
||||
auto& tangents = mesh.tangents;
|
||||
createTangents(mesh, generateFromTexCoords, mesh.vertices, mesh.normals, mesh.tangents,
|
||||
[&](const HFMMesh& mesh, int firstIndex, int secondIndex, glm::vec3* outVertices, glm::vec3& outNormal) {
|
||||
outVertices[0] = mesh.vertices[firstIndex];
|
||||
outVertices[1] = mesh.vertices[secondIndex];
|
||||
outNormal = mesh.normals[firstIndex];
|
||||
return &(tangents[firstIndex]);
|
||||
});
|
||||
}
|
||||
|
||||
static void _createBlendShapeTangents(HFMMesh& mesh, bool generateFromTexCoords, HFMBlendshape& blendShape) {
|
||||
// Create lookup to get index in blend shape from vertex index in mesh
|
||||
std::vector<int> reverseIndices;
|
||||
reverseIndices.resize(mesh.vertices.size());
|
||||
std::iota(reverseIndices.begin(), reverseIndices.end(), 0);
|
||||
|
||||
for (int indexInBlendShape = 0; indexInBlendShape < blendShape.indices.size(); ++indexInBlendShape) {
|
||||
auto indexInMesh = blendShape.indices[indexInBlendShape];
|
||||
reverseIndices[indexInMesh] = indexInBlendShape;
|
||||
}
|
||||
|
||||
createTangents(mesh, generateFromTexCoords, blendShape.vertices, blendShape.normals, blendShape.tangents,
|
||||
[&](const HFMMesh& mesh, int firstIndex, int secondIndex, glm::vec3* outVertices, glm::vec3& outNormal) {
|
||||
const auto index1 = reverseIndices[firstIndex];
|
||||
const auto index2 = reverseIndices[secondIndex];
|
||||
|
||||
if (index1 < blendShape.vertices.size()) {
|
||||
outVertices[0] = blendShape.vertices[index1];
|
||||
if (index2 < blendShape.vertices.size()) {
|
||||
outVertices[1] = blendShape.vertices[index2];
|
||||
} else {
|
||||
// Index isn't in the blend shape so return vertex from mesh
|
||||
outVertices[1] = mesh.vertices[secondIndex];
|
||||
}
|
||||
outNormal = blendShape.normals[index1];
|
||||
return &blendShape.tangents[index1];
|
||||
} else {
|
||||
// Index isn't in blend shape so return nullptr
|
||||
return (glm::vec3*)nullptr;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
QStringList HFMModel::getJointNames() const {
|
||||
QStringList names;
|
||||
foreach (const HFMJoint& joint, joints) {
|
||||
names.append(joint.name);
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
bool HFMModel::hasBlendedMeshes() const {
|
||||
if (!meshes.isEmpty()) {
|
||||
foreach (const HFMMesh& mesh, meshes) {
|
||||
if (!mesh.blendshapes.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Extents HFMModel::getUnscaledMeshExtents() const {
|
||||
const Extents& extents = meshExtents;
|
||||
|
||||
// even though our caller asked for "unscaled" we need to include any fst scaling, translation, and rotation, which
|
||||
// is captured in the offset matrix
|
||||
glm::vec3 minimum = glm::vec3(offset * glm::vec4(extents.minimum, 1.0f));
|
||||
glm::vec3 maximum = glm::vec3(offset * glm::vec4(extents.maximum, 1.0f));
|
||||
Extents scaledExtents = { minimum, maximum };
|
||||
|
||||
return scaledExtents;
|
||||
}
|
||||
|
||||
// TODO: Move to graphics::Mesh when Sam's ready
|
||||
bool HFMModel::convexHullContains(const glm::vec3& point) const {
|
||||
if (!getUnscaledMeshExtents().containsPoint(point)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto checkEachPrimitive = [=](HFMMesh& mesh, QVector<int> indices, int primitiveSize) -> bool {
|
||||
// Check whether the point is "behind" all the primitives.
|
||||
int verticesSize = mesh.vertices.size();
|
||||
for (int j = 0;
|
||||
j < indices.size() - 2; // -2 in case the vertices aren't the right size -- we access j + 2 below
|
||||
j += primitiveSize) {
|
||||
if (indices[j] < verticesSize &&
|
||||
indices[j + 1] < verticesSize &&
|
||||
indices[j + 2] < verticesSize &&
|
||||
!isPointBehindTrianglesPlane(point,
|
||||
mesh.vertices[indices[j]],
|
||||
mesh.vertices[indices[j + 1]],
|
||||
mesh.vertices[indices[j + 2]])) {
|
||||
// it's not behind at least one so we bail
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
// Check that the point is contained in at least one convex mesh.
|
||||
for (auto mesh : meshes) {
|
||||
bool insideMesh = true;
|
||||
|
||||
// To be considered inside a convex mesh,
|
||||
// the point needs to be "behind" all the primitives respective planes.
|
||||
for (auto part : mesh.parts) {
|
||||
// run through all the triangles and quads
|
||||
if (!checkEachPrimitive(mesh, part.triangleIndices, 3) ||
|
||||
!checkEachPrimitive(mesh, part.quadIndices, 4)) {
|
||||
// If not, the point is outside, bail for this mesh
|
||||
insideMesh = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (insideMesh) {
|
||||
// It's inside this mesh, return true.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// It wasn't in any mesh, return false.
|
||||
return false;
|
||||
}
|
||||
|
||||
QString HFMModel::getModelNameOfMesh(int meshIndex) const {
|
||||
if (meshIndicesToModelNames.contains(meshIndex)) {
|
||||
return meshIndicesToModelNames.value(meshIndex);
|
||||
}
|
||||
return QString();
|
||||
}
|
344
libraries/hfm/src/hfm/HFM.h
Normal file
344
libraries/hfm/src/hfm/HFM.h
Normal file
|
@ -0,0 +1,344 @@
|
|||
//
|
||||
// HFM.h
|
||||
// libraries/hfm/src
|
||||
//
|
||||
// Created by Sabrina Shanman on 2018/11/02.
|
||||
// Copyright 2018 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_HFM_h_
|
||||
#define hifi_HFM_h_
|
||||
|
||||
#include <QMetaType>
|
||||
#include <QSet>
|
||||
#include <QVector>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
#include <Extents.h>
|
||||
#include <Transform.h>
|
||||
|
||||
#include <graphics/Geometry.h>
|
||||
#include <graphics/Material.h>
|
||||
|
||||
const int MAX_NUM_PIXELS_FOR_FBX_TEXTURE = 2048 * 2048;
|
||||
|
||||
// High Fidelity Model namespace
|
||||
namespace hfm {
|
||||
|
||||
/// A single blendshape.
|
||||
class Blendshape {
|
||||
public:
|
||||
QVector<int> indices;
|
||||
QVector<glm::vec3> vertices;
|
||||
QVector<glm::vec3> normals;
|
||||
QVector<glm::vec3> tangents;
|
||||
};
|
||||
|
||||
struct JointShapeInfo {
|
||||
// same units and frame as Joint.translation
|
||||
glm::vec3 avgPoint;
|
||||
std::vector<float> dots;
|
||||
std::vector<glm::vec3> points;
|
||||
std::vector<glm::vec3> debugLines;
|
||||
};
|
||||
|
||||
/// A single joint (transformation node).
|
||||
class Joint {
|
||||
public:
|
||||
JointShapeInfo 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.
|
||||
class Cluster {
|
||||
public:
|
||||
|
||||
int jointIndex;
|
||||
glm::mat4 inverseBindMatrix;
|
||||
Transform inverseBindTransform;
|
||||
};
|
||||
|
||||
/// A texture map.
|
||||
class Texture {
|
||||
public:
|
||||
QString id;
|
||||
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 MeshPart {
|
||||
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 Material {
|
||||
public:
|
||||
Material() {};
|
||||
Material(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 };
|
||||
|
||||
float bumpMultiplier { 1.0f }; // TODO: to be implemented
|
||||
|
||||
QString materialID;
|
||||
QString name;
|
||||
QString shadingModel;
|
||||
graphics::MaterialPointer _material;
|
||||
|
||||
Texture normalTexture;
|
||||
Texture albedoTexture;
|
||||
Texture opacityTexture;
|
||||
Texture glossTexture;
|
||||
Texture roughnessTexture;
|
||||
Texture specularTexture;
|
||||
Texture metallicTexture;
|
||||
Texture emissiveTexture;
|
||||
Texture occlusionTexture;
|
||||
Texture scatteringTexture;
|
||||
Texture 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).
|
||||
class Mesh {
|
||||
public:
|
||||
|
||||
QVector<MeshPart> 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<uint16_t> clusterWeights;
|
||||
QVector<int32_t> originalIndices;
|
||||
|
||||
QVector<Cluster> clusters;
|
||||
|
||||
Extents meshExtents;
|
||||
glm::mat4 modelTransform;
|
||||
|
||||
QVector<Blendshape> blendshapes;
|
||||
|
||||
unsigned int meshIndex; // the order the meshes appeared in the object file
|
||||
|
||||
graphics::MeshPointer _mesh;
|
||||
bool wasCompressed { false };
|
||||
|
||||
void createMeshTangents(bool generateFromTexCoords);
|
||||
void createBlendShapeTangents(bool generateTangents);
|
||||
};
|
||||
|
||||
/**jsdoc
|
||||
* @typedef {object} FBXAnimationFrame
|
||||
* @property {Quat[]} rotations
|
||||
* @property {Vec3[]} translations
|
||||
*/
|
||||
/// A single animation frame.
|
||||
class AnimationFrame {
|
||||
public:
|
||||
QVector<glm::quat> rotations;
|
||||
QVector<glm::vec3> translations;
|
||||
};
|
||||
|
||||
/// A light.
|
||||
class Light {
|
||||
public:
|
||||
QString name;
|
||||
Transform transform;
|
||||
float intensity;
|
||||
float fogValue;
|
||||
glm::vec3 color;
|
||||
|
||||
Light() :
|
||||
name(),
|
||||
transform(),
|
||||
intensity(1.0f),
|
||||
fogValue(0.0f),
|
||||
color(1.0f)
|
||||
{}
|
||||
};
|
||||
|
||||
/// The runtime model format.
|
||||
class Model {
|
||||
public:
|
||||
using Pointer = std::shared_ptr<Model>;
|
||||
|
||||
QString originalURL;
|
||||
QString author;
|
||||
QString applicationName; ///< the name of the application that generated the model
|
||||
|
||||
QVector<Joint> joints;
|
||||
QHash<QString, int> jointIndices; ///< 1-based, so as to more easily detect missing indices
|
||||
bool hasSkeletonJoints;
|
||||
|
||||
QVector<Mesh> meshes;
|
||||
QVector<QString> scripts;
|
||||
|
||||
QHash<QString, Material> 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<AnimationFrame> 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;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
class ExtractedMesh {
|
||||
public:
|
||||
hfm::Mesh mesh;
|
||||
QMultiHash<int, int> newIndices;
|
||||
QVector<QHash<int, int> > blendshapeIndexMaps;
|
||||
QVector<QPair<int, int> > partMaterialTextures;
|
||||
QHash<QString, size_t> texcoordSetMap;
|
||||
};
|
||||
|
||||
typedef hfm::Blendshape HFMBlendshape;
|
||||
typedef hfm::JointShapeInfo HFMJointShapeInfo;
|
||||
typedef hfm::Joint HFMJoint;
|
||||
typedef hfm::Cluster HFMCluster;
|
||||
typedef hfm::Texture HFMTexture;
|
||||
typedef hfm::MeshPart HFMMeshPart;
|
||||
typedef hfm::Material HFMMaterial;
|
||||
typedef hfm::Mesh HFMMesh;
|
||||
typedef hfm::AnimationFrame HFMAnimationFrame;
|
||||
typedef hfm::Light HFMLight;
|
||||
typedef hfm::Model HFMModel;
|
||||
|
||||
Q_DECLARE_METATYPE(HFMAnimationFrame)
|
||||
Q_DECLARE_METATYPE(QVector<HFMAnimationFrame>)
|
||||
Q_DECLARE_METATYPE(HFMModel)
|
||||
Q_DECLARE_METATYPE(HFMModel::Pointer)
|
||||
|
||||
#endif // hifi_HFM_h_
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// ModelFormatLogging.cpp
|
||||
// libraries/fbx/src
|
||||
// libraries/hfm/src
|
||||
//
|
||||
// Created by Seth Alves on 4/6/15.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// ModelFormatLogging.h
|
||||
// libraries/fbx/src
|
||||
// libraries/hfm/src
|
||||
//
|
||||
// Created by Seth Alves on 4/6/15.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
|
@ -2,3 +2,4 @@ set(TARGET_NAME model-networking)
|
|||
setup_hifi_library()
|
||||
link_hifi_libraries(shared shaders networking graphics fbx ktx image gl)
|
||||
include_hifi_library_headers(gpu)
|
||||
include_hifi_library_headers(hfm)
|
||||
|
|
|
@ -7,6 +7,7 @@ link_hifi_libraries(shared task ktx gpu shaders graphics graphics-scripting mode
|
|||
include_hifi_library_headers(audio)
|
||||
include_hifi_library_headers(networking)
|
||||
include_hifi_library_headers(octree)
|
||||
include_hifi_library_headers(hfm)
|
||||
|
||||
# tell CMake to exclude qrc_fonts.cpp for policy CMP0071
|
||||
set_property(SOURCE qrc_fonts.cpp PROPERTY SKIP_AUTOMOC ON)
|
||||
|
|
|
@ -17,6 +17,6 @@ if (NOT ANDROID)
|
|||
|
||||
endif ()
|
||||
|
||||
link_hifi_libraries(shared networking octree shaders gpu procedural graphics model-networking ktx recording avatars fbx entities controllers animation audio physics image midi)
|
||||
link_hifi_libraries(shared networking octree shaders gpu procedural graphics model-networking ktx recording avatars fbx hfm entities controllers animation audio physics image midi)
|
||||
# ui includes gl, but link_hifi_libraries does not use transitive includes, so gl must be explicit
|
||||
include_hifi_library_headers(gl)
|
||||
|
|
|
@ -11,7 +11,7 @@ if (WIN32 AND (NOT USE_GLES))
|
|||
setup_hifi_plugin(Gui Qml Multimedia)
|
||||
link_hifi_libraries(shared task gl qml networking controllers ui
|
||||
plugins display-plugins ui-plugins input-plugins script-engine
|
||||
audio-client render-utils graphics shaders gpu render model-networking fbx ktx image procedural ${PLATFORM_GL_BACKEND})
|
||||
audio-client render-utils graphics shaders gpu render model-networking hfm fbx ktx image procedural ${PLATFORM_GL_BACKEND})
|
||||
include_hifi_library_headers(octree)
|
||||
|
||||
target_openvr()
|
||||
|
|
|
@ -6,7 +6,7 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
|
|||
link_hifi_libraries(
|
||||
shared task networking gl
|
||||
ktx shaders gpu procedural octree image
|
||||
graphics model-networking fbx animation
|
||||
graphics model-networking fbx hfm animation
|
||||
script-engine render render-utils
|
||||
${PLATFORM_GL_BACKEND}
|
||||
)
|
||||
|
|
|
@ -11,8 +11,6 @@
|
|||
|
||||
#include <render/ShapePipeline.h>
|
||||
|
||||
class HFMModel;
|
||||
|
||||
class TestFbx : public GpuTestBase {
|
||||
size_t _partCount { 0 };
|
||||
graphics::Material _material;
|
||||
|
|
|
@ -17,7 +17,7 @@ link_hifi_libraries(
|
|||
ktx image octree
|
||||
shaders gl gpu ${PLATFORM_GL_BACKEND}
|
||||
render render-utils
|
||||
graphics fbx model-networking graphics-scripting
|
||||
graphics hfm fbx model-networking graphics-scripting
|
||||
entities entities-renderer audio avatars script-engine
|
||||
physics procedural midi qml ui
|
||||
${PLATFORM_GL_BACKEND}
|
||||
|
|
|
@ -15,7 +15,7 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
|
|||
link_hifi_libraries(
|
||||
shared task networking octree
|
||||
shaders gl gpu render ktx image animation
|
||||
graphics fbx model-networking
|
||||
graphics hfm fbx model-networking
|
||||
render-utils
|
||||
entities entities-renderer audio avatars
|
||||
script-engine physics
|
||||
|
|
|
@ -2,7 +2,7 @@ set(TARGET_NAME oven)
|
|||
|
||||
setup_hifi_project(Widgets Gui Concurrent)
|
||||
|
||||
link_hifi_libraries(networking shared image gpu ktx fbx baking graphics)
|
||||
link_hifi_libraries(networking shared image gpu ktx fbx hfm baking graphics)
|
||||
|
||||
setup_memory_debugger()
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
set(TARGET_NAME skeleton-dump)
|
||||
setup_hifi_project(Core)
|
||||
setup_memory_debugger()
|
||||
link_hifi_libraries(shared fbx graphics gpu gl animation)
|
||||
|
||||
link_hifi_libraries(shared fbx hfm graphics gpu gl animation)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
set(TARGET_NAME vhacd-util)
|
||||
setup_hifi_project(Core)
|
||||
link_hifi_libraries(shared fbx graphics gpu gl)
|
||||
link_hifi_libraries(shared fbx hfm graphics gpu gl)
|
||||
|
||||
add_dependency_external_projects(vhacd)
|
||||
|
||||
|
|
Loading…
Reference in a new issue