From ec748c60cf00cd2515369c196f832e8eaa9c0d02 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Fri, 7 Dec 2018 12:11:19 -0800 Subject: [PATCH 1/7] Give more appropriate names for some model-related constants and move them into HFM.h --- libraries/fbx/src/FBX.h | 14 -------------- libraries/fbx/src/FBXSerializer_Mesh.cpp | 16 +++------------- libraries/hfm/src/hfm/HFM.h | 24 ++++++++++++++++++++++++ 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index 157ca5b282..8ad419c7ec 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -19,20 +19,6 @@ #include -#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 "); diff --git a/libraries/fbx/src/FBXSerializer_Mesh.cpp b/libraries/fbx/src/FBXSerializer_Mesh.cpp index 38533dbc42..a71424e286 100644 --- a/libraries/fbx/src/FBXSerializer_Mesh.cpp +++ b/libraries/fbx/src/FBXSerializer_Mesh.cpp @@ -42,16 +42,6 @@ using vec2h = glm::tvec2; -#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; @@ -608,7 +598,7 @@ void FBXSerializer::buildModelMesh(HFMMesh& extractedMesh, const QString& url) { 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 auto normalElement = HFM_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 @@ -619,7 +609,7 @@ void FBXSerializer::buildModelMesh(HFMMesh& extractedMesh, const QString& url) { const auto normalsAndTangentsSize = normalsSize + tangentsSize; // Color attrib - const auto colorElement = FBX_COLOR_ELEMENT; + const auto colorElement = HFM_COLOR_ELEMENT; const int colorsSize = hfmMesh.colors.size() * colorElement.getSize(); // Texture coordinates are stored in 2 half floats @@ -664,7 +654,7 @@ void FBXSerializer::buildModelMesh(HFMMesh& extractedMesh, const QString& url) { for (auto normalIt = hfmMesh.normals.constBegin(), tangentIt = hfmMesh.tangents.constBegin(); normalIt != hfmMesh.normals.constEnd(); ++normalIt, ++tangentIt) { -#if FBX_PACK_NORMALS +#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)); diff --git a/libraries/hfm/src/hfm/HFM.h b/libraries/hfm/src/hfm/HFM.h index de58d864b3..738f055330 100644 --- a/libraries/hfm/src/hfm/HFM.h +++ b/libraries/hfm/src/hfm/HFM.h @@ -25,6 +25,30 @@ #include #include +#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 From b021c3c46df12b051c35f6f85fd5e3aba0d6d66d Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Fri, 7 Dec 2018 15:49:59 -0800 Subject: [PATCH 2/7] Add model-baker logging category --- .../src/model-baker/ModelBakerLogging.cpp | 14 ++++++++++++++ .../src/model-baker/ModelBakerLogging.h | 19 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 libraries/model-baker/src/model-baker/ModelBakerLogging.cpp create mode 100644 libraries/model-baker/src/model-baker/ModelBakerLogging.h diff --git a/libraries/model-baker/src/model-baker/ModelBakerLogging.cpp b/libraries/model-baker/src/model-baker/ModelBakerLogging.cpp new file mode 100644 index 0000000000..0293f5e65b --- /dev/null +++ b/libraries/model-baker/src/model-baker/ModelBakerLogging.cpp @@ -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") diff --git a/libraries/model-baker/src/model-baker/ModelBakerLogging.h b/libraries/model-baker/src/model-baker/ModelBakerLogging.h new file mode 100644 index 0000000000..a872fe0682 --- /dev/null +++ b/libraries/model-baker/src/model-baker/ModelBakerLogging.h @@ -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 + +Q_DECLARE_LOGGING_CATEGORY(model_baker) + +#endif // hifi_ModelBakerLogging_h From e8a2622ed62a9a0675f10121342d41f194a8e979 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Mon, 10 Dec 2018 16:48:22 -0800 Subject: [PATCH 3/7] Move graphics prep to baker and add related tasks for splitting up the model --- libraries/fbx/src/FBXSerializer.cpp | 35 -- libraries/fbx/src/FBXSerializer.h | 3 - libraries/fbx/src/FBXSerializer_Mesh.cpp | 361 --------------- libraries/fbx/src/GLTFSerializer.cpp | 1 - libraries/fbx/src/OBJSerializer.cpp | 3 - .../model-baker/src/model-baker/Baker.cpp | 83 +++- libraries/model-baker/src/model-baker/Baker.h | 1 - .../model-baker/src/model-baker/BakerTypes.h | 25 ++ .../src/model-baker/BuildGraphicsMeshTask.cpp | 414 ++++++++++++++++++ .../src/model-baker/BuildGraphicsMeshTask.h | 30 ++ 10 files changed, 549 insertions(+), 407 deletions(-) create mode 100644 libraries/model-baker/src/model-baker/BakerTypes.h create mode 100644 libraries/model-baker/src/model-baker/BuildGraphicsMeshTask.cpp create mode 100644 libraries/model-baker/src/model-baker/BuildGraphicsMeshTask.h diff --git a/libraries/fbx/src/FBXSerializer.cpp b/libraries/fbx/src/FBXSerializer.cpp index b425b6795d..76523a31bc 100644 --- a/libraries/fbx/src/FBXSerializer.cpp +++ b/libraries/fbx/src/FBXSerializer.cpp @@ -11,27 +11,13 @@ #include "FBXSerializer.h" -#include #include -#include -#include -#include -#include -#include -#include -#include #include #include #include #include -#include -#include -#include -#include -#include -#include #include @@ -1744,14 +1730,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); } @@ -1819,22 +1800,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(); diff --git a/libraries/fbx/src/FBXSerializer.h b/libraries/fbx/src/FBXSerializer.h index a76fb8f9bf..7227ebac17 100644 --- a/libraries/fbx/src/FBXSerializer.h +++ b/libraries/fbx/src/FBXSerializer.h @@ -111,9 +111,6 @@ public: static ExtractedMesh extractMesh(const FBXNode& object, unsigned int& meshIndex, bool deduplicate = true); QHash meshes; - static void buildModelMesh(HFMMesh& extractedMesh, const QString& url); - - static glm::vec3 normalizeDirForPacking(const glm::vec3& dir); HFMTexture getTexture(const QString& textureID); diff --git a/libraries/fbx/src/FBXSerializer_Mesh.cpp b/libraries/fbx/src/FBXSerializer_Mesh.cpp index a71424e286..fd1f80425b 100644 --- a/libraries/fbx/src/FBXSerializer_Mesh.cpp +++ b/libraries/fbx/src/FBXSerializer_Mesh.cpp @@ -546,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 = HFM_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 = 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(); - vertBuffer->resize(totalVertsSize); - - // First positions - vertBuffer->setSubData(positionsOffset, positionsSize, (const gpu::Byte*) extractedMesh.vertices.data()); - - // Interleave normals and tangents - if (normalsSize > 0) { - std::vector 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 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 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 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 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 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(); - auto vertexBufferStream = std::make_shared(); - - // 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 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(); - 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(); - 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(); - 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; -} diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp index e254a91eb0..53b2ab4b15 100644 --- a/libraries/fbx/src/GLTFSerializer.cpp +++ b/libraries/fbx/src/GLTFSerializer.cpp @@ -894,7 +894,6 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const QUrl& url) { } mesh.meshIndex = hfmModel.meshes.size(); - FBXSerializer::buildModelMesh(mesh, url.toString()); } } diff --git a/libraries/fbx/src/OBJSerializer.cpp b/libraries/fbx/src/OBJSerializer.cpp index 9c92466565..9d4b1f16a1 100644 --- a/libraries/fbx/src/OBJSerializer.cpp +++ b/libraries/fbx/src/OBJSerializer.cpp @@ -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(); diff --git a/libraries/model-baker/src/model-baker/Baker.cpp b/libraries/model-baker/src/model-baker/Baker.cpp index 50b635583b..1f1493e9c2 100644 --- a/libraries/model-baker/src/model-baker/Baker.cpp +++ b/libraries/model-baker/src/model-baker/Baker.cpp @@ -11,17 +11,94 @@ #include "Baker.h" +#include + +#include "BakerTypes.h" +#include "BuildGraphicsMeshTask.h" + namespace baker { + class GetModelPartsTask { + public: + using Input = VaryingSet1; + using Output = VaryingSet3, hifi::URL, MeshIndicesToModelNames>; + using JobModel = Job::ModelIO; + + void run(const BakeContextPointer& context, const Input& input, Output& output) { + auto& hfmModelIn = input.get0(); + output.edit0() = hfmModelIn->meshes.toStdVector(); + output.edit1() = hfmModelIn->originalURL; + output.edit2() = hfmModelIn->meshIndicesToModelNames; + } + }; + + class BuildMeshesTask { + public: + using Input = VaryingSet4, std::vector, TangentsPerMesh, BlendshapesPerMesh>; + using Output = VaryingSet1>; + using JobModel = Job::ModelIO; + + 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::fromStdVector(tangentsPerMeshIn[i]); + meshOut.blendshapes = QVector::fromStdVector(blendshapesPerMeshIn[i]); + } + output.edit0() = meshesOut; + } + }; + + class BuildModelTask { + public: + using Input = VaryingSet2>; + using Output = VaryingSet1; + using JobModel = Job::ModelIO; + + void run(const BakeContextPointer& context, const Input& input, Output& output) { + auto hfmModelOut = input.get0(); + hfmModelOut->meshes = QVector::fromStdVector(input.get1()); + output.edit0() = hfmModelOut; + } + }; + class BakerEngineBuilder { public: - using Unused = int; - using Input = VaryingSet1; using Output = VaryingSet1; using JobModel = Task::ModelIO; void build(JobModel& model, const Varying& in, Varying& out) { - out = Output(in.getN(0)); + const auto hfmModelIn = in.getN(0); + // Split up the inputs from hfm::Model + const auto getModelPartsInputs = GetModelPartsTask::Input(hfmModelIn).asVarying(); + const auto modelPartsIn = model.addJob("GetModelParts", getModelPartsInputs); + const auto meshesIn = modelPartsIn.getN(0); + const auto url = modelPartsIn.getN(1); + const auto meshIndicesToModelNames = modelPartsIn.getN(2); + + // Build the graphics::MeshPointer for each hfm::Mesh + const auto buildGraphicsMeshInputs = BuildGraphicsMeshTask::Input(meshesIn, url, meshIndicesToModelNames).asVarying(); + const auto buildGraphicsMeshOutputs = model.addJob("BuildGraphicsMesh", buildGraphicsMeshInputs); + const auto graphicsMeshes = buildGraphicsMeshOutputs.getN(0); + // TODO: Move tangent/blendshape validation/calculation to an earlier step + const auto tangentsPerMesh = buildGraphicsMeshOutputs.getN(1); + const auto blendshapesPerMesh = buildGraphicsMeshOutputs.getN(2); + + // Combine the outputs into a new hfm::Model + const auto buildMeshesInputs = BuildMeshesTask::Input(meshesIn, graphicsMeshes, tangentsPerMesh, blendshapesPerMesh).asVarying(); + const auto buildMeshesOutputs = model.addJob("BuildMeshes", buildMeshesInputs); + const auto meshesOut = buildMeshesOutputs.getN(0); + const auto buildModelInputs = BuildModelTask::Input(hfmModelIn, meshesOut).asVarying(); + const auto buildModelOutputs = model.addJob("BuildModel", buildModelInputs); + const auto hfmModelOut = buildModelOutputs.getN(0); + out = Output(hfmModelOut); } }; diff --git a/libraries/model-baker/src/model-baker/Baker.h b/libraries/model-baker/src/model-baker/Baker.h index ba233243b3..7fb3f420e0 100644 --- a/libraries/model-baker/src/model-baker/Baker.h +++ b/libraries/model-baker/src/model-baker/Baker.h @@ -17,7 +17,6 @@ #include "Engine.h" namespace baker { - class Baker { public: Baker(const hfm::Model::Pointer& hfmModel); diff --git a/libraries/model-baker/src/model-baker/BakerTypes.h b/libraries/model-baker/src/model-baker/BakerTypes.h new file mode 100644 index 0000000000..8615c1b17c --- /dev/null +++ b/libraries/model-baker/src/model-baker/BakerTypes.h @@ -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 + +namespace baker { + using MeshTangents = std::vector; + using TangentsPerMesh = std::vector>; + using Blendshapes = std::vector; + using BlendshapesPerMesh = std::vector>; + using MeshIndicesToModelNames = QHash; +}; + +#endif // hifi_BakerTypes_h diff --git a/libraries/model-baker/src/model-baker/BuildGraphicsMeshTask.cpp b/libraries/model-baker/src/model-baker/BuildGraphicsMeshTask.cpp new file mode 100644 index 0000000000..5e3635a687 --- /dev/null +++ b/libraries/model-baker/src/model-baker/BuildGraphicsMeshTask.cpp @@ -0,0 +1,414 @@ +// +// 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 + +#include +#include "ModelBakerLogging.h" + +using vec2h = glm::tvec2; + +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(); + + 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(); + vertBuffer->resize(totalVertsSize); + + // First positions + vertBuffer->setSubData(positionsOffset, positionsSize, (const gpu::Byte*) hfmMesh.vertices.data()); + + // Interleave normals and tangents + if (normalsSize > 0) { + std::vector 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 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 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 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 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(); + auto vertexBufferStream = std::make_shared(); + + // 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 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(); + 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(); + 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(); + 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) { + static int repeatMessageID = LogHandler::getInstance().newRepeatedMessageID(); + + 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(); + } + } + } +} diff --git a/libraries/model-baker/src/model-baker/BuildGraphicsMeshTask.h b/libraries/model-baker/src/model-baker/BuildGraphicsMeshTask.h new file mode 100644 index 0000000000..3c8985ef9a --- /dev/null +++ b/libraries/model-baker/src/model-baker/BuildGraphicsMeshTask.h @@ -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 +#include + +#include "Engine.h" +#include "BakerTypes.h" + +class BuildGraphicsMeshTask { +public: + using Input = baker::VaryingSet3, hifi::URL, baker::MeshIndicesToModelNames>; + using Output = baker::VaryingSet3, std::vector, std::vector>; + using JobModel = baker::Job::ModelIO; + + void run(const baker::BakeContextPointer& context, const Input& input, Output& output); +}; + +#endif // hifi_BuildGraphicsMeshTask_h From 7c0f921eed9b7e040845d54361864d2c861cf04b Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Wed, 12 Dec 2018 15:20:23 -0800 Subject: [PATCH 4/7] Add shaders to assignment-client makefile to fix linker error --- assignment-client/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index 1500d7b98e..229e3641d0 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -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 ) From 332a195fa3404c1c1dc5a803940d1c6c6660482b Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Thu, 13 Dec 2018 13:06:12 -0800 Subject: [PATCH 5/7] Fix cross-platform linker errors in model-baker --- libraries/model-baker/CMakeLists.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libraries/model-baker/CMakeLists.txt b/libraries/model-baker/CMakeLists.txt index 58d9d1e60e..50b113976f 100644 --- a/libraries/model-baker/CMakeLists.txt +++ b/libraries/model-baker/CMakeLists.txt @@ -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) From 77a7eb9e0458a25dfb61b97da629024f290140f2 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Thu, 13 Dec 2018 13:38:04 -0800 Subject: [PATCH 6/7] Remove unused debug variable in BuildGraphicsMeshTask::run --- libraries/model-baker/src/model-baker/BuildGraphicsMeshTask.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/model-baker/src/model-baker/BuildGraphicsMeshTask.cpp b/libraries/model-baker/src/model-baker/BuildGraphicsMeshTask.cpp index 5e3635a687..6d351a99e9 100644 --- a/libraries/model-baker/src/model-baker/BuildGraphicsMeshTask.cpp +++ b/libraries/model-baker/src/model-baker/BuildGraphicsMeshTask.cpp @@ -384,8 +384,6 @@ void buildGraphicsMesh(const hfm::Mesh& hfmMesh, graphics::MeshPointer& graphics } void BuildGraphicsMeshTask::run(const baker::BakeContextPointer& context, const Input& input, Output& output) { - static int repeatMessageID = LogHandler::getInstance().newRepeatedMessageID(); - auto& meshes = input.get0(); auto& url = input.get1(); auto& meshIndicesToModelNames = input.get2(); From f25810104edc60beb43eddc1318f51aef810de5c Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Fri, 14 Dec 2018 10:51:45 -0800 Subject: [PATCH 7/7] Remove remaining references to VaryingSet1 in hfm model parts/building tasks --- .../model-baker/src/model-baker/Baker.cpp | 27 +++++++------------ 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/libraries/model-baker/src/model-baker/Baker.cpp b/libraries/model-baker/src/model-baker/Baker.cpp index b294c998eb..bf2b21993c 100644 --- a/libraries/model-baker/src/model-baker/Baker.cpp +++ b/libraries/model-baker/src/model-baker/Baker.cpp @@ -20,12 +20,12 @@ namespace baker { class GetModelPartsTask { public: - using Input = VaryingSet1; + using Input = hfm::Model::Pointer; using Output = VaryingSet3, hifi::URL, MeshIndicesToModelNames>; using JobModel = Job::ModelIO; void run(const BakeContextPointer& context, const Input& input, Output& output) { - auto& hfmModelIn = input.get0(); + auto& hfmModelIn = input; output.edit0() = hfmModelIn->meshes.toStdVector(); output.edit1() = hfmModelIn->originalURL; output.edit2() = hfmModelIn->meshIndicesToModelNames; @@ -35,7 +35,7 @@ namespace baker { class BuildMeshesTask { public: using Input = VaryingSet4, std::vector, TangentsPerMesh, BlendshapesPerMesh>; - using Output = VaryingSet1>; + using Output = std::vector; using JobModel = Job::ModelIO; void run(const BakeContextPointer& context, const Input& input, Output& output) { @@ -52,20 +52,20 @@ namespace baker { meshOut.tangents = QVector::fromStdVector(tangentsPerMeshIn[i]); meshOut.blendshapes = QVector::fromStdVector(blendshapesPerMeshIn[i]); } - output.edit0() = meshesOut; + output = meshesOut; } }; class BuildModelTask { public: using Input = VaryingSet2>; - using Output = VaryingSet1; + using Output = hfm::Model::Pointer; using JobModel = Job::ModelIO; void run(const BakeContextPointer& context, const Input& input, Output& output) { auto hfmModelOut = input.get0(); hfmModelOut->meshes = QVector::fromStdVector(input.get1()); - output.edit0() = hfmModelOut; + output = hfmModelOut; } }; @@ -74,12 +74,9 @@ namespace baker { using Input = hfm::Model::Pointer; using Output = hfm::Model::Pointer; using JobModel = Task::ModelIO; - void build(JobModel& model, const Varying& in, Varying& out) { - const auto hfmModelIn = in; - + void build(JobModel& model, const Varying& hfmModelIn, Varying& hfmModelOut) { // Split up the inputs from hfm::Model - const auto getModelPartsInputs = hfmModelIn; - const auto modelPartsIn = model.addJob("GetModelParts", getModelPartsInputs); + const auto modelPartsIn = model.addJob("GetModelParts", hfmModelIn); const auto meshesIn = modelPartsIn.getN(0); const auto url = modelPartsIn.getN(1); const auto meshIndicesToModelNames = modelPartsIn.getN(2); @@ -94,13 +91,9 @@ namespace baker { // Combine the outputs into a new hfm::Model const auto buildMeshesInputs = BuildMeshesTask::Input(meshesIn, graphicsMeshes, tangentsPerMesh, blendshapesPerMesh).asVarying(); - const auto buildMeshesOutputs = model.addJob("BuildMeshes", buildMeshesInputs); - const auto meshesOut = buildMeshesOutputs.getN(0); + const auto meshesOut = model.addJob("BuildMeshes", buildMeshesInputs); const auto buildModelInputs = BuildModelTask::Input(hfmModelIn, meshesOut).asVarying(); - const auto buildModelOutputs = model.addJob("BuildModel", buildModelInputs); - const auto hfmModelOut = buildModelOutputs.getN(0); - - out = hfmModelOut; + hfmModelOut = model.addJob("BuildModel", buildModelInputs); } };