mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Merge pull request #15122 from raveenajain/gltf_skinning
Case 21502: skinned gltf
This commit is contained in:
commit
05cab9a3cc
2 changed files with 233 additions and 41 deletions
|
@ -711,19 +711,19 @@ glm::mat4 GLTFSerializer::getModelTransform(const GLTFNode& node) {
|
|||
node.matrix[12], node.matrix[13], node.matrix[14], node.matrix[15]);
|
||||
} else {
|
||||
|
||||
if (node.defined["rotation"] && node.rotation.size() == 4) {
|
||||
//quat(x,y,z,w) to quat(w,x,y,z)
|
||||
glm::quat rotquat = glm::quat(node.rotation[3], node.rotation[0], node.rotation[1], node.rotation[2]);
|
||||
tmat = glm::mat4_cast(rotquat) * tmat;
|
||||
}
|
||||
|
||||
if (node.defined["scale"] && node.scale.size() == 3) {
|
||||
glm::vec3 scale = glm::vec3(node.scale[0], node.scale[1], node.scale[2]);
|
||||
glm::mat4 s = glm::mat4(1.0);
|
||||
s = glm::scale(s, scale);
|
||||
tmat = s * tmat;
|
||||
}
|
||||
|
||||
|
||||
if (node.defined["rotation"] && node.rotation.size() == 4) {
|
||||
//quat(x,y,z,w) to quat(w,x,y,z)
|
||||
glm::quat rotquat = glm::quat(node.rotation[3], node.rotation[0], node.rotation[1], node.rotation[2]);
|
||||
tmat = glm::mat4_cast(rotquat) * tmat;
|
||||
}
|
||||
|
||||
if (node.defined["translation"] && node.translation.size() == 3) {
|
||||
glm::vec3 trans = glm::vec3(node.translation[0], node.translation[1], node.translation[2]);
|
||||
glm::mat4 t = glm::mat4(1.0);
|
||||
|
@ -734,15 +734,54 @@ glm::mat4 GLTFSerializer::getModelTransform(const GLTFNode& node) {
|
|||
return tmat;
|
||||
}
|
||||
|
||||
void GLTFSerializer::getSkinInverseBindMatrices(std::vector<std::vector<float>>& inverseBindMatrixValues) {
|
||||
for (auto &skin : _file.skins) {
|
||||
GLTFAccessor& indicesAccessor = _file.accessors[skin.inverseBindMatrices];
|
||||
GLTFBufferView& indicesBufferview = _file.bufferviews[indicesAccessor.bufferView];
|
||||
GLTFBuffer& indicesBuffer = _file.buffers[indicesBufferview.buffer];
|
||||
int accBoffset = indicesAccessor.defined["byteOffset"] ? indicesAccessor.byteOffset : 0;
|
||||
QVector<float> matrices;
|
||||
addArrayOfType(indicesBuffer.blob,
|
||||
indicesBufferview.byteOffset + accBoffset,
|
||||
indicesAccessor.count,
|
||||
matrices,
|
||||
indicesAccessor.type,
|
||||
indicesAccessor.componentType);
|
||||
inverseBindMatrixValues.push_back(matrices.toStdVector());
|
||||
}
|
||||
}
|
||||
|
||||
void GLTFSerializer::getNodeQueueByDepthFirstChildren(std::vector<int>& children, int stride, std::vector<int>& result) {
|
||||
int startingIndex = 0;
|
||||
int finalIndex = (int)children.size();
|
||||
if (stride == -1) {
|
||||
startingIndex = (int)children.size() - 1;
|
||||
finalIndex = -1;
|
||||
}
|
||||
for (int index = startingIndex; index != finalIndex; index += stride) {
|
||||
int c = children[index];
|
||||
result.push_back(c);
|
||||
std::vector<int> nested = _file.nodes[c].children.toStdVector();
|
||||
if (nested.size() != 0) {
|
||||
std::sort(nested.begin(), nested.end());
|
||||
for (int r : nested) {
|
||||
if (result.end() == std::find(result.begin(), result.end(), r)) {
|
||||
getNodeQueueByDepthFirstChildren(nested, stride, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
|
||||
int numNodes = _file.nodes.size();
|
||||
|
||||
//Build dependencies
|
||||
QVector<QVector<int>> nodeDependencies(_file.nodes.size());
|
||||
QVector<QVector<int>> nodeDependencies(numNodes);
|
||||
int nodecount = 0;
|
||||
bool hasChildren = false;
|
||||
foreach(auto &node, _file.nodes) {
|
||||
//nodes_transforms.push_back(getModelTransform(node));
|
||||
hasChildren |= !node.children.isEmpty();
|
||||
foreach(int child, node.children) nodeDependencies[child].push_back(nodecount);
|
||||
nodecount++;
|
||||
}
|
||||
|
@ -764,26 +803,98 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
|
|||
|
||||
nodecount++;
|
||||
}
|
||||
|
||||
HFMJoint joint;
|
||||
joint.isSkeletonJoint = true;
|
||||
joint.bindTransformFoundInCluster = false;
|
||||
joint.distanceToParent = 0;
|
||||
joint.parentIndex = -1;
|
||||
hfmModel.joints.resize(_file.nodes.size());
|
||||
hfmModel.jointIndices["x"] = _file.nodes.size();
|
||||
int jointInd = 0;
|
||||
for (auto& node : _file.nodes) {
|
||||
int size = node.transforms.size();
|
||||
if (hasChildren) { size--; }
|
||||
joint.preTransform = glm::mat4(1);
|
||||
for (int i = 0; i < size; i++) {
|
||||
joint.preTransform = node.transforms[i] * joint.preTransform;
|
||||
}
|
||||
joint.name = node.name;
|
||||
hfmModel.joints[jointInd] = joint;
|
||||
jointInd++;
|
||||
|
||||
|
||||
// initialize order in which nodes will be parsed
|
||||
std::vector<int> nodeQueue;
|
||||
nodeQueue.reserve(numNodes);
|
||||
int rootNode = 0;
|
||||
int finalNode = numNodes;
|
||||
if (!_file.scenes[_file.scene].nodes.contains(0)) {
|
||||
rootNode = numNodes - 1;
|
||||
finalNode = -1;
|
||||
}
|
||||
bool rootAtStartOfList = rootNode < finalNode;
|
||||
int nodeListStride = 1;
|
||||
if (!rootAtStartOfList) { nodeListStride = -1; }
|
||||
|
||||
QVector<int> initialSceneNodes = _file.scenes[_file.scene].nodes;
|
||||
std::sort(initialSceneNodes.begin(), initialSceneNodes.end());
|
||||
int sceneRootNode = 0;
|
||||
int sceneFinalNode = initialSceneNodes.size();
|
||||
if (!rootAtStartOfList) {
|
||||
sceneRootNode = initialSceneNodes.size() - 1;
|
||||
sceneFinalNode = -1;
|
||||
}
|
||||
for (int index = sceneRootNode; index != sceneFinalNode; index += nodeListStride) {
|
||||
int i = initialSceneNodes[index];
|
||||
nodeQueue.push_back(i);
|
||||
std::vector<int> children = _file.nodes[i].children.toStdVector();
|
||||
std::sort(children.begin(), children.end());
|
||||
getNodeQueueByDepthFirstChildren(children, nodeListStride, nodeQueue);
|
||||
}
|
||||
|
||||
// Build joints
|
||||
HFMJoint joint;
|
||||
joint.distanceToParent = 0;
|
||||
hfmModel.jointIndices["x"] = numNodes;
|
||||
hfmModel.hasSkeletonJoints = false;
|
||||
|
||||
for (int nodeIndex : nodeQueue) {
|
||||
auto& node = _file.nodes[nodeIndex];
|
||||
|
||||
joint.parentIndex = -1;
|
||||
if (!_file.scenes[_file.scene].nodes.contains(nodeIndex)) {
|
||||
joint.parentIndex = std::distance(nodeQueue.begin(), std::find(nodeQueue.begin(), nodeQueue.end(), nodeDependencies[nodeIndex][0]));
|
||||
}
|
||||
joint.transform = node.transforms.first();
|
||||
joint.translation = extractTranslation(joint.transform);
|
||||
joint.rotation = glmExtractRotation(joint.transform);
|
||||
glm::vec3 scale = extractScale(joint.transform);
|
||||
joint.postTransform = glm::scale(glm::mat4(), scale);
|
||||
|
||||
joint.name = node.name;
|
||||
joint.isSkeletonJoint = false;
|
||||
hfmModel.joints.push_back(joint);
|
||||
}
|
||||
|
||||
|
||||
// Build skeleton
|
||||
std::vector<glm::mat4> jointInverseBindTransforms;
|
||||
jointInverseBindTransforms.resize(numNodes);
|
||||
if (!_file.skins.isEmpty()) {
|
||||
std::vector<std::vector<float>> inverseBindValues;
|
||||
getSkinInverseBindMatrices(inverseBindValues);
|
||||
|
||||
int jointIndex = finalNode;
|
||||
while (jointIndex != rootNode) {
|
||||
rootAtStartOfList ? jointIndex-- : jointIndex++;
|
||||
int jOffset = nodeQueue[jointIndex];
|
||||
auto joint = hfmModel.joints[jointIndex];
|
||||
|
||||
hfmModel.hasSkeletonJoints = true;
|
||||
for (int s = 0; s < _file.skins.size(); s++) {
|
||||
auto skin = _file.skins[s];
|
||||
joint.isSkeletonJoint = skin.joints.contains(jOffset);
|
||||
|
||||
if (joint.isSkeletonJoint) {
|
||||
std::vector<float> value = inverseBindValues[s];
|
||||
int matrixCount = 16 * skin.joints.indexOf(jOffset);
|
||||
jointInverseBindTransforms[jointIndex] =
|
||||
glm::mat4(value[matrixCount], value[matrixCount + 1], value[matrixCount + 2], value[matrixCount + 3],
|
||||
value[matrixCount + 4], value[matrixCount + 5], value[matrixCount + 6], value[matrixCount + 7],
|
||||
value[matrixCount + 8], value[matrixCount + 9], value[matrixCount + 10], value[matrixCount + 11],
|
||||
value[matrixCount + 12], value[matrixCount + 13], value[matrixCount + 14], value[matrixCount + 15]);
|
||||
} else {
|
||||
jointInverseBindTransforms[jointIndex] = glm::mat4();
|
||||
}
|
||||
glm::vec3 bindTranslation = extractTranslation(hfmModel.offset * glm::inverse(jointInverseBindTransforms[jointIndex]));
|
||||
hfmModel.bindExtents.addPoint(bindTranslation);
|
||||
}
|
||||
hfmModel.joints[jointIndex] = joint;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Build materials
|
||||
QVector<QString> materialIDs;
|
||||
|
@ -803,23 +914,39 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
|
|||
}
|
||||
|
||||
|
||||
|
||||
nodecount = 0;
|
||||
// Build meshes
|
||||
foreach(auto &node, _file.nodes) {
|
||||
nodecount = 0;
|
||||
for (int nodeIndex = rootNode; nodeIndex != finalNode; nodeIndex += nodeListStride) {
|
||||
auto& node = _file.nodes[nodeIndex];
|
||||
|
||||
if (node.defined["mesh"]) {
|
||||
qCDebug(modelformat) << "node_transforms" << node.transforms;
|
||||
foreach(auto &primitive, _file.meshes[node.mesh].primitives) {
|
||||
hfmModel.meshes.append(HFMMesh());
|
||||
HFMMesh& mesh = hfmModel.meshes[hfmModel.meshes.size() - 1];
|
||||
HFMCluster cluster;
|
||||
cluster.jointIndex = nodecount;
|
||||
cluster.inverseBindMatrix = glm::mat4(1, 0, 0, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 1);
|
||||
mesh.clusters.append(cluster);
|
||||
if (!hfmModel.hasSkeletonJoints) {
|
||||
HFMCluster cluster;
|
||||
cluster.jointIndex = nodecount;
|
||||
cluster.inverseBindMatrix = glm::mat4();
|
||||
cluster.inverseBindTransform = Transform(cluster.inverseBindMatrix);
|
||||
mesh.clusters.append(cluster);
|
||||
} else {
|
||||
for (int j = rootNode; j != finalNode; j += nodeListStride) {
|
||||
HFMCluster cluster;
|
||||
cluster.jointIndex = j;
|
||||
cluster.inverseBindMatrix = jointInverseBindTransforms[j];
|
||||
cluster.inverseBindTransform = Transform(cluster.inverseBindMatrix);
|
||||
mesh.clusters.append(cluster);
|
||||
}
|
||||
}
|
||||
HFMCluster root;
|
||||
root.jointIndex = rootNode;
|
||||
if (root.jointIndex == -1) {
|
||||
root.jointIndex = 0;
|
||||
}
|
||||
root.inverseBindMatrix = jointInverseBindTransforms[root.jointIndex];
|
||||
root.inverseBindTransform = Transform(root.inverseBindMatrix);
|
||||
mesh.clusters.append(root);
|
||||
|
||||
HFMMeshPart part = HFMMeshPart();
|
||||
|
||||
|
@ -848,6 +975,8 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
|
|||
}
|
||||
|
||||
QList<QString> keys = primitive.attributes.values.keys();
|
||||
QVector<uint16_t> clusterJoints;
|
||||
QVector<float> clusterWeights;
|
||||
|
||||
foreach(auto &key, keys) {
|
||||
int accessorIdx = primitive.attributes.values[key];
|
||||
|
@ -949,8 +1078,69 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
|
|||
for (int n = 0; n < texcoords.size(); n = n + 2) {
|
||||
mesh.texCoords1.push_back(glm::vec2(texcoords[n], texcoords[n + 1]));
|
||||
}
|
||||
} else if (key == "JOINTS_0") {
|
||||
QVector<uint16_t> joints;
|
||||
success = addArrayOfType(buffer.blob,
|
||||
bufferview.byteOffset + accBoffset,
|
||||
accessor.count,
|
||||
joints,
|
||||
accessor.type,
|
||||
accessor.componentType);
|
||||
if (!success) {
|
||||
qWarning(modelformat) << "There was a problem reading glTF JOINTS_0 data for model " << _url;
|
||||
continue;
|
||||
}
|
||||
for (int n = 0; n < joints.size(); n++) {
|
||||
clusterJoints.push_back(joints[n]);
|
||||
}
|
||||
} else if (key == "WEIGHTS_0") {
|
||||
QVector<float> weights;
|
||||
success = addArrayOfType(buffer.blob,
|
||||
bufferview.byteOffset + accBoffset,
|
||||
accessor.count,
|
||||
weights,
|
||||
accessor.type,
|
||||
accessor.componentType);
|
||||
if (!success) {
|
||||
qWarning(modelformat) << "There was a problem reading glTF WEIGHTS_0 data for model " << _url;
|
||||
continue;
|
||||
}
|
||||
for (int n = 0; n < weights.size(); n++) {
|
||||
clusterWeights.push_back(weights[n]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// adapted from FBXSerializer.cpp
|
||||
if (hfmModel.hasSkeletonJoints) {
|
||||
int numClusterIndices = clusterJoints.size();
|
||||
const int WEIGHTS_PER_VERTEX = 4;
|
||||
const float ALMOST_HALF = 0.499f;
|
||||
int numVertices = mesh.vertices.size();
|
||||
mesh.clusterIndices.fill(mesh.clusters.size() - 1, numClusterIndices);
|
||||
mesh.clusterWeights.fill(0, numClusterIndices);
|
||||
|
||||
for (int c = 0; c < clusterJoints.size(); c++) {
|
||||
mesh.clusterIndices[c] = _file.skins[node.skin].joints[clusterJoints[c]];
|
||||
}
|
||||
|
||||
// normalize and compress to 16-bits
|
||||
for (int i = 0; i < numVertices; ++i) {
|
||||
int j = i * WEIGHTS_PER_VERTEX;
|
||||
|
||||
float totalWeight = 0.0f;
|
||||
for (int k = j; k < j + WEIGHTS_PER_VERTEX; ++k) {
|
||||
totalWeight += clusterWeights[k];
|
||||
}
|
||||
if (totalWeight > 0.0f) {
|
||||
float weightScalingFactor = (float)(UINT16_MAX) / totalWeight;
|
||||
for (int k = j; k < j + WEIGHTS_PER_VERTEX; ++k) {
|
||||
mesh.clusterWeights[k] = (uint16_t)(weightScalingFactor * clusterWeights[k] + ALMOST_HALF);
|
||||
}
|
||||
} else {
|
||||
mesh.clusterWeights[j] = (uint16_t)((float)(UINT16_MAX) + ALMOST_HALF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (primitive.defined["material"]) {
|
||||
|
@ -959,8 +1149,8 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
|
|||
mesh.parts.push_back(part);
|
||||
|
||||
// populate the texture coordinates if they don't exist
|
||||
if (mesh.texCoords.size() == 0) {
|
||||
for (int i = 0; i < part.triangleIndices.size(); i++) mesh.texCoords.push_back(glm::vec2(0.0, 1.0));
|
||||
if (mesh.texCoords.size() == 0 && !hfmModel.hasSkeletonJoints) {
|
||||
for (int i = 0; i < part.triangleIndices.size(); i++) { mesh.texCoords.push_back(glm::vec2(0.0, 1.0)); }
|
||||
}
|
||||
mesh.meshExtents.reset();
|
||||
foreach(const glm::vec3& vertex, mesh.vertices) {
|
||||
|
|
|
@ -712,6 +712,8 @@ private:
|
|||
hifi::ByteArray _glbBinary;
|
||||
|
||||
glm::mat4 getModelTransform(const GLTFNode& node);
|
||||
void getSkinInverseBindMatrices(std::vector<std::vector<float>>& inverseBindMatrixValues);
|
||||
void getNodeQueueByDepthFirstChildren(std::vector<int>& children, int stride, std::vector<int>& result);
|
||||
|
||||
bool buildGeometry(HFMModel& hfmModel, const hifi::URL& url);
|
||||
bool parseGLTF(const hifi::ByteArray& data);
|
||||
|
|
Loading…
Reference in a new issue