mirror of
https://github.com/lubosz/overte.git
synced 2025-04-08 04:42:20 +02:00
Merge pull request #14770 from sabrina-shanman/hfm_prep_tangents
(case 20822) Add normal/tangent generation to HFM preparation step
This commit is contained in:
commit
9b09c09aeb
18 changed files with 648 additions and 163 deletions
|
@ -1480,9 +1480,6 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
|
|||
}
|
||||
}
|
||||
|
||||
extracted.mesh.createMeshTangents(generateTangents);
|
||||
extracted.mesh.createBlendShapeTangents(generateTangents);
|
||||
|
||||
// find the clusters with which the mesh is associated
|
||||
QVector<QString> clusterIDs;
|
||||
foreach (const QString& childID, _connectionChildMap.values(it.key())) {
|
||||
|
|
|
@ -67,108 +67,6 @@ 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) {
|
||||
|
|
|
@ -239,9 +239,6 @@ public:
|
|||
|
||||
graphics::MeshPointer _mesh;
|
||||
bool wasCompressed { false };
|
||||
|
||||
void createMeshTangents(bool generateFromTexCoords);
|
||||
void createBlendShapeTangents(bool generateTangents);
|
||||
};
|
||||
|
||||
/**jsdoc
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
set(TARGET_NAME model-baker)
|
||||
setup_hifi_library()
|
||||
|
||||
link_hifi_libraries(shared task gpu graphics)
|
||||
|
||||
include_hifi_library_headers(hfm)
|
||||
link_hifi_libraries(shared task gpu graphics hfm)
|
||||
|
|
|
@ -15,26 +15,65 @@
|
|||
|
||||
#include "BakerTypes.h"
|
||||
#include "BuildGraphicsMeshTask.h"
|
||||
#include "CalculateMeshNormalsTask.h"
|
||||
#include "CalculateMeshTangentsTask.h"
|
||||
#include "CalculateBlendshapeNormalsTask.h"
|
||||
#include "CalculateBlendshapeTangentsTask.h"
|
||||
|
||||
namespace baker {
|
||||
|
||||
class GetModelPartsTask {
|
||||
public:
|
||||
using Input = hfm::Model::Pointer;
|
||||
using Output = VaryingSet3<std::vector<hfm::Mesh>, hifi::URL, MeshIndicesToModelNames>;
|
||||
using Output = VaryingSet5<std::vector<hfm::Mesh>, hifi::URL, baker::MeshIndicesToModelNames, baker::BlendshapesPerMesh, QHash<QString, hfm::Material>>;
|
||||
using JobModel = Job::ModelIO<GetModelPartsTask, Input, Output>;
|
||||
|
||||
void run(const BakeContextPointer& context, const Input& input, Output& output) {
|
||||
auto& hfmModelIn = input;
|
||||
const auto& hfmModelIn = input;
|
||||
output.edit0() = hfmModelIn->meshes.toStdVector();
|
||||
output.edit1() = hfmModelIn->originalURL;
|
||||
output.edit2() = hfmModelIn->meshIndicesToModelNames;
|
||||
auto& blendshapesPerMesh = output.edit3();
|
||||
blendshapesPerMesh.reserve(hfmModelIn->meshes.size());
|
||||
for (int i = 0; i < hfmModelIn->meshes.size(); i++) {
|
||||
blendshapesPerMesh.push_back(hfmModelIn->meshes[i].blendshapes.toStdVector());
|
||||
}
|
||||
output.edit4() = hfmModelIn->materials;
|
||||
}
|
||||
};
|
||||
|
||||
class BuildBlendshapesTask {
|
||||
public:
|
||||
using Input = VaryingSet3<BlendshapesPerMesh, std::vector<NormalsPerBlendshape>, std::vector<TangentsPerBlendshape>>;
|
||||
using Output = BlendshapesPerMesh;
|
||||
using JobModel = Job::ModelIO<BuildBlendshapesTask, Input, Output>;
|
||||
|
||||
void run(const BakeContextPointer& context, const Input& input, Output& output) {
|
||||
const auto& blendshapesPerMeshIn = input.get0();
|
||||
const auto& normalsPerBlendshapePerMesh = input.get1();
|
||||
const auto& tangentsPerBlendshapePerMesh = input.get2();
|
||||
auto& blendshapesPerMeshOut = output;
|
||||
|
||||
blendshapesPerMeshOut = blendshapesPerMeshIn;
|
||||
|
||||
for (int i = 0; i < (int)blendshapesPerMeshOut.size(); i++) {
|
||||
const auto& normalsPerBlendshape = normalsPerBlendshapePerMesh[i];
|
||||
const auto& tangentsPerBlendshape = tangentsPerBlendshapePerMesh[i];
|
||||
auto& blendshapesOut = blendshapesPerMeshOut[i];
|
||||
for (int j = 0; j < (int)blendshapesOut.size(); j++) {
|
||||
const auto& normals = normalsPerBlendshape[j];
|
||||
const auto& tangents = tangentsPerBlendshape[j];
|
||||
auto& blendshape = blendshapesOut[j];
|
||||
blendshape.normals = QVector<glm::vec3>::fromStdVector(normals);
|
||||
blendshape.tangents = QVector<glm::vec3>::fromStdVector(tangents);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class BuildMeshesTask {
|
||||
public:
|
||||
using Input = VaryingSet4<std::vector<hfm::Mesh>, std::vector<graphics::MeshPointer>, TangentsPerMesh, BlendshapesPerMesh>;
|
||||
using Input = VaryingSet5<std::vector<hfm::Mesh>, std::vector<graphics::MeshPointer>, NormalsPerMesh, TangentsPerMesh, BlendshapesPerMesh>;
|
||||
using Output = std::vector<hfm::Mesh>;
|
||||
using JobModel = Job::ModelIO<BuildMeshesTask, Input, Output>;
|
||||
|
||||
|
@ -42,13 +81,15 @@ namespace baker {
|
|||
auto& meshesIn = input.get0();
|
||||
int numMeshes = (int)meshesIn.size();
|
||||
auto& graphicsMeshesIn = input.get1();
|
||||
auto& tangentsPerMeshIn = input.get2();
|
||||
auto& blendshapesPerMeshIn = input.get3();
|
||||
auto& normalsPerMeshIn = input.get2();
|
||||
auto& tangentsPerMeshIn = input.get3();
|
||||
auto& blendshapesPerMeshIn = input.get4();
|
||||
|
||||
auto meshesOut = meshesIn;
|
||||
for (int i = 0; i < numMeshes; i++) {
|
||||
auto& meshOut = meshesOut[i];
|
||||
meshOut._mesh = graphicsMeshesIn[i];
|
||||
meshOut.normals = QVector<glm::vec3>::fromStdVector(normalsPerMeshIn[i]);
|
||||
meshOut.tangents = QVector<glm::vec3>::fromStdVector(tangentsPerMeshIn[i]);
|
||||
meshOut.blendshapes = QVector<hfm::Blendshape>::fromStdVector(blendshapesPerMeshIn[i]);
|
||||
}
|
||||
|
@ -80,17 +121,27 @@ namespace baker {
|
|||
const auto meshesIn = modelPartsIn.getN<GetModelPartsTask::Output>(0);
|
||||
const auto url = modelPartsIn.getN<GetModelPartsTask::Output>(1);
|
||||
const auto meshIndicesToModelNames = modelPartsIn.getN<GetModelPartsTask::Output>(2);
|
||||
const auto blendshapesPerMeshIn = modelPartsIn.getN<GetModelPartsTask::Output>(3);
|
||||
const auto materials = modelPartsIn.getN<GetModelPartsTask::Output>(4);
|
||||
|
||||
// Calculate normals and tangents for meshes and blendshapes if they do not exist
|
||||
// Note: Normals are never calculated here for OBJ models. OBJ files optionally define normals on a per-face basis, so for consistency normals are calculated beforehand in OBJSerializer.
|
||||
const auto normalsPerMesh = model.addJob<CalculateMeshNormalsTask>("CalculateMeshNormals", meshesIn);
|
||||
const auto calculateMeshTangentsInputs = CalculateMeshTangentsTask::Input(normalsPerMesh, meshesIn, materials).asVarying();
|
||||
const auto tangentsPerMesh = model.addJob<CalculateMeshTangentsTask>("CalculateMeshTangents", calculateMeshTangentsInputs);
|
||||
const auto calculateBlendshapeNormalsInputs = CalculateBlendshapeNormalsTask::Input(blendshapesPerMeshIn, meshesIn).asVarying();
|
||||
const auto normalsPerBlendshapePerMesh = model.addJob<CalculateBlendshapeNormalsTask>("CalculateBlendshapeNormals", calculateBlendshapeNormalsInputs);
|
||||
const auto calculateBlendshapeTangentsInputs = CalculateBlendshapeTangentsTask::Input(normalsPerBlendshapePerMesh, blendshapesPerMeshIn, meshesIn, materials).asVarying();
|
||||
const auto tangentsPerBlendshapePerMesh = model.addJob<CalculateBlendshapeTangentsTask>("CalculateBlendshapeTangents", calculateBlendshapeTangentsInputs);
|
||||
|
||||
// Build the graphics::MeshPointer for each hfm::Mesh
|
||||
const auto buildGraphicsMeshInputs = BuildGraphicsMeshTask::Input(meshesIn, url, meshIndicesToModelNames).asVarying();
|
||||
const auto buildGraphicsMeshOutputs = model.addJob<BuildGraphicsMeshTask>("BuildGraphicsMesh", buildGraphicsMeshInputs);
|
||||
const auto graphicsMeshes = buildGraphicsMeshOutputs.getN<BuildGraphicsMeshTask::Output>(0);
|
||||
// TODO: Move tangent/blendshape validation/calculation to an earlier step
|
||||
const auto tangentsPerMesh = buildGraphicsMeshOutputs.getN<BuildGraphicsMeshTask::Output>(1);
|
||||
const auto blendshapesPerMesh = buildGraphicsMeshOutputs.getN<BuildGraphicsMeshTask::Output>(2);
|
||||
const auto buildGraphicsMeshInputs = BuildGraphicsMeshTask::Input(meshesIn, url, meshIndicesToModelNames, normalsPerMesh, tangentsPerMesh).asVarying();
|
||||
const auto graphicsMeshes = model.addJob<BuildGraphicsMeshTask>("BuildGraphicsMesh", buildGraphicsMeshInputs);
|
||||
|
||||
// Combine the outputs into a new hfm::Model
|
||||
const auto buildMeshesInputs = BuildMeshesTask::Input(meshesIn, graphicsMeshes, tangentsPerMesh, blendshapesPerMesh).asVarying();
|
||||
const auto buildBlendshapesInputs = BuildBlendshapesTask::Input(blendshapesPerMeshIn, normalsPerBlendshapePerMesh, tangentsPerBlendshapePerMesh).asVarying();
|
||||
const auto blendshapesPerMeshOut = model.addJob<BuildBlendshapesTask>("BuildBlendshapes", buildBlendshapesInputs);
|
||||
const auto buildMeshesInputs = BuildMeshesTask::Input(meshesIn, graphicsMeshes, normalsPerMesh, tangentsPerMesh, blendshapesPerMeshOut).asVarying();
|
||||
const auto meshesOut = model.addJob<BuildMeshesTask>("BuildMeshes", buildMeshesInputs);
|
||||
const auto buildModelInputs = BuildModelTask::Input(hfmModelIn, meshesOut).asVarying();
|
||||
hfmModelOut = model.addJob<BuildModelTask>("BuildModel", buildModelInputs);
|
||||
|
|
|
@ -15,10 +15,25 @@
|
|||
#include <hfm/HFM.h>
|
||||
|
||||
namespace baker {
|
||||
using MeshIndices = std::vector<int>;
|
||||
using IndicesPerMesh = std::vector<std::vector<int>>;
|
||||
using VerticesPerMesh = std::vector<std::vector<glm::vec3>>;
|
||||
using MeshNormals = std::vector<glm::vec3>;
|
||||
using NormalsPerMesh = std::vector<std::vector<glm::vec3>>;
|
||||
using MeshTangents = std::vector<glm::vec3>;
|
||||
using TangentsPerMesh = std::vector<std::vector<glm::vec3>>;
|
||||
|
||||
using Blendshapes = std::vector<hfm::Blendshape>;
|
||||
using BlendshapesPerMesh = std::vector<std::vector<hfm::Blendshape>>;
|
||||
using BlendshapeVertices = std::vector<glm::vec3>;
|
||||
using BlendshapeNormals = std::vector<glm::vec3>;
|
||||
using BlendshapeIndices = std::vector<int>;
|
||||
using VerticesPerBlendshape = std::vector<std::vector<glm::vec3>>;
|
||||
using NormalsPerBlendshape = std::vector<std::vector<glm::vec3>>;
|
||||
using IndicesPerBlendshape = std::vector<std::vector<int>>;
|
||||
using BlendshapeTangents = std::vector<glm::vec3>;
|
||||
using TangentsPerBlendshape = std::vector<std::vector<glm::vec3>>;
|
||||
|
||||
using MeshIndicesToModelNames = QHash<int, QString>;
|
||||
};
|
||||
|
||||
|
|
|
@ -26,9 +26,18 @@ glm::vec3 normalizeDirForPacking(const glm::vec3& dir) {
|
|||
return dir;
|
||||
}
|
||||
|
||||
void buildGraphicsMesh(const hfm::Mesh& hfmMesh, graphics::MeshPointer& graphicsMeshPointer, baker::MeshTangents& meshTangents, baker::Blendshapes& blendshapes) {
|
||||
void buildGraphicsMesh(const hfm::Mesh& hfmMesh, graphics::MeshPointer& graphicsMeshPointer, const baker::MeshNormals& meshNormals, const baker::MeshTangents& meshTangentsIn) {
|
||||
auto graphicsMesh = std::make_shared<graphics::Mesh>();
|
||||
|
||||
// Fill tangents with a dummy value to force tangents to be present if there are normals
|
||||
baker::MeshTangents meshTangents;
|
||||
if (!meshTangentsIn.empty()) {
|
||||
meshTangents = meshTangentsIn;
|
||||
} else {
|
||||
meshTangents.reserve(meshNormals.size());
|
||||
std::fill_n(std::back_inserter(meshTangents), meshNormals.size(), Vectors::UNIT_X);
|
||||
}
|
||||
|
||||
unsigned int totalSourceIndices = 0;
|
||||
foreach(const HFMMeshPart& part, hfmMesh.parts) {
|
||||
totalSourceIndices += (part.quadTrianglesIndices.size() + part.triangleIndices.size());
|
||||
|
@ -48,23 +57,6 @@ void buildGraphicsMesh(const hfm::Mesh& hfmMesh, graphics::MeshPointer& graphics
|
|||
|
||||
int numVerts = hfmMesh.vertices.size();
|
||||
|
||||
if (!hfmMesh.normals.empty() && hfmMesh.tangents.empty()) {
|
||||
// Fill with a dummy value to force tangents to be present if there are normals
|
||||
meshTangents.reserve(hfmMesh.normals.size());
|
||||
std::fill_n(std::back_inserter(meshTangents), hfmMesh.normals.size(), Vectors::UNIT_X);
|
||||
} else {
|
||||
meshTangents = hfmMesh.tangents.toStdVector();
|
||||
}
|
||||
// Same thing with blend shapes
|
||||
blendshapes = hfmMesh.blendshapes.toStdVector();
|
||||
for (auto& blendShape : blendshapes) {
|
||||
if (!blendShape.normals.empty() && blendShape.tangents.empty()) {
|
||||
// Fill with a dummy value to force tangents to be present if there are normals
|
||||
blendShape.tangents.reserve(blendShape.normals.size());
|
||||
std::fill_n(std::back_inserter(blendShape.tangents), blendShape.normals.size(), Vectors::UNIT_X);
|
||||
}
|
||||
}
|
||||
|
||||
// evaluate all attribute elements and data sizes
|
||||
|
||||
// Position is a vec3
|
||||
|
@ -73,12 +65,12 @@ void buildGraphicsMesh(const hfm::Mesh& hfmMesh, graphics::MeshPointer& graphics
|
|||
|
||||
// Normal and tangent are always there together packed in normalized xyz32bits word (times 2)
|
||||
const auto normalElement = HFM_NORMAL_ELEMENT;
|
||||
const int normalsSize = hfmMesh.normals.size() * normalElement.getSize();
|
||||
const int normalsSize = (int)meshNormals.size() * normalElement.getSize();
|
||||
const int tangentsSize = (int)meshTangents.size() * normalElement.getSize();
|
||||
// If there are normals then there should be tangents
|
||||
assert(normalsSize <= tangentsSize);
|
||||
if (tangentsSize > normalsSize) {
|
||||
HIFI_FCDEBUG_ID(model_baker(), repeatMessageID, "BuildGraphicsMeshTask -- Unexpected tangents in file");
|
||||
HIFI_FCDEBUG_ID(model_baker(), repeatMessageID, "BuildGraphicsMeshTask -- Unexpected tangents in mesh");
|
||||
}
|
||||
const auto normalsAndTangentsSize = normalsSize + tangentsSize;
|
||||
|
||||
|
@ -124,11 +116,11 @@ void buildGraphicsMesh(const hfm::Mesh& hfmMesh, graphics::MeshPointer& graphics
|
|||
if (normalsSize > 0) {
|
||||
std::vector<NormalType> normalsAndTangents;
|
||||
|
||||
normalsAndTangents.reserve(hfmMesh.normals.size() + (int)meshTangents.size());
|
||||
auto normalIt = hfmMesh.normals.constBegin();
|
||||
normalsAndTangents.reserve(meshNormals.size() + (int)meshTangents.size());
|
||||
auto normalIt = meshNormals.cbegin();
|
||||
auto tangentIt = meshTangents.cbegin();
|
||||
for (;
|
||||
normalIt != hfmMesh.normals.constEnd();
|
||||
normalIt != meshNormals.cend();
|
||||
++normalIt, ++tangentIt) {
|
||||
#if HFM_PACK_NORMALS
|
||||
const auto normal = normalizeDirForPacking(*normalIt);
|
||||
|
@ -212,11 +204,6 @@ void buildGraphicsMesh(const hfm::Mesh& hfmMesh, graphics::MeshPointer& graphics
|
|||
auto vertexFormat = std::make_shared<gpu::Stream::Format>();
|
||||
auto vertexBufferStream = std::make_shared<gpu::BufferStream>();
|
||||
|
||||
// Decision time:
|
||||
// if blendshapes then keep position and normals/tangents as separated channel buffers from interleaved attributes
|
||||
// else everything is interleaved in one buffer
|
||||
|
||||
// Default case is no blend shapes
|
||||
gpu::BufferPointer attribBuffer;
|
||||
int totalAttribBufferSize = totalVertsSize;
|
||||
gpu::uint8 posChannel = 0;
|
||||
|
@ -244,7 +231,7 @@ void buildGraphicsMesh(const hfm::Mesh& hfmMesh, graphics::MeshPointer& graphics
|
|||
}
|
||||
}
|
||||
|
||||
// Pack normal and Tangent with the rest of atributes if no blend shapes
|
||||
// Pack normal and Tangent with the rest of atributes
|
||||
if (colorsSize) {
|
||||
vertexFormat->setAttribute(gpu::Stream::COLOR, attribChannel, colorElement, bufOffset);
|
||||
bufOffset += colorElement.getSize();
|
||||
|
@ -384,22 +371,21 @@ void buildGraphicsMesh(const hfm::Mesh& hfmMesh, graphics::MeshPointer& graphics
|
|||
}
|
||||
|
||||
void BuildGraphicsMeshTask::run(const baker::BakeContextPointer& context, const Input& input, Output& output) {
|
||||
auto& meshes = input.get0();
|
||||
auto& url = input.get1();
|
||||
auto& meshIndicesToModelNames = input.get2();
|
||||
const auto& meshes = input.get0();
|
||||
const auto& url = input.get1();
|
||||
const auto& meshIndicesToModelNames = input.get2();
|
||||
const auto& normalsPerMesh = input.get3();
|
||||
const auto& tangentsPerMesh = input.get4();
|
||||
|
||||
auto& graphicsMeshes = output;
|
||||
|
||||
auto& graphicsMeshes = output.edit0();
|
||||
auto& tangentsPerMesh = output.edit1();
|
||||
auto& blendshapesPerMesh = output.edit2();
|
||||
int n = (int)meshes.size();
|
||||
for (int i = 0; i < n; i++) {
|
||||
graphicsMeshes.emplace_back();
|
||||
auto& graphicsMesh = graphicsMeshes[i];
|
||||
tangentsPerMesh.emplace_back();
|
||||
blendshapesPerMesh.emplace_back();
|
||||
|
||||
// Try to create the graphics::Mesh
|
||||
buildGraphicsMesh(meshes[i], graphicsMesh, tangentsPerMesh[i], blendshapesPerMesh[i]);
|
||||
buildGraphicsMesh(meshes[i], graphicsMesh, normalsPerMesh[i], tangentsPerMesh[i]);
|
||||
|
||||
// Choose a name for the mesh
|
||||
if (graphicsMesh) {
|
||||
|
|
|
@ -20,8 +20,8 @@
|
|||
|
||||
class BuildGraphicsMeshTask {
|
||||
public:
|
||||
using Input = baker::VaryingSet3<std::vector<hfm::Mesh>, hifi::URL, baker::MeshIndicesToModelNames>;
|
||||
using Output = baker::VaryingSet3<std::vector<graphics::MeshPointer>, std::vector<baker::MeshTangents>, std::vector<baker::Blendshapes>>;
|
||||
using Input = baker::VaryingSet5<std::vector<hfm::Mesh>, hifi::URL, baker::MeshIndicesToModelNames, baker::NormalsPerMesh, baker::TangentsPerMesh>;
|
||||
using Output = std::vector<graphics::MeshPointer>;
|
||||
using JobModel = baker::Job::ModelIO<BuildGraphicsMeshTask, Input, Output>;
|
||||
|
||||
void run(const baker::BakeContextPointer& context, const Input& input, Output& output);
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
//
|
||||
// CalculateBlendshapeNormalsTask.h
|
||||
// model-baker/src/model-baker
|
||||
//
|
||||
// Created by Sabrina Shanman on 2019/01/07.
|
||||
// Copyright 2019 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 "CalculateBlendshapeNormalsTask.h"
|
||||
|
||||
#include "ModelMath.h"
|
||||
|
||||
void CalculateBlendshapeNormalsTask::run(const baker::BakeContextPointer& context, const Input& input, Output& output) {
|
||||
const auto& blendshapesPerMesh = input.get0();
|
||||
const auto& meshes = input.get1();
|
||||
auto& normalsPerBlendshapePerMeshOut = output;
|
||||
|
||||
normalsPerBlendshapePerMeshOut.reserve(blendshapesPerMesh.size());
|
||||
for (size_t i = 0; i < blendshapesPerMesh.size(); i++) {
|
||||
const auto& mesh = meshes[i];
|
||||
const auto& blendshapes = blendshapesPerMesh[i];
|
||||
normalsPerBlendshapePerMeshOut.emplace_back();
|
||||
auto& normalsPerBlendshapeOut = normalsPerBlendshapePerMeshOut[normalsPerBlendshapePerMeshOut.size()-1];
|
||||
|
||||
normalsPerBlendshapeOut.reserve(blendshapes.size());
|
||||
for (size_t j = 0; j < blendshapes.size(); j++) {
|
||||
const auto& blendshape = blendshapes[j];
|
||||
const auto& normalsIn = blendshape.normals;
|
||||
// Check if normals are already defined. Otherwise, calculate them from existing blendshape vertices.
|
||||
if (!normalsIn.empty()) {
|
||||
normalsPerBlendshapeOut.push_back(normalsIn.toStdVector());
|
||||
} else {
|
||||
// Create lookup to get index in blendshape 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;
|
||||
}
|
||||
|
||||
normalsPerBlendshapeOut.emplace_back();
|
||||
auto& normals = normalsPerBlendshapeOut[normalsPerBlendshapeOut.size()-1];
|
||||
normals.resize(mesh.vertices.size());
|
||||
baker::calculateNormals(mesh,
|
||||
[&reverseIndices, &blendshape, &normals](int normalIndex) /* NormalAccessor */ {
|
||||
const auto lookupIndex = reverseIndices[normalIndex];
|
||||
if (lookupIndex < blendshape.vertices.size()) {
|
||||
return &normals[lookupIndex];
|
||||
} else {
|
||||
// Index isn't in the blendshape. Request that the normal not be calculated.
|
||||
return (glm::vec3*)nullptr;
|
||||
}
|
||||
},
|
||||
[&mesh, &reverseIndices, &blendshape](int vertexIndex, glm::vec3& outVertex) /* VertexSetter */ {
|
||||
const auto lookupIndex = reverseIndices[vertexIndex];
|
||||
if (lookupIndex < blendshape.vertices.size()) {
|
||||
outVertex = blendshape.vertices[lookupIndex];
|
||||
} else {
|
||||
// Index isn't in the blendshape, so return vertex from mesh
|
||||
outVertex = mesh.vertices[lookupIndex];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
//
|
||||
// CalculateBlendshapeNormalsTask.h
|
||||
// model-baker/src/model-baker
|
||||
//
|
||||
// Created by Sabrina Shanman on 2019/01/07.
|
||||
// Copyright 2019 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_CalculateBlendshapeNormalsTask_h
|
||||
#define hifi_CalculateBlendshapeNormalsTask_h
|
||||
|
||||
#include "Engine.h"
|
||||
#include "BakerTypes.h"
|
||||
|
||||
// Calculate blendshape normals if not already present in the blendshape
|
||||
class CalculateBlendshapeNormalsTask {
|
||||
public:
|
||||
using Input = baker::VaryingSet2<baker::BlendshapesPerMesh, std::vector<hfm::Mesh>>;
|
||||
using Output = std::vector<baker::NormalsPerBlendshape>;
|
||||
using JobModel = baker::Job::ModelIO<CalculateBlendshapeNormalsTask, Input, Output>;
|
||||
|
||||
void run(const baker::BakeContextPointer& context, const Input& input, Output& output);
|
||||
};
|
||||
|
||||
#endif // hifi_CalculateBlendshapeNormalsTask_h
|
|
@ -0,0 +1,95 @@
|
|||
//
|
||||
// CalculateBlendshapeTangentsTask.cpp
|
||||
// model-baker/src/model-baker
|
||||
//
|
||||
// Created by Sabrina Shanman on 2019/01/08.
|
||||
// Copyright 2019 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 "CalculateBlendshapeTangentsTask.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "ModelMath.h"
|
||||
|
||||
void CalculateBlendshapeTangentsTask::run(const baker::BakeContextPointer& context, const Input& input, Output& output) {
|
||||
const auto& normalsPerBlendshapePerMesh = input.get0();
|
||||
const auto& blendshapesPerMesh = input.get1();
|
||||
const auto& meshes = input.get2();
|
||||
const auto& materials = input.get3();
|
||||
auto& tangentsPerBlendshapePerMeshOut = output;
|
||||
|
||||
tangentsPerBlendshapePerMeshOut.reserve(normalsPerBlendshapePerMesh.size());
|
||||
for (size_t i = 0; i < blendshapesPerMesh.size(); i++) {
|
||||
const auto& normalsPerBlendshape = normalsPerBlendshapePerMesh[i];
|
||||
const auto& blendshapes = blendshapesPerMesh[i];
|
||||
const auto& mesh = meshes[i];
|
||||
tangentsPerBlendshapePerMeshOut.emplace_back();
|
||||
auto& tangentsPerBlendshapeOut = tangentsPerBlendshapePerMeshOut[tangentsPerBlendshapePerMeshOut.size()-1];
|
||||
|
||||
// Check if we actually need to calculate the tangents, or just append empty arrays
|
||||
bool needTangents = false;
|
||||
for (const auto& meshPart : mesh.parts) {
|
||||
auto materialIt = materials.find(meshPart.materialID);
|
||||
if (materialIt != materials.end() && (*materialIt).needTangentSpace()) {
|
||||
needTangents = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t j = 0; j < blendshapes.size(); j++) {
|
||||
const auto& blendshape = blendshapes[j];
|
||||
const auto& tangentsIn = blendshape.tangents;
|
||||
const auto& normals = normalsPerBlendshape[j];
|
||||
tangentsPerBlendshapeOut.emplace_back();
|
||||
auto& tangentsOut = tangentsPerBlendshapeOut[tangentsPerBlendshapeOut.size()-1];
|
||||
|
||||
// Check if we already have tangents
|
||||
if (!tangentsIn.empty()) {
|
||||
tangentsOut = tangentsIn.toStdVector();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if we can and should calculate tangents (we need normals to calculate the tangents)
|
||||
if (normals.empty() || !needTangents) {
|
||||
continue;
|
||||
}
|
||||
tangentsOut.resize(normals.size());
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
baker::calculateTangents(mesh,
|
||||
[&mesh, &blendshape, &normals, &tangentsOut, &reverseIndices](int firstIndex, int secondIndex, glm::vec3* outVertices, glm::vec2* outTexCoords, glm::vec3& outNormal) {
|
||||
const auto index1 = reverseIndices[firstIndex];
|
||||
const auto index2 = reverseIndices[secondIndex];
|
||||
|
||||
if (index1 < blendshape.vertices.size()) {
|
||||
outVertices[0] = blendshape.vertices[index1];
|
||||
outTexCoords[0] = mesh.texCoords[index1];
|
||||
outTexCoords[1] = mesh.texCoords[index2];
|
||||
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 = normals[index1];
|
||||
return &tangentsOut[index1];
|
||||
} else {
|
||||
// Index isn't in blend shape so return nullptr
|
||||
return (glm::vec3*)nullptr;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
//
|
||||
// CalculateBlendshapeTangentsTask.h
|
||||
// model-baker/src/model-baker
|
||||
//
|
||||
// Created by Sabrina Shanman on 2019/01/07.
|
||||
// Copyright 2019 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_CalculateBlendshapeTangentsTask_h
|
||||
#define hifi_CalculateBlendshapeTangentsTask_h
|
||||
|
||||
#include "Engine.h"
|
||||
#include "BakerTypes.h"
|
||||
|
||||
// Calculate blendshape tangents if not already present in the blendshape
|
||||
class CalculateBlendshapeTangentsTask {
|
||||
public:
|
||||
using Input = baker::VaryingSet4<std::vector<baker::NormalsPerBlendshape>, baker::BlendshapesPerMesh, std::vector<hfm::Mesh>, QHash<QString, hfm::Material>>;
|
||||
using Output = std::vector<baker::TangentsPerBlendshape>;
|
||||
using JobModel = baker::Job::ModelIO<CalculateBlendshapeTangentsTask, Input, Output>;
|
||||
|
||||
void run(const baker::BakeContextPointer& context, const Input& input, Output& output);
|
||||
};
|
||||
|
||||
#endif // hifi_CalculateBlendshapeTangentsTask_h
|
|
@ -0,0 +1,40 @@
|
|||
//
|
||||
// CalculateMeshNormalsTask.cpp
|
||||
// model-baker/src/model-baker
|
||||
//
|
||||
// Created by Sabrina Shanman on 2019/01/22.
|
||||
// Copyright 2019 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 "CalculateMeshNormalsTask.h"
|
||||
|
||||
#include "ModelMath.h"
|
||||
|
||||
void CalculateMeshNormalsTask::run(const baker::BakeContextPointer& context, const Input& input, Output& output) {
|
||||
const auto& meshes = input;
|
||||
auto& normalsPerMeshOut = output;
|
||||
|
||||
normalsPerMeshOut.reserve(meshes.size());
|
||||
for (int i = 0; i < (int)meshes.size(); i++) {
|
||||
const auto& mesh = meshes[i];
|
||||
normalsPerMeshOut.emplace_back();
|
||||
auto& normalsOut = normalsPerMeshOut[normalsPerMeshOut.size()-1];
|
||||
// Only calculate normals if this mesh doesn't already have them
|
||||
if (!mesh.normals.empty()) {
|
||||
normalsOut = mesh.normals.toStdVector();
|
||||
} else {
|
||||
normalsOut.resize(mesh.vertices.size());
|
||||
baker::calculateNormals(mesh,
|
||||
[&normalsOut](int normalIndex) /* NormalAccessor */ {
|
||||
return &normalsOut[normalIndex];
|
||||
},
|
||||
[&mesh](int vertexIndex, glm::vec3& outVertex) /* VertexSetter */ {
|
||||
outVertex = mesh.vertices[vertexIndex];
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
// CalculateMeshNormalsTask.h
|
||||
// model-baker/src/model-baker
|
||||
//
|
||||
// Created by Sabrina Shanman on 2019/01/07.
|
||||
// Copyright 2019 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_CalculateMeshNormalsTask_h
|
||||
#define hifi_CalculateMeshNormalsTask_h
|
||||
|
||||
#include <hfm/HFM.h>
|
||||
|
||||
#include "Engine.h"
|
||||
#include "BakerTypes.h"
|
||||
|
||||
// Calculate mesh normals if not already present in the mesh
|
||||
class CalculateMeshNormalsTask {
|
||||
public:
|
||||
using Input = std::vector<hfm::Mesh>;
|
||||
using Output = baker::NormalsPerMesh;
|
||||
using JobModel = baker::Job::ModelIO<CalculateMeshNormalsTask, Input, Output>;
|
||||
|
||||
void run(const baker::BakeContextPointer& context, const Input& input, Output& output);
|
||||
};
|
||||
|
||||
#endif // hifi_CalculateMeshNormalsTask_h
|
|
@ -0,0 +1,65 @@
|
|||
//
|
||||
// CalculateMeshTangentsTask.cpp
|
||||
// model-baker/src/model-baker
|
||||
//
|
||||
// Created by Sabrina Shanman on 2019/01/22.
|
||||
// Copyright 2019 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 "CalculateMeshTangentsTask.h"
|
||||
|
||||
#include "ModelMath.h"
|
||||
|
||||
void CalculateMeshTangentsTask::run(const baker::BakeContextPointer& context, const Input& input, Output& output) {
|
||||
const auto& normalsPerMesh = input.get0();
|
||||
const std::vector<hfm::Mesh>& meshes = input.get1();
|
||||
const auto& materials = input.get2();
|
||||
auto& tangentsPerMeshOut = output;
|
||||
|
||||
tangentsPerMeshOut.reserve(meshes.size());
|
||||
for (int i = 0; i < (int)meshes.size(); i++) {
|
||||
const auto& mesh = meshes[i];
|
||||
const auto& tangentsIn = mesh.tangents;
|
||||
const auto& normals = normalsPerMesh[i];
|
||||
tangentsPerMeshOut.emplace_back();
|
||||
auto& tangentsOut = tangentsPerMeshOut[tangentsPerMeshOut.size()-1];
|
||||
|
||||
// Check if we already have tangents and therefore do not need to do any calculation
|
||||
if (!tangentsIn.empty()) {
|
||||
tangentsOut = tangentsIn.toStdVector();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if we have normals, and if not then tangents can't be calculated
|
||||
if (normals.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if we actually need to calculate the tangents
|
||||
bool needTangents = false;
|
||||
for (const auto& meshPart : mesh.parts) {
|
||||
auto materialIt = materials.find(meshPart.materialID);
|
||||
if (materialIt != materials.end() && (*materialIt).needTangentSpace()) {
|
||||
needTangents = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (needTangents) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tangentsOut.resize(normals.size());
|
||||
baker::calculateTangents(mesh,
|
||||
[&mesh, &normals, &tangentsOut](int firstIndex, int secondIndex, glm::vec3* outVertices, glm::vec2* outTexCoords, glm::vec3& outNormal) {
|
||||
outVertices[0] = mesh.vertices[firstIndex];
|
||||
outVertices[1] = mesh.vertices[secondIndex];
|
||||
outNormal = normals[firstIndex];
|
||||
outTexCoords[0] = mesh.texCoords[firstIndex];
|
||||
outTexCoords[1] = mesh.texCoords[secondIndex];
|
||||
return &(tangentsOut[firstIndex]);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
//
|
||||
// CalculateMeshTangentsTask.h
|
||||
// model-baker/src/model-baker
|
||||
//
|
||||
// Created by Sabrina Shanman on 2019/01/07.
|
||||
// Copyright 2019 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_CalculateMeshTangentsTask_h
|
||||
#define hifi_CalculateMeshTangentsTask_h
|
||||
|
||||
#include <hfm/HFM.h>
|
||||
|
||||
#include "Engine.h"
|
||||
#include "BakerTypes.h"
|
||||
|
||||
// Calculate mesh tangents if not already present in the mesh
|
||||
class CalculateMeshTangentsTask {
|
||||
public:
|
||||
using NormalsPerMesh = std::vector<std::vector<glm::vec3>>;
|
||||
|
||||
using Input = baker::VaryingSet3<baker::NormalsPerMesh, std::vector<hfm::Mesh>, QHash<QString, hfm::Material>>;
|
||||
using Output = baker::TangentsPerMesh;
|
||||
using JobModel = baker::Job::ModelIO<CalculateMeshTangentsTask, Input, Output>;
|
||||
|
||||
void run(const baker::BakeContextPointer& context, const Input& input, Output& output);
|
||||
};
|
||||
|
||||
#endif // hifi_CalculateMeshTangentsTask_h
|
121
libraries/model-baker/src/model-baker/ModelMath.cpp
Normal file
121
libraries/model-baker/src/model-baker/ModelMath.cpp
Normal file
|
@ -0,0 +1,121 @@
|
|||
//
|
||||
// ModelMath.cpp
|
||||
// model-baker/src/model-baker
|
||||
//
|
||||
// Created by Sabrina Shanman on 2019/01/18.
|
||||
// Copyright 2019 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 "ModelMath.h"
|
||||
|
||||
#include <LogHandler.h>
|
||||
#include "ModelBakerLogging.h"
|
||||
|
||||
namespace baker {
|
||||
template<class T>
|
||||
const T& checkedAt(const QVector<T>& vector, int i) {
|
||||
if (i < 0 || i >= vector.size()) {
|
||||
throw std::out_of_range("baker::checked_at (ModelMath.cpp): index " + std::to_string(i) + " is out of range");
|
||||
}
|
||||
return vector[i];
|
||||
}
|
||||
|
||||
template<class T>
|
||||
const T& checkedAt(const std::vector<T>& vector, int i) {
|
||||
if (i < 0 || i >= vector.size()) {
|
||||
throw std::out_of_range("baker::checked_at (ModelMath.cpp): index " + std::to_string(i) + " is out of range");
|
||||
}
|
||||
return vector[i];
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T& checkedAt(std::vector<T>& vector, int i) {
|
||||
if (i < 0 || i >= vector.size()) {
|
||||
throw std::out_of_range("baker::checked_at (ModelMath.cpp): index " + std::to_string(i) + " is out of range");
|
||||
}
|
||||
return vector[i];
|
||||
}
|
||||
|
||||
void setTangent(const HFMMesh& mesh, const IndexAccessor& vertexAccessor, int firstIndex, int secondIndex) {
|
||||
glm::vec3 vertex[2];
|
||||
glm::vec2 texCoords[2];
|
||||
glm::vec3 normal;
|
||||
glm::vec3* tangent = vertexAccessor(firstIndex, secondIndex, vertex, texCoords, normal);
|
||||
if (tangent) {
|
||||
glm::vec3 bitangent = glm::cross(normal, vertex[1] - vertex[0]);
|
||||
if (glm::length(bitangent) < EPSILON) {
|
||||
return;
|
||||
}
|
||||
glm::vec2 texCoordDelta = texCoords[1] - texCoords[0];
|
||||
glm::vec3 normalizedNormal = glm::normalize(normal);
|
||||
*tangent += glm::cross(glm::angleAxis(-atan2f(-texCoordDelta.t, texCoordDelta.s), normalizedNormal) *
|
||||
glm::normalize(bitangent), normalizedNormal);
|
||||
}
|
||||
}
|
||||
|
||||
void calculateNormals(const hfm::Mesh& mesh, NormalAccessor normalAccessor, VertexSetter vertexSetter) {
|
||||
static int repeatMessageID = LogHandler::getInstance().newRepeatedMessageID();
|
||||
for (const HFMMeshPart& part : mesh.parts) {
|
||||
for (int i = 0; i < part.quadIndices.size(); i += 4) {
|
||||
glm::vec3* n0 = normalAccessor(part.quadIndices[i]);
|
||||
glm::vec3* n1 = normalAccessor(part.quadIndices[i + 1]);
|
||||
glm::vec3* n2 = normalAccessor(part.quadIndices[i + 2]);
|
||||
glm::vec3* n3 = normalAccessor(part.quadIndices[i + 3]);
|
||||
if (!n0 || !n1 || !n2 || !n3) {
|
||||
// Quad is not in the mesh (can occur with blendshape meshes, which are a subset of the hfm Mesh vertices)
|
||||
continue;
|
||||
}
|
||||
glm::vec3 vertices[3]; // Assume all vertices in this quad are in the same plane, so only the first three are needed to calculate the normal
|
||||
vertexSetter(part.quadIndices[i], vertices[0]);
|
||||
vertexSetter(part.quadIndices[i + 1], vertices[1]);
|
||||
vertexSetter(part.quadIndices[i + 2], vertices[2]);
|
||||
*n0 = *n1 = *n2 = *n3 = glm::cross(vertices[1] - vertices[0], vertices[2] - vertices[0]);
|
||||
}
|
||||
// <= 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) {
|
||||
glm::vec3* n0 = normalAccessor(part.triangleIndices[i]);
|
||||
glm::vec3* n1 = normalAccessor(part.triangleIndices[i + 1]);
|
||||
glm::vec3* n2 = normalAccessor(part.triangleIndices[i + 2]);
|
||||
if (!n0 || !n1 || !n2) {
|
||||
// Tri is not in the mesh (can occur with blendshape meshes, which are a subset of the hfm Mesh vertices)
|
||||
continue;
|
||||
}
|
||||
glm::vec3 vertices[3];
|
||||
vertexSetter(part.triangleIndices[i], vertices[0]);
|
||||
vertexSetter(part.triangleIndices[i + 1], vertices[1]);
|
||||
vertexSetter(part.triangleIndices[i + 2], vertices[2]);
|
||||
*n0 = *n1 = *n2 = glm::cross(vertices[1] - vertices[0], vertices[2] - vertices[0]);
|
||||
}
|
||||
if ((part.triangleIndices.size() % 3) != 0) {
|
||||
HIFI_FCDEBUG_ID(model_baker(), repeatMessageID, "Error in baker::calculateNormals: part.triangleIndices.size() is not divisible by three");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void calculateTangents(const hfm::Mesh& mesh, IndexAccessor accessor) {
|
||||
static int repeatMessageID = LogHandler::getInstance().newRepeatedMessageID();
|
||||
for (const HFMMeshPart& part : mesh.parts) {
|
||||
for (int i = 0; i < part.quadIndices.size(); i += 4) {
|
||||
setTangent(mesh, accessor, part.quadIndices.at(i), part.quadIndices.at(i + 1));
|
||||
setTangent(mesh, accessor, part.quadIndices.at(i + 1), part.quadIndices.at(i + 2));
|
||||
setTangent(mesh, accessor, part.quadIndices.at(i + 2), part.quadIndices.at(i + 3));
|
||||
setTangent(mesh, accessor, part.quadIndices.at(i + 3), part.quadIndices.at(i));
|
||||
}
|
||||
// <= 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) {
|
||||
setTangent(mesh, accessor, part.triangleIndices.at(i), part.triangleIndices.at(i + 1));
|
||||
setTangent(mesh, accessor, part.triangleIndices.at(i + 1), part.triangleIndices.at(i + 2));
|
||||
setTangent(mesh, accessor, part.triangleIndices.at(i + 2), part.triangleIndices.at(i));
|
||||
}
|
||||
if ((part.triangleIndices.size() % 3) != 0) {
|
||||
HIFI_FCDEBUG_ID(model_baker(), repeatMessageID, "Error in baker::calculateTangents: part.triangleIndices.size() is not divisible by three");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
34
libraries/model-baker/src/model-baker/ModelMath.h
Normal file
34
libraries/model-baker/src/model-baker/ModelMath.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
//
|
||||
// ModelMath.h
|
||||
// model-baker/src/model-baker
|
||||
//
|
||||
// Created by Sabrina Shanman on 2019/01/07.
|
||||
// Copyright 2019 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/HFM.h>
|
||||
|
||||
#include "BakerTypes.h"
|
||||
|
||||
namespace baker {
|
||||
// Returns a reference to the normal at the specified index, or nullptr if it cannot be accessed
|
||||
using NormalAccessor = std::function<glm::vec3*(int index)>;
|
||||
|
||||
// Assigns a vertex to outVertex given the lookup index
|
||||
using VertexSetter = std::function<void(int index, glm::vec3& outVertex)>;
|
||||
|
||||
void calculateNormals(const hfm::Mesh& mesh, NormalAccessor normalAccessor, VertexSetter vertexAccessor);
|
||||
|
||||
// firstIndex, secondIndex: the vertex indices to be used for calculation
|
||||
// outVertices: should be assigned a 2 element array containing the vertices at firstIndex and secondIndex
|
||||
// outTexCoords: same as outVertices but for texture coordinates
|
||||
// outNormal: reference to the normal of this triangle
|
||||
//
|
||||
// Return value: pointer to the tangent you want to be calculated
|
||||
using IndexAccessor = std::function<glm::vec3*(int firstIndex, int secondIndex, glm::vec3* outVertices, glm::vec2* outTexCoords, glm::vec3& outNormal)>;
|
||||
|
||||
void calculateTangents(const hfm::Mesh& mesh, IndexAccessor accessor);
|
||||
};
|
Loading…
Reference in a new issue