Merge pull request #14348 from sabrina-shanman/hfm_library

(case 19302) Create the HFM library and namespace
This commit is contained in:
Brad Hefta-Gaub 2018-11-13 12:03:53 -08:00 committed by GitHub
commit 153b8920be
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 649 additions and 593 deletions

View file

@ -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()

View file

@ -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
)

View file

@ -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

View file

@ -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;

View file

@ -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;
};

View file

@ -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()

View file

@ -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)

View file

@ -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()

View file

@ -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)>;

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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()

View file

@ -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_

View file

@ -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) {

View file

@ -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>

View file

@ -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;

View file

@ -31,7 +31,7 @@
#include <QFileInfo>
#include <QHash>
#include <LogHandler.h>
#include "ModelFormatLogging.h"
#include <hfm/ModelFormatLogging.h>
#include "FBXReader.h"

View file

@ -22,7 +22,7 @@
#include <QtCore/QFileInfo>
#include <shared/NsightHelpers.h>
#include "ModelFormatLogging.h"
#include <hfm/ModelFormatLogging.h>
template<class T>
int streamSize() {

View file

@ -14,7 +14,7 @@
#include <memory.h>
#include <QtNetwork/QNetworkReply>
#include "ModelFormatLogging.h"
#include <hfm/ModelFormatLogging.h>
#include "FBXReader.h"

View file

@ -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},

View file

@ -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.

View 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)

View 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
View 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_

View file

@ -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.

View file

@ -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.

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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()

View file

@ -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}
)

View file

@ -11,8 +11,6 @@
#include <render/ShapePipeline.h>
class HFMModel;
class TestFbx : public GpuTestBase {
size_t _partCount { 0 };
graphics::Material _material;

View file

@ -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}

View file

@ -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

View file

@ -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()

View file

@ -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)

View file

@ -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)