mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Merge pull request #14576 from sabrina-shanman/hfm_prep_graphics
(case 20297) Move graphics preparation from Serializers to runtime preparation step
This commit is contained in:
commit
0d27cb65ca
16 changed files with 602 additions and 436 deletions
|
@ -11,7 +11,7 @@ setup_memory_debugger()
|
|||
|
||||
# link in the shared libraries
|
||||
link_hifi_libraries(
|
||||
audio avatars octree gpu graphics fbx hfm entities
|
||||
audio avatars octree gpu graphics shaders fbx hfm entities
|
||||
networking animation recording shared script-engine embedded-webserver
|
||||
controllers physics plugins midi image
|
||||
)
|
||||
|
|
|
@ -19,20 +19,6 @@
|
|||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
#define FBX_PACK_NORMALS 0
|
||||
#else
|
||||
#define FBX_PACK_NORMALS 1
|
||||
#endif
|
||||
|
||||
#if FBX_PACK_NORMALS
|
||||
using NormalType = glm::uint32;
|
||||
#define FBX_NORMAL_ELEMENT gpu::Element::VEC4F_NORMALIZED_XYZ10W2
|
||||
#else
|
||||
using NormalType = glm::vec3;
|
||||
#define FBX_NORMAL_ELEMENT gpu::Element::VEC3F_XYZ
|
||||
#endif
|
||||
|
||||
// See comment in FBXSerializer::parseFBX().
|
||||
static const int FBX_HEADER_BYTES_BEFORE_VERSION = 23;
|
||||
static const QByteArray FBX_BINARY_PROLOG("Kaydara FBX Binary ");
|
||||
|
|
|
@ -11,27 +11,13 @@
|
|||
|
||||
#include "FBXSerializer.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <QBuffer>
|
||||
#include <QDataStream>
|
||||
#include <QIODevice>
|
||||
#include <QStringList>
|
||||
#include <QTextStream>
|
||||
#include <QtDebug>
|
||||
#include <QtEndian>
|
||||
#include <QFileInfo>
|
||||
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
#include <glm/gtx/transform.hpp>
|
||||
|
||||
#include <FaceshiftConstants.h>
|
||||
#include <GeometryUtil.h>
|
||||
#include <GLMHelpers.h>
|
||||
#include <NumericalConstants.h>
|
||||
#include <OctalCode.h>
|
||||
#include <gpu/Format.h>
|
||||
#include <LogHandler.h>
|
||||
|
||||
#include <hfm/ModelFormatLogging.h>
|
||||
|
||||
|
@ -1640,14 +1626,9 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
|
|||
}
|
||||
}
|
||||
}
|
||||
buildModelMesh(extracted.mesh, url);
|
||||
|
||||
hfmModel.meshes.append(extracted.mesh);
|
||||
int meshIndex = hfmModel.meshes.size() - 1;
|
||||
if (extracted.mesh._mesh) {
|
||||
extracted.mesh._mesh->displayName = QString("%1#/mesh/%2").arg(url).arg(meshIndex).toStdString();
|
||||
extracted.mesh._mesh->modelName = modelIDsToNames.value(modelID).toStdString();
|
||||
}
|
||||
meshIDsToMeshIndices.insert(it.key(), meshIndex);
|
||||
}
|
||||
|
||||
|
@ -1715,22 +1696,6 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
|
|||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
int i = 0;
|
||||
for (const auto& mesh : hfmModel.meshes) {
|
||||
auto name = hfmModel.getModelNameOfMesh(i++);
|
||||
if (!name.isEmpty()) {
|
||||
if (mesh._mesh) {
|
||||
mesh._mesh->modelName = name.toStdString();
|
||||
if (!mesh._mesh->displayName.size()) {
|
||||
mesh._mesh->displayName = QString("#%1").arg(name).toStdString();
|
||||
}
|
||||
} else {
|
||||
qDebug() << "modelName but no mesh._mesh" << name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto offsets = getJointRotationOffsets(mapping);
|
||||
hfmModel.jointRotationOffsets.clear();
|
||||
|
|
|
@ -111,9 +111,6 @@ public:
|
|||
|
||||
static ExtractedMesh extractMesh(const FBXNode& object, unsigned int& meshIndex, bool deduplicate = true);
|
||||
QHash<QString, ExtractedMesh> meshes;
|
||||
static void buildModelMesh(HFMMesh& extractedMesh, const QString& url);
|
||||
|
||||
static glm::vec3 normalizeDirForPacking(const glm::vec3& dir);
|
||||
|
||||
HFMTexture getTexture(const QString& textureID);
|
||||
|
||||
|
|
|
@ -42,16 +42,6 @@
|
|||
|
||||
using vec2h = glm::tvec2<glm::detail::hdata>;
|
||||
|
||||
#define HFM_PACK_COLORS 1
|
||||
|
||||
#if HFM_PACK_COLORS
|
||||
using ColorType = glm::uint32;
|
||||
#define FBX_COLOR_ELEMENT gpu::Element::COLOR_RGBA_32
|
||||
#else
|
||||
using ColorType = glm::vec3;
|
||||
#define FBX_COLOR_ELEMENT gpu::Element::VEC3F_XYZ
|
||||
#endif
|
||||
|
||||
class Vertex {
|
||||
public:
|
||||
int originalIndex;
|
||||
|
@ -556,364 +546,3 @@ ExtractedMesh FBXSerializer::extractMesh(const FBXNode& object, unsigned int& me
|
|||
|
||||
return data.extracted;
|
||||
}
|
||||
|
||||
glm::vec3 FBXSerializer::normalizeDirForPacking(const glm::vec3& dir) {
|
||||
auto maxCoord = glm::max(fabsf(dir.x), glm::max(fabsf(dir.y), fabsf(dir.z)));
|
||||
if (maxCoord > 1e-6f) {
|
||||
return dir / maxCoord;
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
|
||||
void FBXSerializer::buildModelMesh(HFMMesh& extractedMesh, const QString& url) {
|
||||
unsigned int totalSourceIndices = 0;
|
||||
foreach(const HFMMeshPart& part, extractedMesh.parts) {
|
||||
totalSourceIndices += (part.quadTrianglesIndices.size() + part.triangleIndices.size());
|
||||
}
|
||||
|
||||
static int repeatMessageID = LogHandler::getInstance().newRepeatedMessageID();
|
||||
|
||||
if (!totalSourceIndices) {
|
||||
HIFI_FCDEBUG_ID(modelformat(), repeatMessageID, "buildModelMesh failed -- no indices, url = " << url);
|
||||
return;
|
||||
}
|
||||
|
||||
if (extractedMesh.vertices.size() == 0) {
|
||||
HIFI_FCDEBUG_ID(modelformat(), repeatMessageID, "buildModelMesh failed -- no vertices, url = " << url);
|
||||
return;
|
||||
}
|
||||
|
||||
HFMMesh& hfmMesh = extractedMesh;
|
||||
graphics::MeshPointer mesh(new graphics::Mesh());
|
||||
int numVerts = extractedMesh.vertices.size();
|
||||
|
||||
if (!hfmMesh.normals.empty() && hfmMesh.tangents.empty()) {
|
||||
// Fill with a dummy value to force tangents to be present if there are normals
|
||||
hfmMesh.tangents.reserve(hfmMesh.normals.size());
|
||||
std::fill_n(std::back_inserter(hfmMesh.tangents), hfmMesh.normals.size(), Vectors::UNIT_X);
|
||||
}
|
||||
// Same thing with blend shapes
|
||||
for (auto& blendShape : hfmMesh.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
|
||||
const auto positionElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ);
|
||||
const int positionsSize = numVerts * positionElement.getSize();
|
||||
|
||||
// Normal and tangent are always there together packed in normalized xyz32bits word (times 2)
|
||||
const auto normalElement = FBX_NORMAL_ELEMENT;
|
||||
const int normalsSize = hfmMesh.normals.size() * normalElement.getSize();
|
||||
const int tangentsSize = hfmMesh.tangents.size() * normalElement.getSize();
|
||||
// If there are normals then there should be tangents
|
||||
assert(normalsSize <= tangentsSize);
|
||||
if (tangentsSize > normalsSize) {
|
||||
qWarning() << "Unexpected tangents in " << url;
|
||||
}
|
||||
const auto normalsAndTangentsSize = normalsSize + tangentsSize;
|
||||
|
||||
// Color attrib
|
||||
const auto colorElement = FBX_COLOR_ELEMENT;
|
||||
const int colorsSize = hfmMesh.colors.size() * colorElement.getSize();
|
||||
|
||||
// Texture coordinates are stored in 2 half floats
|
||||
const auto texCoordsElement = gpu::Element(gpu::VEC2, gpu::HALF, gpu::UV);
|
||||
const int texCoordsSize = hfmMesh.texCoords.size() * texCoordsElement.getSize();
|
||||
const int texCoords1Size = hfmMesh.texCoords1.size() * texCoordsElement.getSize();
|
||||
|
||||
// Support for 4 skinning clusters:
|
||||
// 4 Indices are uint8 ideally, uint16 if more than 256.
|
||||
const auto clusterIndiceElement = (hfmMesh.clusters.size() < UINT8_MAX ? gpu::Element(gpu::VEC4, gpu::UINT8, gpu::XYZW) : gpu::Element(gpu::VEC4, gpu::UINT16, gpu::XYZW));
|
||||
// 4 Weights are normalized 16bits
|
||||
const auto clusterWeightElement = gpu::Element(gpu::VEC4, gpu::NUINT16, gpu::XYZW);
|
||||
|
||||
// Cluster indices and weights must be the same sizes
|
||||
const int NUM_CLUSTERS_PER_VERT = 4;
|
||||
const int numVertClusters = (hfmMesh.clusterIndices.size() == hfmMesh.clusterWeights.size() ? hfmMesh.clusterIndices.size() / NUM_CLUSTERS_PER_VERT : 0);
|
||||
const int clusterIndicesSize = numVertClusters * clusterIndiceElement.getSize();
|
||||
const int clusterWeightsSize = numVertClusters * clusterWeightElement.getSize();
|
||||
|
||||
// Decide on where to put what seequencially in a big buffer:
|
||||
const int positionsOffset = 0;
|
||||
const int normalsAndTangentsOffset = positionsOffset + positionsSize;
|
||||
const int colorsOffset = normalsAndTangentsOffset + normalsAndTangentsSize;
|
||||
const int texCoordsOffset = colorsOffset + colorsSize;
|
||||
const int texCoords1Offset = texCoordsOffset + texCoordsSize;
|
||||
const int clusterIndicesOffset = texCoords1Offset + texCoords1Size;
|
||||
const int clusterWeightsOffset = clusterIndicesOffset + clusterIndicesSize;
|
||||
const int totalVertsSize = clusterWeightsOffset + clusterWeightsSize;
|
||||
|
||||
// Copy all vertex data in a single buffer
|
||||
auto vertBuffer = std::make_shared<gpu::Buffer>();
|
||||
vertBuffer->resize(totalVertsSize);
|
||||
|
||||
// First positions
|
||||
vertBuffer->setSubData(positionsOffset, positionsSize, (const gpu::Byte*) extractedMesh.vertices.data());
|
||||
|
||||
// Interleave normals and tangents
|
||||
if (normalsSize > 0) {
|
||||
std::vector<NormalType> normalsAndTangents;
|
||||
|
||||
normalsAndTangents.reserve(hfmMesh.normals.size() + hfmMesh.tangents.size());
|
||||
for (auto normalIt = hfmMesh.normals.constBegin(), tangentIt = hfmMesh.tangents.constBegin();
|
||||
normalIt != hfmMesh.normals.constEnd();
|
||||
++normalIt, ++tangentIt) {
|
||||
#if FBX_PACK_NORMALS
|
||||
const auto normal = normalizeDirForPacking(*normalIt);
|
||||
const auto tangent = normalizeDirForPacking(*tangentIt);
|
||||
const auto packedNormal = glm::packSnorm3x10_1x2(glm::vec4(normal, 0.0f));
|
||||
const auto packedTangent = glm::packSnorm3x10_1x2(glm::vec4(tangent, 0.0f));
|
||||
#else
|
||||
const auto packedNormal = *normalIt;
|
||||
const auto packedTangent = *tangentIt;
|
||||
#endif
|
||||
normalsAndTangents.push_back(packedNormal);
|
||||
normalsAndTangents.push_back(packedTangent);
|
||||
}
|
||||
vertBuffer->setSubData(normalsAndTangentsOffset, normalsAndTangentsSize, (const gpu::Byte*) normalsAndTangents.data());
|
||||
}
|
||||
|
||||
// Pack colors
|
||||
if (colorsSize > 0) {
|
||||
#if HFM_PACK_COLORS
|
||||
std::vector<ColorType> colors;
|
||||
|
||||
colors.reserve(hfmMesh.colors.size());
|
||||
for (const auto& color : hfmMesh.colors) {
|
||||
colors.push_back(glm::packUnorm4x8(glm::vec4(color, 1.0f)));
|
||||
}
|
||||
vertBuffer->setSubData(colorsOffset, colorsSize, (const gpu::Byte*) colors.data());
|
||||
#else
|
||||
vertBuffer->setSubData(colorsOffset, colorsSize, (const gpu::Byte*) hfmMesh.colors.constData());
|
||||
#endif
|
||||
}
|
||||
|
||||
// Pack Texcoords 0 and 1 (if exists)
|
||||
if (texCoordsSize > 0) {
|
||||
QVector<vec2h> texCoordData;
|
||||
texCoordData.reserve(hfmMesh.texCoords.size());
|
||||
for (auto& texCoordVec2f : hfmMesh.texCoords) {
|
||||
vec2h texCoordVec2h;
|
||||
|
||||
texCoordVec2h.x = glm::detail::toFloat16(texCoordVec2f.x);
|
||||
texCoordVec2h.y = glm::detail::toFloat16(texCoordVec2f.y);
|
||||
texCoordData.push_back(texCoordVec2h);
|
||||
}
|
||||
vertBuffer->setSubData(texCoordsOffset, texCoordsSize, (const gpu::Byte*) texCoordData.constData());
|
||||
}
|
||||
if (texCoords1Size > 0) {
|
||||
QVector<vec2h> texCoordData;
|
||||
texCoordData.reserve(hfmMesh.texCoords1.size());
|
||||
for (auto& texCoordVec2f : hfmMesh.texCoords1) {
|
||||
vec2h texCoordVec2h;
|
||||
|
||||
texCoordVec2h.x = glm::detail::toFloat16(texCoordVec2f.x);
|
||||
texCoordVec2h.y = glm::detail::toFloat16(texCoordVec2f.y);
|
||||
texCoordData.push_back(texCoordVec2h);
|
||||
}
|
||||
vertBuffer->setSubData(texCoords1Offset, texCoords1Size, (const gpu::Byte*) texCoordData.constData());
|
||||
}
|
||||
|
||||
// Clusters data
|
||||
if (clusterIndicesSize > 0) {
|
||||
if (hfmMesh.clusters.size() < UINT8_MAX) {
|
||||
// yay! we can fit the clusterIndices within 8-bits
|
||||
int32_t numIndices = hfmMesh.clusterIndices.size();
|
||||
QVector<uint8_t> clusterIndices;
|
||||
clusterIndices.resize(numIndices);
|
||||
for (int32_t i = 0; i < numIndices; ++i) {
|
||||
assert(hfmMesh.clusterIndices[i] <= UINT8_MAX);
|
||||
clusterIndices[i] = (uint8_t)(hfmMesh.clusterIndices[i]);
|
||||
}
|
||||
vertBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (const gpu::Byte*) clusterIndices.constData());
|
||||
} else {
|
||||
vertBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (const gpu::Byte*) hfmMesh.clusterIndices.constData());
|
||||
}
|
||||
}
|
||||
if (clusterWeightsSize > 0) {
|
||||
vertBuffer->setSubData(clusterWeightsOffset, clusterWeightsSize, (const gpu::Byte*) hfmMesh.clusterWeights.constData());
|
||||
}
|
||||
|
||||
|
||||
// Now we decide on how to interleave the attributes and provide the vertices among bufers:
|
||||
// Aka the Vertex format and the vertexBufferStream
|
||||
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;
|
||||
gpu::uint8 tangentChannel = posChannel;
|
||||
gpu::uint8 attribChannel = posChannel;
|
||||
bool interleavePositions = true;
|
||||
bool interleaveNormalsTangents = true;
|
||||
|
||||
// Define the vertex format, compute the offset for each attributes as we append them to the vertex format
|
||||
gpu::Offset bufOffset = 0;
|
||||
if (positionsSize) {
|
||||
vertexFormat->setAttribute(gpu::Stream::POSITION, posChannel, positionElement, bufOffset);
|
||||
bufOffset += positionElement.getSize();
|
||||
if (!interleavePositions) {
|
||||
bufOffset = 0;
|
||||
}
|
||||
}
|
||||
if (normalsSize) {
|
||||
vertexFormat->setAttribute(gpu::Stream::NORMAL, tangentChannel, normalElement, bufOffset);
|
||||
bufOffset += normalElement.getSize();
|
||||
vertexFormat->setAttribute(gpu::Stream::TANGENT, tangentChannel, normalElement, bufOffset);
|
||||
bufOffset += normalElement.getSize();
|
||||
if (!interleaveNormalsTangents) {
|
||||
bufOffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Pack normal and Tangent with the rest of atributes if no blend shapes
|
||||
if (colorsSize) {
|
||||
vertexFormat->setAttribute(gpu::Stream::COLOR, attribChannel, colorElement, bufOffset);
|
||||
bufOffset += colorElement.getSize();
|
||||
}
|
||||
if (texCoordsSize) {
|
||||
vertexFormat->setAttribute(gpu::Stream::TEXCOORD, attribChannel, texCoordsElement, bufOffset);
|
||||
bufOffset += texCoordsElement.getSize();
|
||||
}
|
||||
if (texCoords1Size) {
|
||||
vertexFormat->setAttribute(gpu::Stream::TEXCOORD1, attribChannel, texCoordsElement, bufOffset);
|
||||
bufOffset += texCoordsElement.getSize();
|
||||
} else if (texCoordsSize) {
|
||||
vertexFormat->setAttribute(gpu::Stream::TEXCOORD1, attribChannel, texCoordsElement, bufOffset - texCoordsElement.getSize());
|
||||
}
|
||||
if (clusterIndicesSize) {
|
||||
vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_INDEX, attribChannel, clusterIndiceElement, bufOffset);
|
||||
bufOffset += clusterIndiceElement.getSize();
|
||||
}
|
||||
if (clusterWeightsSize) {
|
||||
vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT, attribChannel, clusterWeightElement, bufOffset);
|
||||
bufOffset += clusterWeightElement.getSize();
|
||||
}
|
||||
|
||||
// Finally, allocate and fill the attribBuffer interleaving the attributes as needed:
|
||||
{
|
||||
auto vPositionOffset = 0;
|
||||
auto vPositionSize = (interleavePositions ? positionsSize / numVerts : 0);
|
||||
|
||||
auto vNormalsAndTangentsOffset = vPositionOffset + vPositionSize;
|
||||
auto vNormalsAndTangentsSize = (interleaveNormalsTangents ? normalsAndTangentsSize / numVerts : 0);
|
||||
|
||||
auto vColorOffset = vNormalsAndTangentsOffset + vNormalsAndTangentsSize;
|
||||
auto vColorSize = colorsSize / numVerts;
|
||||
|
||||
auto vTexcoord0Offset = vColorOffset + vColorSize;
|
||||
auto vTexcoord0Size = texCoordsSize / numVerts;
|
||||
|
||||
auto vTexcoord1Offset = vTexcoord0Offset + vTexcoord0Size;
|
||||
auto vTexcoord1Size = texCoords1Size / numVerts;
|
||||
|
||||
auto vClusterIndiceOffset = vTexcoord1Offset + vTexcoord1Size;
|
||||
auto vClusterIndiceSize = clusterIndicesSize / numVerts;
|
||||
|
||||
auto vClusterWeightOffset = vClusterIndiceOffset + vClusterIndiceSize;
|
||||
auto vClusterWeightSize = clusterWeightsSize / numVerts;
|
||||
|
||||
auto vStride = vClusterWeightOffset + vClusterWeightSize;
|
||||
|
||||
std::vector<gpu::Byte> dest;
|
||||
dest.resize(totalAttribBufferSize);
|
||||
auto vDest = dest.data();
|
||||
|
||||
auto source = vertBuffer->getData();
|
||||
|
||||
for (int i = 0; i < numVerts; i++) {
|
||||
|
||||
if (vPositionSize) memcpy(vDest + vPositionOffset, source + positionsOffset + i * vPositionSize, vPositionSize);
|
||||
if (vNormalsAndTangentsSize) memcpy(vDest + vNormalsAndTangentsOffset, source + normalsAndTangentsOffset + i * vNormalsAndTangentsSize, vNormalsAndTangentsSize);
|
||||
if (vColorSize) memcpy(vDest + vColorOffset, source + colorsOffset + i * vColorSize, vColorSize);
|
||||
if (vTexcoord0Size) memcpy(vDest + vTexcoord0Offset, source + texCoordsOffset + i * vTexcoord0Size, vTexcoord0Size);
|
||||
if (vTexcoord1Size) memcpy(vDest + vTexcoord1Offset, source + texCoords1Offset + i * vTexcoord1Size, vTexcoord1Size);
|
||||
if (vClusterIndiceSize) memcpy(vDest + vClusterIndiceOffset, source + clusterIndicesOffset + i * vClusterIndiceSize, vClusterIndiceSize);
|
||||
if (vClusterWeightSize) memcpy(vDest + vClusterWeightOffset, source + clusterWeightsOffset + i * vClusterWeightSize, vClusterWeightSize);
|
||||
|
||||
vDest += vStride;
|
||||
}
|
||||
|
||||
auto attribBuffer = std::make_shared<gpu::Buffer>();
|
||||
attribBuffer->setData(totalAttribBufferSize, dest.data());
|
||||
vertexBufferStream->addBuffer(attribBuffer, 0, vStride);
|
||||
}
|
||||
|
||||
// Mesh vertex format and vertex stream is ready
|
||||
mesh->setVertexFormatAndStream(vertexFormat, vertexBufferStream);
|
||||
|
||||
// Index and Part Buffers
|
||||
unsigned int totalIndices = 0;
|
||||
foreach(const HFMMeshPart& part, extractedMesh.parts) {
|
||||
totalIndices += (part.quadTrianglesIndices.size() + part.triangleIndices.size());
|
||||
}
|
||||
|
||||
if (! totalIndices) {
|
||||
qCDebug(modelformat) << "buildModelMesh failed -- no indices, url = " << url;
|
||||
return;
|
||||
}
|
||||
|
||||
auto indexBuffer = std::make_shared<gpu::Buffer>();
|
||||
indexBuffer->resize(totalIndices * sizeof(int));
|
||||
|
||||
int indexNum = 0;
|
||||
int offset = 0;
|
||||
|
||||
std::vector< graphics::Mesh::Part > parts;
|
||||
if (extractedMesh.parts.size() > 1) {
|
||||
indexNum = 0;
|
||||
}
|
||||
foreach(const HFMMeshPart& part, extractedMesh.parts) {
|
||||
graphics::Mesh::Part modelPart(indexNum, 0, 0, graphics::Mesh::TRIANGLES);
|
||||
|
||||
if (part.quadTrianglesIndices.size()) {
|
||||
indexBuffer->setSubData(offset,
|
||||
part.quadTrianglesIndices.size() * sizeof(int),
|
||||
(gpu::Byte*) part.quadTrianglesIndices.constData());
|
||||
offset += part.quadTrianglesIndices.size() * sizeof(int);
|
||||
indexNum += part.quadTrianglesIndices.size();
|
||||
modelPart._numIndices += part.quadTrianglesIndices.size();
|
||||
}
|
||||
|
||||
if (part.triangleIndices.size()) {
|
||||
indexBuffer->setSubData(offset,
|
||||
part.triangleIndices.size() * sizeof(int),
|
||||
(gpu::Byte*) part.triangleIndices.constData());
|
||||
offset += part.triangleIndices.size() * sizeof(int);
|
||||
indexNum += part.triangleIndices.size();
|
||||
modelPart._numIndices += part.triangleIndices.size();
|
||||
}
|
||||
|
||||
parts.push_back(modelPart);
|
||||
}
|
||||
|
||||
gpu::BufferView indexBufferView(indexBuffer, gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::XYZ));
|
||||
mesh->setIndexBuffer(indexBufferView);
|
||||
|
||||
if (parts.size()) {
|
||||
auto pb = std::make_shared<gpu::Buffer>();
|
||||
pb->setData(parts.size() * sizeof(graphics::Mesh::Part), (const gpu::Byte*) parts.data());
|
||||
gpu::BufferView pbv(pb, gpu::Element(gpu::VEC4, gpu::UINT32, gpu::XYZW));
|
||||
mesh->setPartBuffer(pbv);
|
||||
} else {
|
||||
qCDebug(modelformat) << "buildModelMesh failed -- no parts, url = " << url;
|
||||
return;
|
||||
}
|
||||
|
||||
// graphics::Box box =
|
||||
mesh->evalPartBound(0);
|
||||
|
||||
extractedMesh._mesh = mesh;
|
||||
}
|
||||
|
|
|
@ -890,7 +890,6 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const QUrl& url) {
|
|||
}
|
||||
|
||||
mesh.meshIndex = hfmModel.meshes.size();
|
||||
FBXSerializer::buildModelMesh(mesh, url.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -831,9 +831,6 @@ HFMModel::Pointer OBJSerializer::read(const QByteArray& data, const QVariantHash
|
|||
hfmModel.meshExtents.addPoint(vertex);
|
||||
}
|
||||
|
||||
// Build the single mesh.
|
||||
FBXSerializer::buildModelMesh(mesh, _url.toString());
|
||||
|
||||
// hfmDebugDump(hfmModel);
|
||||
} catch(const std::exception& e) {
|
||||
qCDebug(modelformat) << "OBJSerializer fail: " << e.what();
|
||||
|
|
|
@ -25,6 +25,30 @@
|
|||
#include <graphics/Geometry.h>
|
||||
#include <graphics/Material.h>
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
#define HFM_PACK_NORMALS 0
|
||||
#else
|
||||
#define HFM_PACK_NORMALS 1
|
||||
#endif
|
||||
|
||||
#if HFM_PACK_NORMALS
|
||||
using NormalType = glm::uint32;
|
||||
#define HFM_NORMAL_ELEMENT gpu::Element::VEC4F_NORMALIZED_XYZ10W2
|
||||
#else
|
||||
using NormalType = glm::vec3;
|
||||
#define HFM_NORMAL_ELEMENT gpu::Element::VEC3F_XYZ
|
||||
#endif
|
||||
|
||||
#define HFM_PACK_COLORS 1
|
||||
|
||||
#if HFM_PACK_COLORS
|
||||
using ColorType = glm::uint32;
|
||||
#define HFM_COLOR_ELEMENT gpu::Element::COLOR_RGBA_32
|
||||
#else
|
||||
using ColorType = glm::vec3;
|
||||
#define HFM_COLOR_ELEMENT gpu::Element::VEC3F_XYZ
|
||||
#endif
|
||||
|
||||
const int MAX_NUM_PIXELS_FOR_FBX_TEXTURE = 2048 * 2048;
|
||||
|
||||
// High Fidelity Model namespace
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
set(TARGET_NAME model-baker)
|
||||
setup_hifi_library()
|
||||
|
||||
link_hifi_libraries(shared task)
|
||||
link_hifi_libraries(shared task gpu graphics)
|
||||
|
||||
include_hifi_library_headers(gpu)
|
||||
include_hifi_library_headers(graphics)
|
||||
include_hifi_library_headers(hfm)
|
||||
|
|
|
@ -11,17 +11,89 @@
|
|||
|
||||
#include "Baker.h"
|
||||
|
||||
#include <shared/HifiTypes.h>
|
||||
|
||||
#include "BakerTypes.h"
|
||||
#include "BuildGraphicsMeshTask.h"
|
||||
|
||||
namespace baker {
|
||||
|
||||
class GetModelPartsTask {
|
||||
public:
|
||||
using Input = hfm::Model::Pointer;
|
||||
using Output = VaryingSet3<std::vector<hfm::Mesh>, hifi::URL, MeshIndicesToModelNames>;
|
||||
using JobModel = Job::ModelIO<GetModelPartsTask, Input, Output>;
|
||||
|
||||
void run(const BakeContextPointer& context, const Input& input, Output& output) {
|
||||
auto& hfmModelIn = input;
|
||||
output.edit0() = hfmModelIn->meshes.toStdVector();
|
||||
output.edit1() = hfmModelIn->originalURL;
|
||||
output.edit2() = hfmModelIn->meshIndicesToModelNames;
|
||||
}
|
||||
};
|
||||
|
||||
class BuildMeshesTask {
|
||||
public:
|
||||
using Input = VaryingSet4<std::vector<hfm::Mesh>, std::vector<graphics::MeshPointer>, TangentsPerMesh, BlendshapesPerMesh>;
|
||||
using Output = std::vector<hfm::Mesh>;
|
||||
using JobModel = Job::ModelIO<BuildMeshesTask, Input, Output>;
|
||||
|
||||
void run(const BakeContextPointer& context, const Input& input, Output& output) {
|
||||
auto& meshesIn = input.get0();
|
||||
int numMeshes = (int)meshesIn.size();
|
||||
auto& graphicsMeshesIn = input.get1();
|
||||
auto& tangentsPerMeshIn = input.get2();
|
||||
auto& blendshapesPerMeshIn = input.get3();
|
||||
|
||||
auto meshesOut = meshesIn;
|
||||
for (int i = 0; i < numMeshes; i++) {
|
||||
auto& meshOut = meshesOut[i];
|
||||
meshOut._mesh = graphicsMeshesIn[i];
|
||||
meshOut.tangents = QVector<glm::vec3>::fromStdVector(tangentsPerMeshIn[i]);
|
||||
meshOut.blendshapes = QVector<hfm::Blendshape>::fromStdVector(blendshapesPerMeshIn[i]);
|
||||
}
|
||||
output = meshesOut;
|
||||
}
|
||||
};
|
||||
|
||||
class BuildModelTask {
|
||||
public:
|
||||
using Input = VaryingSet2<hfm::Model::Pointer, std::vector<hfm::Mesh>>;
|
||||
using Output = hfm::Model::Pointer;
|
||||
using JobModel = Job::ModelIO<BuildModelTask, Input, Output>;
|
||||
|
||||
void run(const BakeContextPointer& context, const Input& input, Output& output) {
|
||||
auto hfmModelOut = input.get0();
|
||||
hfmModelOut->meshes = QVector<hfm::Mesh>::fromStdVector(input.get1());
|
||||
output = hfmModelOut;
|
||||
}
|
||||
};
|
||||
|
||||
class BakerEngineBuilder {
|
||||
public:
|
||||
using Unused = int;
|
||||
|
||||
using Input = hfm::Model::Pointer;
|
||||
using Output = hfm::Model::Pointer;
|
||||
using JobModel = Task::ModelIO<BakerEngineBuilder, Input, Output>;
|
||||
void build(JobModel& model, const Varying& in, Varying& out) {
|
||||
out = in;
|
||||
void build(JobModel& model, const Varying& hfmModelIn, Varying& hfmModelOut) {
|
||||
// Split up the inputs from hfm::Model
|
||||
const auto modelPartsIn = model.addJob<GetModelPartsTask>("GetModelParts", hfmModelIn);
|
||||
const auto meshesIn = modelPartsIn.getN<GetModelPartsTask::Output>(0);
|
||||
const auto url = modelPartsIn.getN<GetModelPartsTask::Output>(1);
|
||||
const auto meshIndicesToModelNames = modelPartsIn.getN<GetModelPartsTask::Output>(2);
|
||||
|
||||
// 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);
|
||||
|
||||
// Combine the outputs into a new hfm::Model
|
||||
const auto buildMeshesInputs = BuildMeshesTask::Input(meshesIn, graphicsMeshes, tangentsPerMesh, blendshapesPerMesh).asVarying();
|
||||
const auto meshesOut = model.addJob<BuildMeshesTask>("BuildMeshes", buildMeshesInputs);
|
||||
const auto buildModelInputs = BuildModelTask::Input(hfmModelIn, meshesOut).asVarying();
|
||||
hfmModelOut = model.addJob<BuildModelTask>("BuildModel", buildModelInputs);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include "Engine.h"
|
||||
|
||||
namespace baker {
|
||||
|
||||
class Baker {
|
||||
public:
|
||||
Baker(const hfm::Model::Pointer& hfmModel);
|
||||
|
|
25
libraries/model-baker/src/model-baker/BakerTypes.h
Normal file
25
libraries/model-baker/src/model-baker/BakerTypes.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
// BakerTypes.h
|
||||
// model-baker/src/model-baker
|
||||
//
|
||||
// Created by Sabrina Shanman on 2018/12/10.
|
||||
// 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_BakerTypes_h
|
||||
#define hifi_BakerTypes_h
|
||||
|
||||
#include <hfm/HFM.h>
|
||||
|
||||
namespace baker {
|
||||
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 MeshIndicesToModelNames = QHash<int, QString>;
|
||||
};
|
||||
|
||||
#endif // hifi_BakerTypes_h
|
412
libraries/model-baker/src/model-baker/BuildGraphicsMeshTask.cpp
Normal file
412
libraries/model-baker/src/model-baker/BuildGraphicsMeshTask.cpp
Normal file
|
@ -0,0 +1,412 @@
|
|||
//
|
||||
// BuildGraphicsMeshTask.h
|
||||
// model-baker/src/model-baker
|
||||
//
|
||||
// Created by Sabrina Shanman on 2018/12/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 "BuildGraphicsMeshTask.h"
|
||||
|
||||
#include <glm/gtc/packing.hpp>
|
||||
|
||||
#include <LogHandler.h>
|
||||
#include "ModelBakerLogging.h"
|
||||
|
||||
using vec2h = glm::tvec2<glm::detail::hdata>;
|
||||
|
||||
glm::vec3 normalizeDirForPacking(const glm::vec3& dir) {
|
||||
auto maxCoord = glm::max(fabsf(dir.x), glm::max(fabsf(dir.y), fabsf(dir.z)));
|
||||
if (maxCoord > 1e-6f) {
|
||||
return dir / maxCoord;
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
|
||||
void buildGraphicsMesh(const hfm::Mesh& hfmMesh, graphics::MeshPointer& graphicsMeshPointer, baker::MeshTangents& meshTangents, baker::Blendshapes& blendshapes) {
|
||||
auto graphicsMesh = std::make_shared<graphics::Mesh>();
|
||||
|
||||
unsigned int totalSourceIndices = 0;
|
||||
foreach(const HFMMeshPart& part, hfmMesh.parts) {
|
||||
totalSourceIndices += (part.quadTrianglesIndices.size() + part.triangleIndices.size());
|
||||
}
|
||||
|
||||
static int repeatMessageID = LogHandler::getInstance().newRepeatedMessageID();
|
||||
|
||||
if (!totalSourceIndices) {
|
||||
HIFI_FCDEBUG_ID(model_baker(), repeatMessageID, "BuildGraphicsMeshTask failed -- no indices");
|
||||
return;
|
||||
}
|
||||
|
||||
if (hfmMesh.vertices.size() == 0) {
|
||||
HIFI_FCDEBUG_ID(model_baker(), repeatMessageID, "BuildGraphicsMeshTask failed -- no vertices");
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
const auto positionElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ);
|
||||
const int positionsSize = numVerts * positionElement.getSize();
|
||||
|
||||
// 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 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");
|
||||
}
|
||||
const auto normalsAndTangentsSize = normalsSize + tangentsSize;
|
||||
|
||||
// Color attrib
|
||||
const auto colorElement = HFM_COLOR_ELEMENT;
|
||||
const int colorsSize = hfmMesh.colors.size() * colorElement.getSize();
|
||||
|
||||
// Texture coordinates are stored in 2 half floats
|
||||
const auto texCoordsElement = gpu::Element(gpu::VEC2, gpu::HALF, gpu::UV);
|
||||
const int texCoordsSize = hfmMesh.texCoords.size() * texCoordsElement.getSize();
|
||||
const int texCoords1Size = hfmMesh.texCoords1.size() * texCoordsElement.getSize();
|
||||
|
||||
// Support for 4 skinning clusters:
|
||||
// 4 Indices are uint8 ideally, uint16 if more than 256.
|
||||
const auto clusterIndiceElement = (hfmMesh.clusters.size() < UINT8_MAX ? gpu::Element(gpu::VEC4, gpu::UINT8, gpu::XYZW) : gpu::Element(gpu::VEC4, gpu::UINT16, gpu::XYZW));
|
||||
// 4 Weights are normalized 16bits
|
||||
const auto clusterWeightElement = gpu::Element(gpu::VEC4, gpu::NUINT16, gpu::XYZW);
|
||||
|
||||
// Cluster indices and weights must be the same sizes
|
||||
const int NUM_CLUSTERS_PER_VERT = 4;
|
||||
const int numVertClusters = (hfmMesh.clusterIndices.size() == hfmMesh.clusterWeights.size() ? hfmMesh.clusterIndices.size() / NUM_CLUSTERS_PER_VERT : 0);
|
||||
const int clusterIndicesSize = numVertClusters * clusterIndiceElement.getSize();
|
||||
const int clusterWeightsSize = numVertClusters * clusterWeightElement.getSize();
|
||||
|
||||
// Decide on where to put what seequencially in a big buffer:
|
||||
const int positionsOffset = 0;
|
||||
const int normalsAndTangentsOffset = positionsOffset + positionsSize;
|
||||
const int colorsOffset = normalsAndTangentsOffset + normalsAndTangentsSize;
|
||||
const int texCoordsOffset = colorsOffset + colorsSize;
|
||||
const int texCoords1Offset = texCoordsOffset + texCoordsSize;
|
||||
const int clusterIndicesOffset = texCoords1Offset + texCoords1Size;
|
||||
const int clusterWeightsOffset = clusterIndicesOffset + clusterIndicesSize;
|
||||
const int totalVertsSize = clusterWeightsOffset + clusterWeightsSize;
|
||||
|
||||
// Copy all vertex data in a single buffer
|
||||
auto vertBuffer = std::make_shared<gpu::Buffer>();
|
||||
vertBuffer->resize(totalVertsSize);
|
||||
|
||||
// First positions
|
||||
vertBuffer->setSubData(positionsOffset, positionsSize, (const gpu::Byte*) hfmMesh.vertices.data());
|
||||
|
||||
// Interleave normals and tangents
|
||||
if (normalsSize > 0) {
|
||||
std::vector<NormalType> normalsAndTangents;
|
||||
|
||||
normalsAndTangents.reserve(hfmMesh.normals.size() + (int)meshTangents.size());
|
||||
auto normalIt = hfmMesh.normals.constBegin();
|
||||
auto tangentIt = meshTangents.cbegin();
|
||||
for (;
|
||||
normalIt != hfmMesh.normals.constEnd();
|
||||
++normalIt, ++tangentIt) {
|
||||
#if HFM_PACK_NORMALS
|
||||
const auto normal = normalizeDirForPacking(*normalIt);
|
||||
const auto tangent = normalizeDirForPacking(*tangentIt);
|
||||
const auto packedNormal = glm::packSnorm3x10_1x2(glm::vec4(normal, 0.0f));
|
||||
const auto packedTangent = glm::packSnorm3x10_1x2(glm::vec4(tangent, 0.0f));
|
||||
#else
|
||||
const auto packedNormal = *normalIt;
|
||||
const auto packedTangent = *tangentIt;
|
||||
#endif
|
||||
normalsAndTangents.push_back(packedNormal);
|
||||
normalsAndTangents.push_back(packedTangent);
|
||||
}
|
||||
vertBuffer->setSubData(normalsAndTangentsOffset, normalsAndTangentsSize, (const gpu::Byte*) normalsAndTangents.data());
|
||||
}
|
||||
|
||||
// Pack colors
|
||||
if (colorsSize > 0) {
|
||||
#if HFM_PACK_COLORS
|
||||
std::vector<ColorType> colors;
|
||||
|
||||
colors.reserve(hfmMesh.colors.size());
|
||||
for (const auto& color : hfmMesh.colors) {
|
||||
colors.push_back(glm::packUnorm4x8(glm::vec4(color, 1.0f)));
|
||||
}
|
||||
vertBuffer->setSubData(colorsOffset, colorsSize, (const gpu::Byte*) colors.data());
|
||||
#else
|
||||
vertBuffer->setSubData(colorsOffset, colorsSize, (const gpu::Byte*) hfmMesh.colors.constData());
|
||||
#endif
|
||||
}
|
||||
|
||||
// Pack Texcoords 0 and 1 (if exists)
|
||||
if (texCoordsSize > 0) {
|
||||
QVector<vec2h> texCoordData;
|
||||
texCoordData.reserve(hfmMesh.texCoords.size());
|
||||
for (auto& texCoordVec2f : hfmMesh.texCoords) {
|
||||
vec2h texCoordVec2h;
|
||||
|
||||
texCoordVec2h.x = glm::detail::toFloat16(texCoordVec2f.x);
|
||||
texCoordVec2h.y = glm::detail::toFloat16(texCoordVec2f.y);
|
||||
texCoordData.push_back(texCoordVec2h);
|
||||
}
|
||||
vertBuffer->setSubData(texCoordsOffset, texCoordsSize, (const gpu::Byte*) texCoordData.constData());
|
||||
}
|
||||
if (texCoords1Size > 0) {
|
||||
QVector<vec2h> texCoordData;
|
||||
texCoordData.reserve(hfmMesh.texCoords1.size());
|
||||
for (auto& texCoordVec2f : hfmMesh.texCoords1) {
|
||||
vec2h texCoordVec2h;
|
||||
|
||||
texCoordVec2h.x = glm::detail::toFloat16(texCoordVec2f.x);
|
||||
texCoordVec2h.y = glm::detail::toFloat16(texCoordVec2f.y);
|
||||
texCoordData.push_back(texCoordVec2h);
|
||||
}
|
||||
vertBuffer->setSubData(texCoords1Offset, texCoords1Size, (const gpu::Byte*) texCoordData.constData());
|
||||
}
|
||||
|
||||
// Clusters data
|
||||
if (clusterIndicesSize > 0) {
|
||||
if (hfmMesh.clusters.size() < UINT8_MAX) {
|
||||
// yay! we can fit the clusterIndices within 8-bits
|
||||
int32_t numIndices = hfmMesh.clusterIndices.size();
|
||||
QVector<uint8_t> clusterIndices;
|
||||
clusterIndices.resize(numIndices);
|
||||
for (int32_t i = 0; i < numIndices; ++i) {
|
||||
assert(hfmMesh.clusterIndices[i] <= UINT8_MAX);
|
||||
clusterIndices[i] = (uint8_t)(hfmMesh.clusterIndices[i]);
|
||||
}
|
||||
vertBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (const gpu::Byte*) clusterIndices.constData());
|
||||
} else {
|
||||
vertBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (const gpu::Byte*) hfmMesh.clusterIndices.constData());
|
||||
}
|
||||
}
|
||||
if (clusterWeightsSize > 0) {
|
||||
vertBuffer->setSubData(clusterWeightsOffset, clusterWeightsSize, (const gpu::Byte*) hfmMesh.clusterWeights.constData());
|
||||
}
|
||||
|
||||
|
||||
// Now we decide on how to interleave the attributes and provide the vertices among bufers:
|
||||
// Aka the Vertex format and the vertexBufferStream
|
||||
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;
|
||||
gpu::uint8 tangentChannel = posChannel;
|
||||
gpu::uint8 attribChannel = posChannel;
|
||||
bool interleavePositions = true;
|
||||
bool interleaveNormalsTangents = true;
|
||||
|
||||
// Define the vertex format, compute the offset for each attributes as we append them to the vertex format
|
||||
gpu::Offset bufOffset = 0;
|
||||
if (positionsSize) {
|
||||
vertexFormat->setAttribute(gpu::Stream::POSITION, posChannel, positionElement, bufOffset);
|
||||
bufOffset += positionElement.getSize();
|
||||
if (!interleavePositions) {
|
||||
bufOffset = 0;
|
||||
}
|
||||
}
|
||||
if (normalsSize) {
|
||||
vertexFormat->setAttribute(gpu::Stream::NORMAL, tangentChannel, normalElement, bufOffset);
|
||||
bufOffset += normalElement.getSize();
|
||||
vertexFormat->setAttribute(gpu::Stream::TANGENT, tangentChannel, normalElement, bufOffset);
|
||||
bufOffset += normalElement.getSize();
|
||||
if (!interleaveNormalsTangents) {
|
||||
bufOffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Pack normal and Tangent with the rest of atributes if no blend shapes
|
||||
if (colorsSize) {
|
||||
vertexFormat->setAttribute(gpu::Stream::COLOR, attribChannel, colorElement, bufOffset);
|
||||
bufOffset += colorElement.getSize();
|
||||
}
|
||||
if (texCoordsSize) {
|
||||
vertexFormat->setAttribute(gpu::Stream::TEXCOORD, attribChannel, texCoordsElement, bufOffset);
|
||||
bufOffset += texCoordsElement.getSize();
|
||||
}
|
||||
if (texCoords1Size) {
|
||||
vertexFormat->setAttribute(gpu::Stream::TEXCOORD1, attribChannel, texCoordsElement, bufOffset);
|
||||
bufOffset += texCoordsElement.getSize();
|
||||
} else if (texCoordsSize) {
|
||||
vertexFormat->setAttribute(gpu::Stream::TEXCOORD1, attribChannel, texCoordsElement, bufOffset - texCoordsElement.getSize());
|
||||
}
|
||||
if (clusterIndicesSize) {
|
||||
vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_INDEX, attribChannel, clusterIndiceElement, bufOffset);
|
||||
bufOffset += clusterIndiceElement.getSize();
|
||||
}
|
||||
if (clusterWeightsSize) {
|
||||
vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT, attribChannel, clusterWeightElement, bufOffset);
|
||||
bufOffset += clusterWeightElement.getSize();
|
||||
}
|
||||
|
||||
// Finally, allocate and fill the attribBuffer interleaving the attributes as needed:
|
||||
{
|
||||
auto vPositionOffset = 0;
|
||||
auto vPositionSize = (interleavePositions ? positionsSize / numVerts : 0);
|
||||
|
||||
auto vNormalsAndTangentsOffset = vPositionOffset + vPositionSize;
|
||||
auto vNormalsAndTangentsSize = (interleaveNormalsTangents ? normalsAndTangentsSize / numVerts : 0);
|
||||
|
||||
auto vColorOffset = vNormalsAndTangentsOffset + vNormalsAndTangentsSize;
|
||||
auto vColorSize = colorsSize / numVerts;
|
||||
|
||||
auto vTexcoord0Offset = vColorOffset + vColorSize;
|
||||
auto vTexcoord0Size = texCoordsSize / numVerts;
|
||||
|
||||
auto vTexcoord1Offset = vTexcoord0Offset + vTexcoord0Size;
|
||||
auto vTexcoord1Size = texCoords1Size / numVerts;
|
||||
|
||||
auto vClusterIndiceOffset = vTexcoord1Offset + vTexcoord1Size;
|
||||
auto vClusterIndiceSize = clusterIndicesSize / numVerts;
|
||||
|
||||
auto vClusterWeightOffset = vClusterIndiceOffset + vClusterIndiceSize;
|
||||
auto vClusterWeightSize = clusterWeightsSize / numVerts;
|
||||
|
||||
auto vStride = vClusterWeightOffset + vClusterWeightSize;
|
||||
|
||||
std::vector<gpu::Byte> dest;
|
||||
dest.resize(totalAttribBufferSize);
|
||||
auto vDest = dest.data();
|
||||
|
||||
auto source = vertBuffer->getData();
|
||||
|
||||
for (int i = 0; i < numVerts; i++) {
|
||||
|
||||
if (vPositionSize) memcpy(vDest + vPositionOffset, source + positionsOffset + i * vPositionSize, vPositionSize);
|
||||
if (vNormalsAndTangentsSize) memcpy(vDest + vNormalsAndTangentsOffset, source + normalsAndTangentsOffset + i * vNormalsAndTangentsSize, vNormalsAndTangentsSize);
|
||||
if (vColorSize) memcpy(vDest + vColorOffset, source + colorsOffset + i * vColorSize, vColorSize);
|
||||
if (vTexcoord0Size) memcpy(vDest + vTexcoord0Offset, source + texCoordsOffset + i * vTexcoord0Size, vTexcoord0Size);
|
||||
if (vTexcoord1Size) memcpy(vDest + vTexcoord1Offset, source + texCoords1Offset + i * vTexcoord1Size, vTexcoord1Size);
|
||||
if (vClusterIndiceSize) memcpy(vDest + vClusterIndiceOffset, source + clusterIndicesOffset + i * vClusterIndiceSize, vClusterIndiceSize);
|
||||
if (vClusterWeightSize) memcpy(vDest + vClusterWeightOffset, source + clusterWeightsOffset + i * vClusterWeightSize, vClusterWeightSize);
|
||||
|
||||
vDest += vStride;
|
||||
}
|
||||
|
||||
auto attribBuffer = std::make_shared<gpu::Buffer>();
|
||||
attribBuffer->setData(totalAttribBufferSize, dest.data());
|
||||
vertexBufferStream->addBuffer(attribBuffer, 0, vStride);
|
||||
}
|
||||
|
||||
// Mesh vertex format and vertex stream is ready
|
||||
graphicsMesh->setVertexFormatAndStream(vertexFormat, vertexBufferStream);
|
||||
|
||||
// Index and Part Buffers
|
||||
unsigned int totalIndices = 0;
|
||||
foreach(const HFMMeshPart& part, hfmMesh.parts) {
|
||||
totalIndices += (part.quadTrianglesIndices.size() + part.triangleIndices.size());
|
||||
}
|
||||
|
||||
if (!totalIndices) {
|
||||
HIFI_FCDEBUG_ID(model_baker(), repeatMessageID, "BuildGraphicsMeshTask failed -- no indices");
|
||||
return;
|
||||
}
|
||||
|
||||
auto indexBuffer = std::make_shared<gpu::Buffer>();
|
||||
indexBuffer->resize(totalIndices * sizeof(int));
|
||||
|
||||
int indexNum = 0;
|
||||
int offset = 0;
|
||||
|
||||
std::vector< graphics::Mesh::Part > parts;
|
||||
if (hfmMesh.parts.size() > 1) {
|
||||
indexNum = 0;
|
||||
}
|
||||
foreach(const HFMMeshPart& part, hfmMesh.parts) {
|
||||
graphics::Mesh::Part modelPart(indexNum, 0, 0, graphics::Mesh::TRIANGLES);
|
||||
|
||||
if (part.quadTrianglesIndices.size()) {
|
||||
indexBuffer->setSubData(offset,
|
||||
part.quadTrianglesIndices.size() * sizeof(int),
|
||||
(gpu::Byte*) part.quadTrianglesIndices.constData());
|
||||
offset += part.quadTrianglesIndices.size() * sizeof(int);
|
||||
indexNum += part.quadTrianglesIndices.size();
|
||||
modelPart._numIndices += part.quadTrianglesIndices.size();
|
||||
}
|
||||
|
||||
if (part.triangleIndices.size()) {
|
||||
indexBuffer->setSubData(offset,
|
||||
part.triangleIndices.size() * sizeof(int),
|
||||
(gpu::Byte*) part.triangleIndices.constData());
|
||||
offset += part.triangleIndices.size() * sizeof(int);
|
||||
indexNum += part.triangleIndices.size();
|
||||
modelPart._numIndices += part.triangleIndices.size();
|
||||
}
|
||||
|
||||
parts.push_back(modelPart);
|
||||
}
|
||||
|
||||
gpu::BufferView indexBufferView(indexBuffer, gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::XYZ));
|
||||
graphicsMesh->setIndexBuffer(indexBufferView);
|
||||
|
||||
if (parts.size()) {
|
||||
auto pb = std::make_shared<gpu::Buffer>();
|
||||
pb->setData(parts.size() * sizeof(graphics::Mesh::Part), (const gpu::Byte*) parts.data());
|
||||
gpu::BufferView pbv(pb, gpu::Element(gpu::VEC4, gpu::UINT32, gpu::XYZW));
|
||||
graphicsMesh->setPartBuffer(pbv);
|
||||
} else {
|
||||
HIFI_FCDEBUG_ID(model_baker(), repeatMessageID, "BuildGraphicsMeshTask failed -- no parts");
|
||||
return;
|
||||
}
|
||||
|
||||
graphicsMesh->evalPartBound(0);
|
||||
|
||||
graphicsMeshPointer = graphicsMesh;
|
||||
}
|
||||
|
||||
void BuildGraphicsMeshTask::run(const baker::BakeContextPointer& context, const Input& input, Output& output) {
|
||||
auto& meshes = input.get0();
|
||||
auto& url = input.get1();
|
||||
auto& meshIndicesToModelNames = input.get2();
|
||||
|
||||
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]);
|
||||
|
||||
// Choose a name for the mesh
|
||||
if (graphicsMesh) {
|
||||
graphicsMesh->displayName = url.toString().toStdString() + "#/mesh/" + std::to_string(i);
|
||||
if (meshIndicesToModelNames.find(i) != meshIndicesToModelNames.cend()) {
|
||||
graphicsMesh->modelName = meshIndicesToModelNames[i].toStdString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
// BuildGraphicsMeshTask.h
|
||||
// model-baker/src/model-baker
|
||||
//
|
||||
// Created by Sabrina Shanman on 2018/12/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
|
||||
//
|
||||
|
||||
#ifndef hifi_BuildGraphicsMeshTask_h
|
||||
#define hifi_BuildGraphicsMeshTask_h
|
||||
|
||||
#include <hfm/HFM.h>
|
||||
#include <shared/HifiTypes.h>
|
||||
|
||||
#include "Engine.h"
|
||||
#include "BakerTypes.h"
|
||||
|
||||
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 JobModel = baker::Job::ModelIO<BuildGraphicsMeshTask, Input, Output>;
|
||||
|
||||
void run(const baker::BakeContextPointer& context, const Input& input, Output& output);
|
||||
};
|
||||
|
||||
#endif // hifi_BuildGraphicsMeshTask_h
|
14
libraries/model-baker/src/model-baker/ModelBakerLogging.cpp
Normal file
14
libraries/model-baker/src/model-baker/ModelBakerLogging.cpp
Normal file
|
@ -0,0 +1,14 @@
|
|||
//
|
||||
// ModelBakerLogging.cpp
|
||||
// libraries/baker/src/baker
|
||||
//
|
||||
// Created by Sabrina Shanman on 2018/12/12.
|
||||
// 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 "ModelBakerLogging.h"
|
||||
|
||||
Q_LOGGING_CATEGORY(model_baker, "hifi.model_baker")
|
19
libraries/model-baker/src/model-baker/ModelBakerLogging.h
Normal file
19
libraries/model-baker/src/model-baker/ModelBakerLogging.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
//
|
||||
// ModelBakerLogging.h
|
||||
// libraries/baker/src/baker
|
||||
//
|
||||
// Created by Sabrina Shanman on 2018/12/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
|
||||
//
|
||||
|
||||
#ifndef hifi_ModelBakerLogging_h
|
||||
#define hifi_ModelBakerLogging_h
|
||||
|
||||
#include <QLoggingCategory>
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(model_baker)
|
||||
|
||||
#endif // hifi_ModelBakerLogging_h
|
Loading…
Reference in a new issue