From e96676fea41751f51ed611c57c1d3cfbd5e489ac Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 30 Nov 2016 18:28:55 -0800 Subject: [PATCH 1/2] Support for the 3ds Max object offset feature FBXReader now supports the GeometricTranslation, GeometricRotation and GeometricScaling FBX fields. These offsets are applied directly to the mesh vertices, as they are never animated and the rest of our rendering and animation system would have difficulty supporting them otherwise. --- libraries/fbx/src/FBXReader.cpp | 42 ++++++++++++++++++++++++++++++++- libraries/fbx/src/FBXReader.h | 6 +++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 370a7bb89f..29f044b1d4 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -202,6 +202,11 @@ public: glm::vec3 rotationMin; // radians glm::vec3 rotationMax; // radians + + bool hasGeometricOffset; + glm::vec3 geometricTranslation; + glm::quat geometricRotation; + glm::vec3 geometricScaling; }; glm::mat4 getGlobalTransform(const QMultiMap& _connectionParentMap, @@ -639,6 +644,13 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS glm::vec3 scalePivot, rotationPivot, scaleOffset; bool rotationMinX = false, rotationMinY = false, rotationMinZ = false; bool rotationMaxX = false, rotationMaxY = false, rotationMaxZ = false; + + // local offset transforms from 3ds max + bool hasGeometricOffset = false; + glm::vec3 geometricTranslation; + glm::vec3 geometricScaling(1.0f, 1.0f, 1.0f); + glm::vec3 geometricRotation; + glm::vec3 rotationMin, rotationMax; FBXModel model = { name, -1, glm::vec3(), glm::mat4(), glm::quat(), glm::quat(), glm::quat(), glm::mat4(), glm::vec3(), glm::vec3()}; @@ -712,6 +724,15 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } else if (property.properties.at(0) == "RotationMaxZ") { rotationMaxZ = property.properties.at(index).toBool(); + } else if (property.properties.at(0) == "GeometricTranslation") { + geometricTranslation = getVec3(property.properties, index); + hasGeometricOffset = true; + } else if (property.properties.at(0) == "GeometricRotation") { + geometricRotation = getVec3(property.properties, index); + hasGeometricOffset = true; + } else if (property.properties.at(0) == "GeometricScaling") { + geometricScaling = getVec3(property.properties, index); + hasGeometricOffset = true; } } } @@ -768,8 +789,13 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS rotationMinY ? rotationMin.y : -180.0f, rotationMinZ ? rotationMin.z : -180.0f)); model.rotationMax = glm::radians(glm::vec3(rotationMaxX ? rotationMax.x : 180.0f, rotationMaxY ? rotationMax.y : 180.0f, rotationMaxZ ? rotationMax.z : 180.0f)); - models.insert(getID(object.properties), model); + model.hasGeometricOffset = hasGeometricOffset; + model.geometricTranslation = geometricTranslation; + model.geometricRotation = glm::quat(glm::radians(geometricRotation)); + model.geometricScaling = geometricScaling; + + models.insert(getID(object.properties), model); } else if (object.name == "Texture") { TextureParam tex; foreach (const FBXNode& subobject, object.children) { @@ -1286,7 +1312,14 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS joint.postTransform = model.postTransform; joint.rotationMin = model.rotationMin; joint.rotationMax = model.rotationMax; + + joint.hasGeometricOffset = model.hasGeometricOffset; + joint.geometricTranslation = model.geometricTranslation; + joint.geometricRotation = model.geometricRotation; + joint.geometricScaling = model.geometricScaling; + glm::quat combinedRotation = joint.preRotation * joint.rotation * joint.postRotation; + if (joint.parentIndex == -1) { joint.transform = geometry.offset * glm::translate(joint.translation) * joint.preTransform * glm::mat4_cast(combinedRotation) * joint.postTransform; @@ -1602,6 +1635,13 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS points.push_back(extractTranslation(vertexTransform) * clusterScale); } + // Apply geometric offset, if present, by transforming the vertices directly + if (joint.hasGeometricOffset) { + glm::mat4 geometricOffset = createMatFromScaleQuatAndPos(joint.geometricScaling, joint.geometricRotation, joint.geometricTranslation); + for (int i = 0; i < extracted.mesh.vertices.size(); i++) { + extracted.mesh.vertices[i] = transformPoint(geometricOffset, extracted.mesh.vertices[i]); + } + } } extracted.mesh.isEye = (maxJointIndex == geometry.leftEyeJointIndex || maxJointIndex == geometry.rightEyeJointIndex); diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index d2d433d53e..cd1dbc5c4f 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -88,6 +88,12 @@ public: QString name; bool isSkeletonJoint; bool bindTransformFoundInCluster; + + // geometric offset is applied in local space but does NOT affect children. + bool hasGeometricOffset; + glm::vec3 geometricTranslation; + glm::quat geometricRotation; + glm::vec3 geometricScaling; }; From 68b103b46b72d0775e890cabea62358d39cfe0f4 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 1 Dec 2016 15:21:49 -0800 Subject: [PATCH 2/2] Fix for missing fields in static initializer. --- libraries/fbx/src/FBXReader.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 29f044b1d4..0171b95a3d 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -653,7 +653,8 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS glm::vec3 rotationMin, rotationMax; FBXModel model = { name, -1, glm::vec3(), glm::mat4(), glm::quat(), glm::quat(), glm::quat(), - glm::mat4(), glm::vec3(), glm::vec3()}; + glm::mat4(), glm::vec3(), glm::vec3(), + false, glm::vec3(), glm::quat(), glm::vec3(1.0f) }; ExtractedMesh* mesh = NULL; QVector blendshapes; foreach (const FBXNode& subobject, object.children) {