From cec0ee0b22b9508610938cd64feb95339dffc86a Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 11 Oct 2013 17:51:29 -0700 Subject: [PATCH 01/13] Working on processing the cluster/skin information. --- interface/src/avatar/BlendFace.cpp | 5 ++ interface/src/avatar/BlendFace.h | 7 ++ interface/src/renderer/FBXReader.cpp | 127 +++++++++++++++++++++------ interface/src/renderer/FBXReader.h | 24 +++++ 4 files changed, 138 insertions(+), 25 deletions(-) diff --git a/interface/src/avatar/BlendFace.cpp b/interface/src/avatar/BlendFace.cpp index ce6422dcb4..cd2d3aa59f 100644 --- a/interface/src/avatar/BlendFace.cpp +++ b/interface/src/avatar/BlendFace.cpp @@ -59,6 +59,10 @@ void BlendFace::simulate(float deltaTime) { const FBXGeometry& geometry = _geometry->getFBXGeometry(); if (_meshStates.isEmpty()) { QVector vertices; + foreach (const FBXJoint& joint, geometry.joints) { + JointState state; + _jointStates.append(state); + } foreach (const FBXMesh& mesh, geometry.meshes) { MeshState state; if (mesh.springiness > 0.0f) { @@ -365,5 +369,6 @@ void BlendFace::deleteGeometry() { glDeleteBuffers(1, &id); } _blendedVertexBufferIDs.clear(); + _jointStates.clear(); _meshStates.clear(); } diff --git a/interface/src/avatar/BlendFace.h b/interface/src/avatar/BlendFace.h index 2a954cc249..62b04ca19e 100644 --- a/interface/src/avatar/BlendFace.h +++ b/interface/src/avatar/BlendFace.h @@ -58,6 +58,13 @@ private: QSharedPointer _geometry; + class JointState { + public: + glm::quat rotation; + }; + + QVector _jointStates; + class MeshState { public: QVector worldSpaceVertices; diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index 8091687488..c55872dd1a 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -298,7 +298,7 @@ const char* FACESHIFT_BLENDSHAPES[] = { "" }; -class Transform { +class Model { public: QByteArray name; bool inheritScale; @@ -306,20 +306,20 @@ public: glm::mat4 withoutScale; }; -glm::mat4 getGlobalTransform(const QMultiHash& parentMap, const QHash& localTransforms, +glm::mat4 getGlobalTransform(const QMultiHash& parentMap, const QHash& models, qint64 nodeID, bool forceScale = true) { glm::mat4 globalTransform; bool useScale = true; while (nodeID != 0) { - const Transform& localTransform = localTransforms.value(nodeID); - globalTransform = (useScale ? localTransform.withScale : localTransform.withoutScale) * globalTransform; - useScale = (useScale && localTransform.inheritScale) || forceScale; + const Model& model = models.value(nodeID); + globalTransform = (useScale ? model.withScale : model.withoutScale) * globalTransform; + useScale = (useScale && model.inheritScale) || forceScale; QList parentIDs = parentMap.values(nodeID); nodeID = 0; foreach (qint64 parentID, parentIDs) { - if (localTransforms.contains(parentID)) { + if (models.contains(parentID)) { nodeID = parentID; break; } @@ -354,13 +354,31 @@ public: float shininess; }; +class Cluster { +public: + QVector indices; + QVector weights; + glm::mat4 transformLink; +}; + +void appendModelIDs(qint64 parentID, const QMultiHash& childMap, QHash& models, QVector& modelIDs) { + if (parentID != 0) { + modelIDs.append(parentID); + } + foreach (qint64 childID, childMap.values(parentID)) { + if (models.contains(childID)) { + appendModelIDs(childID, childMap, models, modelIDs); + } + } +} + FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) { QHash meshes; QVector blendshapes; QMultiHash parentMap; QMultiHash childMap; - QHash localTransforms; - QHash transformLinkMatrices; + QHash models; + QHash clusters; QHash textureFilenames; QHash materials; QHash diffuseTextures; @@ -536,7 +554,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) glm::vec3 preRotation, rotation, postRotation; glm::vec3 scale = glm::vec3(1.0f, 1.0f, 1.0f); glm::vec3 scalePivot, rotationPivot; - Transform transform = { name, true }; + Model model = { name, true }; foreach (const FBXNode& subobject, object.children) { if (subobject.name == "Properties70") { foreach (const FBXNode& property, subobject.children) { @@ -577,20 +595,20 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) property.properties.at(6).value()); } else if (property.properties.at(0) == "InheritType") { - transform.inheritScale = property.properties.at(4) != 2; + model.inheritScale = property.properties.at(4) != 2; } } } } } // see FBX documentation, http://download.autodesk.com/us/fbx/20112/FBX_SDK_HELP/index.html - transform.withoutScale = glm::translate(translation) * glm::translate(rotationPivot) * + model.withoutScale = glm::translate(translation) * glm::translate(rotationPivot) * glm::mat4_cast(glm::quat(glm::radians(preRotation))) * glm::mat4_cast(glm::quat(glm::radians(rotation))) * glm::mat4_cast(glm::quat(glm::radians(postRotation))) * glm::translate(-rotationPivot); - transform.withScale = transform.withoutScale * glm::translate(scalePivot) * glm::scale(scale) * + model.withScale = model.withoutScale * glm::translate(scalePivot) * glm::scale(scale) * glm::translate(-scalePivot); - localTransforms.insert(object.properties.at(0).value(), transform); + models.insert(object.properties.at(0).value(), model); } else if (object.name == "Texture") { foreach (const FBXNode& subobject, object.children) { @@ -628,12 +646,21 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) } else if (object.name == "Deformer") { if (object.properties.at(2) == "Cluster") { + Cluster cluster; foreach (const FBXNode& subobject, object.children) { - if (subobject.name == "TransformLink") { + if (subobject.name == "Indexes") { + cluster.indices = subobject.properties.at(0).value >(); + + } else if (subobject.name == "Weights") { + cluster.weights = subobject.properties.at(0).value >(); + + } else if (subobject.name == "TransformLink") { QVector values = subobject.properties.at(0).value >(); - transformLinkMatrices.insert(object.properties.at(0).value(), createMat4(values)); + cluster.transformLink = createMat4(values); } } + clusters.insert(object.properties.at(0).value(), cluster); + } else if (object.properties.at(2) == "BlendShapeChannel") { QByteArray name = object.properties.at(1).toByteArray(); name = name.left(name.indexOf('\0')); @@ -678,13 +705,24 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) } // get offset transform from mapping + FBXGeometry geometry; float offsetScale = mapping.value("scale", 1.0f).toFloat(); - glm::mat4 offset = glm::translate(mapping.value("tx").toFloat(), mapping.value("ty").toFloat(), + geometry.offset = glm::translate(mapping.value("tx").toFloat(), mapping.value("ty").toFloat(), mapping.value("tz").toFloat()) * glm::mat4_cast(glm::quat(glm::radians(glm::vec3(mapping.value("rx").toFloat(), mapping.value("ry").toFloat(), mapping.value("rz").toFloat())))) * glm::scale(offsetScale, offsetScale, offsetScale); - FBXGeometry geometry; + // get the list of models in depth-first traversal order + QVector modelIDs; + appendModelIDs(0, childMap, models, modelIDs); + + // convert the models to joints + foreach (qint64 modelID, modelIDs) { + FBXJoint joint; + joint.parentIndex = modelIDs.indexOf(parentMap.value(modelID)); + geometry.joints.append(joint); + } + QVariantHash springs = mapping.value("spring").toHash(); QVariant defaultSpring = springs.value("default"); for (QHash::iterator it = meshes.begin(); it != meshes.end(); it++) { @@ -692,8 +730,8 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) // accumulate local transforms qint64 modelID = parentMap.value(it.key()); - mesh.springiness = springs.value(localTransforms.value(modelID).name, defaultSpring).toFloat(); - glm::mat4 modelTransform = getGlobalTransform(parentMap, localTransforms, modelID); + mesh.springiness = springs.value(models.value(modelID).name, defaultSpring).toFloat(); + glm::mat4 modelTransform = getGlobalTransform(parentMap, models, modelID); // look for textures, material properties foreach (qint64 childID, childMap.values(modelID)) { @@ -714,29 +752,68 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) } } - // look for a limb pivot + // find the clusters with which the mesh is associated mesh.isEye = false; + QVector clusterIDs; foreach (qint64 childID, childMap.values(it.key())) { foreach (qint64 clusterID, childMap.values(childID)) { - if (!transformLinkMatrices.contains(clusterID)) { + if (!clusters.contains(clusterID)) { continue; } + FBXCluster fbxCluster; + const Cluster& cluster = clusters[clusterID]; + clusterIDs.append(clusterID); + qint64 jointID = childMap.value(clusterID); if (jointID == jointEyeLeftID || jointID == jointEyeRightID) { mesh.isEye = true; } + fbxCluster.jointIndex = modelIDs.indexOf(jointID); + fbxCluster.inverseBindMatrix = glm::inverse(cluster.transformLink) * modelTransform; + mesh.clusters.append(fbxCluster); // see http://stackoverflow.com/questions/13566608/loading-skinning-information-from-fbx for a discussion // of skinning information in FBX - glm::mat4 jointTransform = offset * getGlobalTransform(parentMap, localTransforms, jointID); - mesh.transform = jointTransform * glm::inverse(transformLinkMatrices.value(clusterID)) * modelTransform; + glm::mat4 jointTransform = geometry.offset * getGlobalTransform(parentMap, models, jointID); + mesh.transform = jointTransform * glm::inverse(cluster.transformLink) * modelTransform; // extract translation component for pivot - glm::mat4 jointTransformScaled = offset * getGlobalTransform(parentMap, localTransforms, jointID, true); + glm::mat4 jointTransformScaled = geometry.offset * getGlobalTransform(parentMap, models, jointID, true); mesh.pivot = glm::vec3(jointTransformScaled[3][0], jointTransformScaled[3][1], jointTransformScaled[3][2]); } } + // if we don't have a skinned joint, parent to the model itself + if (mesh.clusters.isEmpty()) { + FBXCluster cluster; + cluster.jointIndex = modelIDs.indexOf(modelID); + mesh.clusters.append(cluster); + } + + // whether we're skinned depends on how many clusters are attached + if (clusterIDs.size() > 1) { + mesh.clusterIndices.resize(mesh.vertices.size()); + mesh.clusterWeights.resize(mesh.vertices.size()); + for (int i = 0; i < clusterIDs.size(); i++) { + qint64 clusterID = clusterIDs.at(i); + const Cluster& cluster = clusters[clusterID]; + qint64 jointID = childMap.value(clusterID); + for (int j = 0; j < cluster.indices.size(); j++) { + int index = cluster.indices.at(j); + glm::vec4& weights = mesh.clusterWeights[index]; + + // look for an unused slot in the weights vector + for (int k = 0; k < 4; k++) { + if (weights[k] == 0.0f) { + mesh.clusterIndices[index][k] = i; + weights[k] = cluster.weights.at(j); + break; + } + } + } + } + } + // extract spring edges, connections if springy if (mesh.springiness > 0.0f) { QSet > edges; @@ -781,7 +858,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) } // extract translation component for neck pivot - glm::mat4 neckTransform = offset * getGlobalTransform(parentMap, localTransforms, jointNeckID, true); + glm::mat4 neckTransform = geometry.offset * getGlobalTransform(parentMap, models, jointNeckID, true); geometry.neckPivot = glm::vec3(neckTransform[3][0], neckTransform[3][1], neckTransform[3][2]); return geometry; diff --git a/interface/src/renderer/FBXReader.h b/interface/src/renderer/FBXReader.h index 5c2ffd9ee0..3d8c3d0fa2 100644 --- a/interface/src/renderer/FBXReader.h +++ b/interface/src/renderer/FBXReader.h @@ -37,6 +37,22 @@ public: QVector normals; }; +/// A single joint (transformation node) extracted from an FBX document. +class FBXJoint { +public: + + int parentIndex; + glm::quat rotation; +}; + +/// A single binding to a joint in an FBX document. +class FBXCluster { +public: + + int jointIndex; + glm::mat4 inverseBindMatrix; +}; + /// A single mesh (with optional blendshapes) extracted from an FBX document. class FBXMesh { public: @@ -46,6 +62,10 @@ public: QVector vertices; QVector normals; QVector texCoords; + QVector clusterIndices; + QVector clusterWeights; + + QVector clusters; glm::vec3 pivot; glm::mat4 transform; @@ -70,8 +90,12 @@ public: class FBXGeometry { public: + QVector joints; + QVector meshes; + glm::mat4 offset; + glm::vec3 neckPivot; }; From db30e729d4e15ae199c45b5163851b260c224cd5 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 14 Oct 2013 16:46:17 -0700 Subject: [PATCH 02/13] More work towards skinning. --- interface/src/avatar/BlendFace.cpp | 124 +++++++++++++---------- interface/src/avatar/BlendFace.h | 2 + interface/src/renderer/FBXReader.cpp | 58 ++++++----- interface/src/renderer/FBXReader.h | 6 ++ interface/src/renderer/GeometryCache.cpp | 25 ++++- 5 files changed, 134 insertions(+), 81 deletions(-) diff --git a/interface/src/avatar/BlendFace.cpp b/interface/src/avatar/BlendFace.cpp index cd2d3aa59f..1380703fe6 100644 --- a/interface/src/avatar/BlendFace.cpp +++ b/interface/src/avatar/BlendFace.cpp @@ -61,10 +61,12 @@ void BlendFace::simulate(float deltaTime) { QVector vertices; foreach (const FBXJoint& joint, geometry.joints) { JointState state; + state.rotation = joint.rotation; _jointStates.append(state); } foreach (const FBXMesh& mesh, geometry.meshes) { MeshState state; + state.jointMatrices.resize(mesh.clusters.size()); if (mesh.springiness > 0.0f) { state.worldSpaceVertices.resize(mesh.vertices.size()); state.vertexVelocities.resize(mesh.vertices.size()); @@ -75,14 +77,44 @@ void BlendFace::simulate(float deltaTime) { _resetStates = true; } - glm::quat orientation = _owningHead->getOrientation(); + glm::quat orientation = static_cast(_owningHead->_owningAvatar)->getOrientation(); glm::vec3 scale = glm::vec3(-1.0f, 1.0f, -1.0f) * _owningHead->getScale() * MODEL_SCALE; glm::vec3 offset = MODEL_TRANSLATION - _geometry->getFBXGeometry().neckPivot; glm::mat4 baseTransform = glm::translate(_owningHead->getPosition()) * glm::mat4_cast(orientation) * glm::scale(scale) * glm::translate(offset); + + // apply the neck rotation + if (geometry.neckJointIndex != -1) { + _jointStates[geometry.neckJointIndex].rotation = glm::quat(glm::radians(glm::vec3( + _owningHead->getPitch(), _owningHead->getYaw(), _owningHead->getRoll()))); + } + + // update the world space transforms for all joints + for (int i = 0; i < _jointStates.size(); i++) { + JointState& state = _jointStates[i]; + const FBXJoint& joint = geometry.joints.at(i); + if (joint.parentIndex == -1) { + state.transform = baseTransform * geometry.offset * joint.preRotation * + glm::mat4_cast(state.rotation) * joint.postRotation; + } else { + state.transform = _jointStates[joint.parentIndex].transform * joint.preRotation; + if (i == geometry.leftEyeJointIndex || i == geometry.rightEyeJointIndex) { + // extract the translation component of the matrix + state.rotation = _owningHead->getEyeRotation(glm::vec3( + state.transform[3][0], state.transform[3][1], state.transform[3][2])); + } + state.transform = state.transform * glm::mat4_cast(state.rotation) * joint.postRotation; + } + } + for (int i = 0; i < _meshStates.size(); i++) { MeshState& state = _meshStates[i]; + const FBXMesh& mesh = geometry.meshes.at(i); + for (int j = 0; j < mesh.clusters.size(); j++) { + const FBXCluster& cluster = mesh.clusters.at(j); + state.jointMatrices[j] = _jointStates[cluster.jointIndex].transform * cluster.inverseBindMatrix; + } int vertexCount = state.worldSpaceVertices.size(); if (vertexCount == 0) { continue; @@ -90,7 +122,7 @@ void BlendFace::simulate(float deltaTime) { glm::vec3* destVertices = state.worldSpaceVertices.data(); glm::vec3* destVelocities = state.vertexVelocities.data(); glm::vec3* destNormals = state.worldSpaceNormals.data(); - const FBXMesh& mesh = geometry.meshes.at(i); + const glm::vec3* sourceVertices = mesh.vertices.constData(); if (!mesh.blendshapes.isEmpty()) { _blendedVertices.resize(max(_blendedVertices.size(), vertexCount)); @@ -112,11 +144,29 @@ void BlendFace::simulate(float deltaTime) { sourceVertices = _blendedVertices.constData(); } - glm::mat4 transform = baseTransform; - if (mesh.isEye) { - transform = transform * glm::translate(mesh.pivot) * glm::mat4_cast(glm::inverse(orientation) * - _owningHead->getEyeRotation(orientation * ((mesh.pivot + offset) * scale) + _owningHead->getPosition())) * - glm::translate(-mesh.pivot); + glm::mat4 transform; + if (mesh.clusters.size() > 1) { + _blendedVertices.resize(max(_blendedVertices.size(), vertexCount)); + + // skin each vertex + const glm::vec4* clusterIndices = mesh.clusterIndices.constData(); + const glm::vec4* clusterWeights = mesh.clusterWeights.constData(); + for (int j = 0; j < vertexCount; j++) { + _blendedVertices[j] = + glm::vec3(state.jointMatrices[clusterIndices[j][0]] * + glm::vec4(sourceVertices[j], 1.0f)) * clusterWeights[j][0] + + glm::vec3(state.jointMatrices[clusterIndices[j][1]] * + glm::vec4(sourceVertices[j], 1.0f)) * clusterWeights[j][1] + + glm::vec3(state.jointMatrices[clusterIndices[j][2]] * + glm::vec4(sourceVertices[j], 1.0f)) * clusterWeights[j][2] + + glm::vec3(state.jointMatrices[clusterIndices[j][3]] * + glm::vec4(sourceVertices[j], 1.0f)) * clusterWeights[j][3]; + } + + sourceVertices = _blendedVertices.constData(); + + } else { + transform = state.jointMatrices[0]; } if (_resetStates) { for (int j = 0; j < vertexCount; j++) { @@ -171,21 +221,11 @@ bool BlendFace::render(float alpha) { _dilatedTextures.resize(geometry.meshes.size()); } - glm::mat4 viewMatrix; - glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat*)&viewMatrix); - - glPushMatrix(); - glTranslatef(_owningHead->getPosition().x, _owningHead->getPosition().y, _owningHead->getPosition().z); glm::quat orientation = _owningHead->getOrientation(); - glm::vec3 axis = glm::axis(orientation); - glRotatef(glm::angle(orientation), axis.x, axis.y, axis.z); glm::vec3 scale(-_owningHead->getScale() * MODEL_SCALE, _owningHead->getScale() * MODEL_SCALE, -_owningHead->getScale() * MODEL_SCALE); - glScalef(scale.x, scale.y, scale.z); - glm::vec3 offset = MODEL_TRANSLATION - geometry.neckPivot; - glTranslatef(offset.x, offset.y, offset.z); - + glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); @@ -210,13 +250,6 @@ bool BlendFace::render(float alpha) { // apply eye rotation if appropriate Texture* texture = networkMesh.diffuseTexture.data(); if (mesh.isEye) { - glTranslatef(mesh.pivot.x, mesh.pivot.y, mesh.pivot.z); - glm::quat rotation = glm::inverse(orientation) * _owningHead->getEyeRotation(orientation * - ((mesh.pivot + offset) * scale) + _owningHead->getPosition()); - glm::vec3 rotationAxis = glm::axis(rotation); - glRotatef(glm::angle(rotation), -rotationAxis.x, rotationAxis.y, -rotationAxis.z); - glTranslatef(-mesh.pivot.x, -mesh.pivot.y, -mesh.pivot.z); - _eyeProgram.bind(); if (texture != NULL) { @@ -233,7 +266,10 @@ bool BlendFace::render(float alpha) { glMaterialfv(GL_FRONT, GL_SPECULAR, (const float*)&specular); glMaterialf(GL_FRONT, GL_SHININESS, mesh.shininess); - glMultMatrixf((const GLfloat*)&mesh.transform); + const MeshState& state = _meshStates.at(i); + if (state.worldSpaceVertices.isEmpty()) { + glMultMatrixf((const GLfloat*)&state.jointMatrices[0]); + } glBindTexture(GL_TEXTURE_2D, texture == NULL ? 0 : texture->getID()); @@ -245,10 +281,7 @@ bool BlendFace::render(float alpha) { glTexCoordPointer(2, GL_FLOAT, 0, 0); glBindBuffer(GL_ARRAY_BUFFER, _blendedVertexBufferIDs.at(i)); - const MeshState& state = _meshStates.at(i); if (!state.worldSpaceVertices.isEmpty()) { - glLoadMatrixf((const GLfloat*)&viewMatrix); - glBufferSubData(GL_ARRAY_BUFFER, 0, vertexCount * sizeof(glm::vec3), state.worldSpaceVertices.constData()); glBufferSubData(GL_ARRAY_BUFFER, vertexCount * sizeof(glm::vec3), vertexCount * sizeof(glm::vec3), state.worldSpaceNormals.constData()); @@ -309,8 +342,6 @@ bool BlendFace::render(float alpha) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glBindTexture(GL_TEXTURE_2D, 0); - glPopMatrix(); - // restore all the default material settings Application::getInstance()->setupWorldLight(*Application::getInstance()->getCamera()); @@ -318,32 +349,19 @@ bool BlendFace::render(float alpha) { } bool BlendFace::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition, bool upright) const { - if (!isActive()) { + if (!isActive() || _jointStates.isEmpty()) { return false; } - glm::vec3 translation = _owningHead->getPosition(); - glm::quat orientation = _owningHead->getOrientation(); - if (upright) { - translation = static_cast(_owningHead->_owningAvatar)->getUprightHeadPosition(); - orientation = static_cast(_owningHead->_owningAvatar)->getWorldAlignedOrientation(); - } - glm::vec3 scale(-_owningHead->getScale() * MODEL_SCALE, _owningHead->getScale() * MODEL_SCALE, - -_owningHead->getScale() * MODEL_SCALE); - bool foundFirst = false; - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - foreach (const FBXMesh& mesh, geometry.meshes) { - if (mesh.isEye) { - glm::vec3 position = orientation * ((mesh.pivot + MODEL_TRANSLATION - geometry.neckPivot) * scale) + translation; - if (foundFirst) { - secondEyePosition = position; - return true; - } - firstEyePosition = position; - foundFirst = true; - } + if (geometry.leftEyeJointIndex != -1) { + const glm::mat4& transform = _jointStates[geometry.leftEyeJointIndex].transform; + firstEyePosition = glm::vec3(transform[3][0], transform[3][1], transform[3][2]); } - return false; + if (geometry.rightEyeJointIndex != -1) { + const glm::mat4& transform = _jointStates[geometry.rightEyeJointIndex].transform; + secondEyePosition = glm::vec3(transform[3][0], transform[3][1], transform[3][2]); + } + return geometry.leftEyeJointIndex != -1 && geometry.rightEyeJointIndex != -1; } glm::vec4 BlendFace::computeAverageColor() const { diff --git a/interface/src/avatar/BlendFace.h b/interface/src/avatar/BlendFace.h index 62b04ca19e..df944558b1 100644 --- a/interface/src/avatar/BlendFace.h +++ b/interface/src/avatar/BlendFace.h @@ -61,12 +61,14 @@ private: class JointState { public: glm::quat rotation; + glm::mat4 transform; }; QVector _jointStates; class MeshState { public: + QVector jointMatrices; QVector worldSpaceVertices; QVector vertexVelocities; QVector worldSpaceNormals; diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index c55872dd1a..c20a965a24 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -301,20 +301,19 @@ const char* FACESHIFT_BLENDSHAPES[] = { class Model { public: QByteArray name; - bool inheritScale; - glm::mat4 withScale; - glm::mat4 withoutScale; + + glm::mat4 preRotation; + glm::quat rotation; + glm::mat4 postRotation; + + int parentIndex; }; -glm::mat4 getGlobalTransform(const QMultiHash& parentMap, const QHash& models, - qint64 nodeID, bool forceScale = true) { - +glm::mat4 getGlobalTransform(const QMultiHash& parentMap, const QHash& models, qint64 nodeID) { glm::mat4 globalTransform; - bool useScale = true; while (nodeID != 0) { const Model& model = models.value(nodeID); - globalTransform = (useScale ? model.withScale : model.withoutScale) * globalTransform; - useScale = (useScale && model.inheritScale) || forceScale; + globalTransform = model.preRotation * glm::mat4_cast(model.rotation) * model.postRotation * globalTransform; QList parentIDs = parentMap.values(nodeID); nodeID = 0; @@ -361,12 +360,15 @@ public: glm::mat4 transformLink; }; -void appendModelIDs(qint64 parentID, const QMultiHash& childMap, QHash& models, QVector& modelIDs) { +void appendModelIDs(qint64 parentID, const QMultiHash& childMap, + QHash& models, QVector& modelIDs) { if (parentID != 0) { modelIDs.append(parentID); } + int parentIndex = modelIDs.size() - 1; foreach (qint64 childID, childMap.values(parentID)) { if (models.contains(childID)) { + models[childID].parentIndex = parentIndex; appendModelIDs(childID, childMap, models, modelIDs); } } @@ -554,7 +556,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) glm::vec3 preRotation, rotation, postRotation; glm::vec3 scale = glm::vec3(1.0f, 1.0f, 1.0f); glm::vec3 scalePivot, rotationPivot; - Model model = { name, true }; + Model model = { name }; foreach (const FBXNode& subobject, object.children) { if (subobject.name == "Properties70") { foreach (const FBXNode& property, subobject.children) { @@ -592,22 +594,18 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) } else if (property.properties.at(0) == "Lcl Scaling") { scale = glm::vec3(property.properties.at(4).value(), property.properties.at(5).value(), - property.properties.at(6).value()); - - } else if (property.properties.at(0) == "InheritType") { - model.inheritScale = property.properties.at(4) != 2; + property.properties.at(6).value()); } } } } } // see FBX documentation, http://download.autodesk.com/us/fbx/20112/FBX_SDK_HELP/index.html - model.withoutScale = glm::translate(translation) * glm::translate(rotationPivot) * - glm::mat4_cast(glm::quat(glm::radians(preRotation))) * - glm::mat4_cast(glm::quat(glm::radians(rotation))) * - glm::mat4_cast(glm::quat(glm::radians(postRotation))) * glm::translate(-rotationPivot); - model.withScale = model.withoutScale * glm::translate(scalePivot) * glm::scale(scale) * - glm::translate(-scalePivot); + model.preRotation = glm::translate(translation) * glm::translate(rotationPivot) * + glm::mat4_cast(glm::quat(glm::radians(preRotation))); + model.rotation = glm::quat(glm::radians(rotation)); + model.postRotation = glm::mat4_cast(glm::quat(glm::radians(postRotation))) * glm::translate(-rotationPivot) * + glm::translate(scalePivot) * glm::scale(scale) * glm::translate(-scalePivot); models.insert(object.properties.at(0).value(), model); } else if (object.name == "Texture") { @@ -718,11 +716,20 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) // convert the models to joints foreach (qint64 modelID, modelIDs) { + const Model& model = models[modelID]; FBXJoint joint; - joint.parentIndex = modelIDs.indexOf(parentMap.value(modelID)); + joint.parentIndex = model.parentIndex; + joint.preRotation = model.preRotation; + joint.rotation = model.rotation; + joint.postRotation = model.postRotation; geometry.joints.append(joint); } + // find our special joints + geometry.leftEyeJointIndex = modelIDs.indexOf(jointEyeLeftID); + geometry.rightEyeJointIndex = modelIDs.indexOf(jointEyeRightID); + geometry.neckJointIndex = modelIDs.indexOf(jointNeckID); + QVariantHash springs = mapping.value("spring").toHash(); QVariant defaultSpring = springs.value("default"); for (QHash::iterator it = meshes.begin(); it != meshes.end(); it++) { @@ -754,7 +761,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) // find the clusters with which the mesh is associated mesh.isEye = false; - QVector clusterIDs; + QVector clusterIDs; foreach (qint64 childID, childMap.values(it.key())) { foreach (qint64 clusterID, childMap.values(childID)) { if (!clusters.contains(clusterID)) { @@ -778,7 +785,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) mesh.transform = jointTransform * glm::inverse(cluster.transformLink) * modelTransform; // extract translation component for pivot - glm::mat4 jointTransformScaled = geometry.offset * getGlobalTransform(parentMap, models, jointID, true); + glm::mat4 jointTransformScaled = geometry.offset * getGlobalTransform(parentMap, models, jointID); mesh.pivot = glm::vec3(jointTransformScaled[3][0], jointTransformScaled[3][1], jointTransformScaled[3][2]); } } @@ -798,6 +805,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) qint64 clusterID = clusterIDs.at(i); const Cluster& cluster = clusters[clusterID]; qint64 jointID = childMap.value(clusterID); + for (int j = 0; j < cluster.indices.size(); j++) { int index = cluster.indices.at(j); glm::vec4& weights = mesh.clusterWeights[index]; @@ -858,7 +866,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) } // extract translation component for neck pivot - glm::mat4 neckTransform = geometry.offset * getGlobalTransform(parentMap, models, jointNeckID, true); + glm::mat4 neckTransform = geometry.offset * getGlobalTransform(parentMap, models, jointNeckID); geometry.neckPivot = glm::vec3(neckTransform[3][0], neckTransform[3][1], neckTransform[3][2]); return geometry; diff --git a/interface/src/renderer/FBXReader.h b/interface/src/renderer/FBXReader.h index 3d8c3d0fa2..72033813fa 100644 --- a/interface/src/renderer/FBXReader.h +++ b/interface/src/renderer/FBXReader.h @@ -42,7 +42,9 @@ class FBXJoint { public: int parentIndex; + glm::mat4 preRotation; glm::quat rotation; + glm::mat4 postRotation; }; /// A single binding to a joint in an FBX document. @@ -96,6 +98,10 @@ public: glm::mat4 offset; + int leftEyeJointIndex; + int rightEyeJointIndex; + int neckJointIndex; + glm::vec3 neckPivot; }; diff --git a/interface/src/renderer/GeometryCache.cpp b/interface/src/renderer/GeometryCache.cpp index 926f226d6c..408d159198 100644 --- a/interface/src/renderer/GeometryCache.cpp +++ b/interface/src/renderer/GeometryCache.cpp @@ -363,18 +363,37 @@ void NetworkGeometry::maybeReadModelWithMapping() { glGenBuffers(1, &networkMesh.vertexBufferID); glBindBuffer(GL_ARRAY_BUFFER, networkMesh.vertexBufferID); + // if we don't need to do any blending or springing, then the positions/normals can be static if (mesh.blendshapes.isEmpty() && mesh.springiness == 0.0f) { glBufferData(GL_ARRAY_BUFFER, (mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3) + - mesh.texCoords.size() * sizeof(glm::vec2), NULL, GL_STATIC_DRAW); + mesh.texCoords.size() * sizeof(glm::vec2) + (mesh.clusterIndices.size() + + mesh.clusterWeights.size()) * sizeof(glm::vec4), NULL, GL_STATIC_DRAW); glBufferSubData(GL_ARRAY_BUFFER, 0, mesh.vertices.size() * sizeof(glm::vec3), mesh.vertices.constData()); glBufferSubData(GL_ARRAY_BUFFER, mesh.vertices.size() * sizeof(glm::vec3), mesh.normals.size() * sizeof(glm::vec3), mesh.normals.constData()); glBufferSubData(GL_ARRAY_BUFFER, (mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3), mesh.texCoords.size() * sizeof(glm::vec2), mesh.texCoords.constData()); + glBufferSubData(GL_ARRAY_BUFFER, (mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3) + + mesh.texCoords.size() * sizeof(glm::vec2), mesh.clusterIndices.size() * sizeof(glm::vec4), + mesh.clusterIndices.constData()); + glBufferSubData(GL_ARRAY_BUFFER, (mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3) + + mesh.texCoords.size() * sizeof(glm::vec2) + mesh.clusterIndices.size() * sizeof(glm::vec4), + mesh.clusterWeights.size() * sizeof(glm::vec4), mesh.clusterWeights.constData()); + + // if there's no springiness, then the cluster indices/weights can be static + } else if (mesh.springiness == 0.0f) { + glBufferData(GL_ARRAY_BUFFER, mesh.texCoords.size() * sizeof(glm::vec2) + (mesh.clusterIndices.size() + + mesh.clusterWeights.size()) * sizeof(glm::vec4), NULL, GL_STATIC_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, mesh.texCoords.size() * sizeof(glm::vec2), mesh.texCoords.constData()); + glBufferSubData(GL_ARRAY_BUFFER, mesh.texCoords.size() * sizeof(glm::vec2), + mesh.clusterIndices.size() * sizeof(glm::vec4), mesh.clusterIndices.constData()); + glBufferSubData(GL_ARRAY_BUFFER, mesh.texCoords.size() * sizeof(glm::vec2) + + mesh.clusterIndices.size() * sizeof(glm::vec4), mesh.clusterWeights.size() * sizeof(glm::vec4), + mesh.clusterWeights.constData()); } else { - glBufferData(GL_ARRAY_BUFFER, mesh.texCoords.size() * sizeof(glm::vec2), - mesh.texCoords.constData(), GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, mesh.texCoords.size() * sizeof(glm::vec2), NULL, GL_STATIC_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, mesh.texCoords.size() * sizeof(glm::vec2), mesh.texCoords.constData()); } glBindBuffer(GL_ARRAY_BUFFER, 0); From 124966d2e40a71e5e18fa120a8a9250f94ecb30a Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 14 Oct 2013 18:39:19 -0700 Subject: [PATCH 03/13] More progress on skinning. --- .../resources/shaders/skin_blendface.vert | 39 +++++++++ interface/src/avatar/BlendFace.cpp | 82 ++++++++++++------- interface/src/avatar/BlendFace.h | 6 +- interface/src/avatar/Head.h | 1 + 4 files changed, 98 insertions(+), 30 deletions(-) create mode 100644 interface/resources/shaders/skin_blendface.vert diff --git a/interface/resources/shaders/skin_blendface.vert b/interface/resources/shaders/skin_blendface.vert new file mode 100644 index 0000000000..d3d833d6fe --- /dev/null +++ b/interface/resources/shaders/skin_blendface.vert @@ -0,0 +1,39 @@ +#version 120 + +// +// skin_blendface.vert +// vertex shader +// +// Created by Andrzej Kapolka on 10/14/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +const int MAX_CLUSTERS = 32; +const int INDICES_PER_VERTEX = 4; + +uniform mat4 clusterMatrices[MAX_CLUSTERS]; + +attribute vec4 clusterIndices; +attribute vec4 clusterWeights; + +void main(void) { + vec4 position = vec4(0.0, 0.0, 0.0, 0.0); + vec4 normal = vec4(0.0, 0.0, 0.0, 0.0); + for (int i = 0; i < INDICES_PER_VERTEX; i++) { + mat4 clusterMatrix = clusterMatrices[int(clusterIndices[i])]; + float clusterWeight = clusterWeights[i]; + position += clusterMatrix * gl_Vertex * clusterWeight; + normal += clusterMatrix * vec4(gl_Normal, 0.0) * clusterWeight; + } + position = gl_ModelViewProjectionMatrix * position; + normal = normalize(gl_ModelViewMatrix * normal); + + // standard diffuse lighting + gl_FrontColor = (gl_LightModel.ambient + gl_LightSource[0].ambient + + gl_LightSource[0].diffuse * max(0.0, dot(normal, gl_LightSource[0].position))); + + // pass along the texture coordinate + gl_TexCoord[0] = gl_MultiTexCoord0; + + gl_Position = position; +} diff --git a/interface/src/avatar/BlendFace.cpp b/interface/src/avatar/BlendFace.cpp index 1380703fe6..b438769f5e 100644 --- a/interface/src/avatar/BlendFace.cpp +++ b/interface/src/avatar/BlendFace.cpp @@ -29,6 +29,10 @@ BlendFace::~BlendFace() { } ProgramObject BlendFace::_eyeProgram; +ProgramObject BlendFace::_skinProgram; +int BlendFace::_clusterMatricesLocation; +int BlendFace::_clusterIndicesLocation; +int BlendFace::_clusterWeightsLocation; void BlendFace::init() { if (!_eyeProgram.isLinked()) { @@ -40,6 +44,15 @@ void BlendFace::init() { _eyeProgram.bind(); _eyeProgram.setUniformValue("texture", 0); _eyeProgram.release(); + + _skinProgram.addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/skin_blendface.vert"); + _skinProgram.link(); + + _skinProgram.bind(); + _clusterMatricesLocation = _skinProgram.uniformLocation("clusterMatrices"); + _clusterIndicesLocation = _skinProgram.attributeLocation("clusterIndices"); + _clusterWeightsLocation = _skinProgram.attributeLocation("clusterWeights"); + _skinProgram.release(); } } @@ -66,7 +79,7 @@ void BlendFace::simulate(float deltaTime) { } foreach (const FBXMesh& mesh, geometry.meshes) { MeshState state; - state.jointMatrices.resize(mesh.clusters.size()); + state.clusterMatrices.resize(mesh.clusters.size()); if (mesh.springiness > 0.0f) { state.worldSpaceVertices.resize(mesh.vertices.size()); state.vertexVelocities.resize(mesh.vertices.size()); @@ -98,13 +111,14 @@ void BlendFace::simulate(float deltaTime) { glm::mat4_cast(state.rotation) * joint.postRotation; } else { - state.transform = _jointStates[joint.parentIndex].transform * joint.preRotation; + state.transform = _jointStates[joint.parentIndex].transform * joint.preRotation * + glm::mat4_cast(state.rotation) * joint.postRotation; if (i == geometry.leftEyeJointIndex || i == geometry.rightEyeJointIndex) { - // extract the translation component of the matrix - state.rotation = _owningHead->getEyeRotation(glm::vec3( - state.transform[3][0], state.transform[3][1], state.transform[3][2])); + // get the lookat position relative to the eye matrix + glm::vec3 lookat = glm::vec3(glm::inverse(state.transform) * + glm::vec4(_owningHead->getLookAtPosition() + _owningHead->getSaccade(), 1.0f)); + state.transform = state.transform * glm::mat4_cast(rotationBetween(glm::vec3(0.0f, 0.0f, 1.0f), lookat)); } - state.transform = state.transform * glm::mat4_cast(state.rotation) * joint.postRotation; } } @@ -113,7 +127,7 @@ void BlendFace::simulate(float deltaTime) { const FBXMesh& mesh = geometry.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - state.jointMatrices[j] = _jointStates[cluster.jointIndex].transform * cluster.inverseBindMatrix; + state.clusterMatrices[j] = _jointStates[cluster.jointIndex].transform * cluster.inverseBindMatrix; } int vertexCount = state.worldSpaceVertices.size(); if (vertexCount == 0) { @@ -141,7 +155,6 @@ void BlendFace::simulate(float deltaTime) { _blendedVertices[*index] += *vertex * coefficient; } } - sourceVertices = _blendedVertices.constData(); } glm::mat4 transform; @@ -153,20 +166,19 @@ void BlendFace::simulate(float deltaTime) { const glm::vec4* clusterWeights = mesh.clusterWeights.constData(); for (int j = 0; j < vertexCount; j++) { _blendedVertices[j] = - glm::vec3(state.jointMatrices[clusterIndices[j][0]] * + glm::vec3(state.clusterMatrices[clusterIndices[j][0]] * glm::vec4(sourceVertices[j], 1.0f)) * clusterWeights[j][0] + - glm::vec3(state.jointMatrices[clusterIndices[j][1]] * + glm::vec3(state.clusterMatrices[clusterIndices[j][1]] * glm::vec4(sourceVertices[j], 1.0f)) * clusterWeights[j][1] + - glm::vec3(state.jointMatrices[clusterIndices[j][2]] * + glm::vec3(state.clusterMatrices[clusterIndices[j][2]] * glm::vec4(sourceVertices[j], 1.0f)) * clusterWeights[j][2] + - glm::vec3(state.jointMatrices[clusterIndices[j][3]] * + glm::vec3(state.clusterMatrices[clusterIndices[j][3]] * glm::vec4(sourceVertices[j], 1.0f)) * clusterWeights[j][3]; } - sourceVertices = _blendedVertices.constData(); } else { - transform = state.jointMatrices[0]; + transform = state.clusterMatrices[0]; } if (_resetStates) { for (int j = 0; j < vertexCount; j++) { @@ -221,11 +233,6 @@ bool BlendFace::render(float alpha) { _dilatedTextures.resize(geometry.meshes.size()); } - glm::quat orientation = _owningHead->getOrientation(); - glm::vec3 scale(-_owningHead->getScale() * MODEL_SCALE, _owningHead->getScale() * MODEL_SCALE, - -_owningHead->getScale() * MODEL_SCALE); - glm::vec3 offset = MODEL_TRANSLATION - geometry.neckPivot; - glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); @@ -245,13 +252,9 @@ bool BlendFace::render(float alpha) { const FBXMesh& mesh = geometry.meshes.at(i); int vertexCount = mesh.vertices.size(); - glPushMatrix(); - // apply eye rotation if appropriate Texture* texture = networkMesh.diffuseTexture.data(); if (mesh.isEye) { - _eyeProgram.bind(); - if (texture != NULL) { texture = (_dilatedTextures[i] = static_cast(texture)->getDilatedTexture( _owningHead->getPupilDilation())).data(); @@ -266,14 +269,30 @@ bool BlendFace::render(float alpha) { glMaterialfv(GL_FRONT, GL_SPECULAR, (const float*)&specular); glMaterialf(GL_FRONT, GL_SHININESS, mesh.shininess); + glBindBuffer(GL_ARRAY_BUFFER, networkMesh.vertexBufferID); + const MeshState& state = _meshStates.at(i); if (state.worldSpaceVertices.isEmpty()) { - glMultMatrixf((const GLfloat*)&state.jointMatrices[0]); + if (state.clusterMatrices.size() > 1) { + _skinProgram.bind(); + glUniformMatrix4fvARB(_clusterMatricesLocation, state.clusterMatrices.size(), false, + (const float*)state.clusterMatrices.constData()); + int offset = vertexCount * sizeof(glm::vec2) + (mesh.blendshapes.isEmpty() ? + vertexCount * 2 * sizeof(glm::vec3) : 0); + _skinProgram.setAttributeBuffer(_clusterIndicesLocation, GL_FLOAT, offset, 4); + _skinProgram.setAttributeBuffer(_clusterWeightsLocation, GL_FLOAT, + offset + vertexCount * sizeof(glm::vec4), 4); + _skinProgram.enableAttributeArray(_clusterIndicesLocation); + _skinProgram.enableAttributeArray(_clusterWeightsLocation); + + } else { + glPushMatrix(); + glMultMatrixf((const GLfloat*)&state.clusterMatrices[0]); + } } glBindTexture(GL_TEXTURE_2D, texture == NULL ? 0 : texture->getID()); - glBindBuffer(GL_ARRAY_BUFFER, networkMesh.vertexBufferID); if (mesh.blendshapes.isEmpty() && mesh.springiness == 0.0f) { glTexCoordPointer(2, GL_FLOAT, 0, (void*)(vertexCount * 2 * sizeof(glm::vec3))); @@ -322,11 +341,16 @@ bool BlendFace::render(float alpha) { glDrawRangeElementsEXT(GL_TRIANGLES, 0, vertexCount - 1, mesh.triangleIndices.size(), GL_UNSIGNED_INT, (void*)(mesh.quadIndices.size() * sizeof(int))); - if (mesh.isEye) { - _eyeProgram.release(); + if (state.worldSpaceVertices.isEmpty()) { + if (state.clusterMatrices.size() > 1) { + _skinProgram.disableAttributeArray(_clusterIndicesLocation); + _skinProgram.disableAttributeArray(_clusterWeightsLocation); + _skinProgram.release(); + + } else { + glPopMatrix(); + } } - - glPopMatrix(); } glDisable(GL_NORMALIZE); diff --git a/interface/src/avatar/BlendFace.h b/interface/src/avatar/BlendFace.h index df944558b1..9a6d87f275 100644 --- a/interface/src/avatar/BlendFace.h +++ b/interface/src/avatar/BlendFace.h @@ -68,7 +68,7 @@ private: class MeshState { public: - QVector jointMatrices; + QVector clusterMatrices; QVector worldSpaceVertices; QVector vertexVelocities; QVector worldSpaceNormals; @@ -83,6 +83,10 @@ private: QVector _blendedNormals; static ProgramObject _eyeProgram; + static ProgramObject _skinProgram; + static int _clusterMatricesLocation; + static int _clusterIndicesLocation; + static int _clusterWeightsLocation; }; #endif /* defined(__interface__BlendFace__) */ diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h index e8dc25d9da..c8dbbf0735 100644 --- a/interface/src/avatar/Head.h +++ b/interface/src/avatar/Head.h @@ -69,6 +69,7 @@ public: glm::vec3 getPosition() const { return _position; } const glm::vec3& getSkinColor() const { return _skinColor; } const glm::vec3& getEyePosition() const { return _eyePosition; } + const glm::vec3& getSaccade() const { return _saccade; } glm::vec3 getRightDirection() const { return getOrientation() * IDENTITY_RIGHT; } glm::vec3 getUpDirection() const { return getOrientation() * IDENTITY_UP; } glm::vec3 getFrontDirection() const { return getOrientation() * IDENTITY_FRONT; } From e97cfb8cf80ceaa77490531ead0579684c6d2566 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 14 Oct 2013 19:59:13 -0700 Subject: [PATCH 04/13] Per-pixel specular for blend faces. --- interface/resources/shaders/blendface.frag | 24 ++++++++++++++++ interface/resources/shaders/blendface.vert | 28 +++++++++++++++++++ .../resources/shaders/skin_blendface.vert | 5 +++- interface/src/avatar/BlendFace.cpp | 24 ++++++++++------ interface/src/avatar/BlendFace.h | 2 +- 5 files changed, 73 insertions(+), 10 deletions(-) create mode 100644 interface/resources/shaders/blendface.frag create mode 100644 interface/resources/shaders/blendface.vert diff --git a/interface/resources/shaders/blendface.frag b/interface/resources/shaders/blendface.frag new file mode 100644 index 0000000000..ad37e514b9 --- /dev/null +++ b/interface/resources/shaders/blendface.frag @@ -0,0 +1,24 @@ +#version 120 + +// +// blendface.frag +// fragment shader +// +// Created by Andrzej Kapolka on 10/14/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +// the diffuse texture +uniform sampler2D texture; + +// the interpolated normal +varying vec4 normal; + +void main(void) { + // compute the specular component (sans exponent) based on the normal OpenGL lighting model + float specular = max(0.0, dot(normalize(gl_LightSource[0].position + vec4(0.0, 0.0, 1.0, 0.0)), normalize(normal))); + + // modulate texture by diffuse color and add specular contribution + gl_FragColor = gl_Color * texture2D(texture, gl_TexCoord[0].st) + + pow(specular, gl_FrontMaterial.shininess) * gl_FrontLightProduct[0].specular; +} diff --git a/interface/resources/shaders/blendface.vert b/interface/resources/shaders/blendface.vert new file mode 100644 index 0000000000..5556000b0e --- /dev/null +++ b/interface/resources/shaders/blendface.vert @@ -0,0 +1,28 @@ +#version 120 + +// +// blendface.vert +// vertex shader +// +// Created by Andrzej Kapolka on 10/14/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +// the interpolated normal +varying vec4 normal; + +void main(void) { + + // transform and store the normal for interpolation + normal = normalize(gl_ModelViewMatrix * vec4(gl_Normal, 0.0)); + + // compute standard diffuse lighting per-vertex + gl_FrontColor = vec4(gl_Color.rgb * (gl_LightModel.ambient.rgb + gl_LightSource[0].ambient.rgb + + gl_LightSource[0].diffuse.rgb * max(0.0, dot(normal, gl_LightSource[0].position))), gl_Color.a); + + // pass along the texture coordinate + gl_TexCoord[0] = gl_MultiTexCoord0; + + // use standard pipeline transform + gl_Position = ftransform(); +} diff --git a/interface/resources/shaders/skin_blendface.vert b/interface/resources/shaders/skin_blendface.vert index d3d833d6fe..2bd1e35525 100644 --- a/interface/resources/shaders/skin_blendface.vert +++ b/interface/resources/shaders/skin_blendface.vert @@ -16,9 +16,12 @@ uniform mat4 clusterMatrices[MAX_CLUSTERS]; attribute vec4 clusterIndices; attribute vec4 clusterWeights; +// the interpolated normal +varying vec4 normal; + void main(void) { vec4 position = vec4(0.0, 0.0, 0.0, 0.0); - vec4 normal = vec4(0.0, 0.0, 0.0, 0.0); + normal = vec4(0.0, 0.0, 0.0, 0.0); for (int i = 0; i < INDICES_PER_VERTEX; i++) { mat4 clusterMatrix = clusterMatrices[int(clusterIndices[i])]; float clusterWeight = clusterWeights[i]; diff --git a/interface/src/avatar/BlendFace.cpp b/interface/src/avatar/BlendFace.cpp index b438769f5e..b0992488d5 100644 --- a/interface/src/avatar/BlendFace.cpp +++ b/interface/src/avatar/BlendFace.cpp @@ -28,30 +28,32 @@ BlendFace::~BlendFace() { deleteGeometry(); } -ProgramObject BlendFace::_eyeProgram; +ProgramObject BlendFace::_program; ProgramObject BlendFace::_skinProgram; int BlendFace::_clusterMatricesLocation; int BlendFace::_clusterIndicesLocation; int BlendFace::_clusterWeightsLocation; void BlendFace::init() { - if (!_eyeProgram.isLinked()) { + if (!_program.isLinked()) { switchToResourcesParentIfRequired(); - _eyeProgram.addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/eye.vert"); - _eyeProgram.addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/iris.frag"); - _eyeProgram.link(); + _program.addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/blendface.vert"); + _program.addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/blendface.frag"); + _program.link(); - _eyeProgram.bind(); - _eyeProgram.setUniformValue("texture", 0); - _eyeProgram.release(); + _program.bind(); + _program.setUniformValue("texture", 0); + _program.release(); _skinProgram.addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/skin_blendface.vert"); + _skinProgram.addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/blendface.frag"); _skinProgram.link(); _skinProgram.bind(); _clusterMatricesLocation = _skinProgram.uniformLocation("clusterMatrices"); _clusterIndicesLocation = _skinProgram.attributeLocation("clusterIndices"); _clusterWeightsLocation = _skinProgram.attributeLocation("clusterWeights"); + _skinProgram.setUniformValue("texture", 0); _skinProgram.release(); } } @@ -288,7 +290,10 @@ bool BlendFace::render(float alpha) { } else { glPushMatrix(); glMultMatrixf((const GLfloat*)&state.clusterMatrices[0]); + _program.bind(); } + } else { + _program.bind(); } glBindTexture(GL_TEXTURE_2D, texture == NULL ? 0 : texture->getID()); @@ -349,7 +354,10 @@ bool BlendFace::render(float alpha) { } else { glPopMatrix(); + _program.release(); } + } else { + _program.release(); } } diff --git a/interface/src/avatar/BlendFace.h b/interface/src/avatar/BlendFace.h index 9a6d87f275..6add54d6b8 100644 --- a/interface/src/avatar/BlendFace.h +++ b/interface/src/avatar/BlendFace.h @@ -82,7 +82,7 @@ private: QVector _blendedVertices; QVector _blendedNormals; - static ProgramObject _eyeProgram; + static ProgramObject _program; static ProgramObject _skinProgram; static int _clusterMatricesLocation; static int _clusterIndicesLocation; From d06acdd1695e36e6f49738778d35710b1be54fd1 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 15 Oct 2013 11:50:29 -0700 Subject: [PATCH 05/13] Per-pixel shading, material tweaks. --- interface/resources/shaders/blendface.frag | 13 ++++++--- interface/resources/shaders/blendface.vert | 4 --- .../resources/shaders/skin_blendface.vert | 4 --- interface/src/avatar/BlendFace.cpp | 11 +------- interface/src/renderer/FBXReader.cpp | 28 ++++++++++--------- interface/src/renderer/FBXReader.h | 4 +-- 6 files changed, 26 insertions(+), 38 deletions(-) diff --git a/interface/resources/shaders/blendface.frag b/interface/resources/shaders/blendface.frag index ad37e514b9..7358c7dba9 100644 --- a/interface/resources/shaders/blendface.frag +++ b/interface/resources/shaders/blendface.frag @@ -15,10 +15,15 @@ uniform sampler2D texture; varying vec4 normal; void main(void) { - // compute the specular component (sans exponent) based on the normal OpenGL lighting model - float specular = max(0.0, dot(normalize(gl_LightSource[0].position + vec4(0.0, 0.0, 1.0, 0.0)), normalize(normal))); + // compute the base color based on OpenGL lighting model + vec4 normalizedNormal = normalize(normal); + vec4 base = gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient + + gl_FrontLightProduct[0].diffuse * max(0.0, dot(normalizedNormal, gl_LightSource[0].position)); + + // compute the specular component (sans exponent) + float specular = max(0.0, dot(normalize(gl_LightSource[0].position + vec4(0.0, 0.0, 1.0, 0.0)), normalizedNormal)); - // modulate texture by diffuse color and add specular contribution - gl_FragColor = gl_Color * texture2D(texture, gl_TexCoord[0].st) + + // modulate texture by base color and add specular contribution + gl_FragColor = base * texture2D(texture, gl_TexCoord[0].st) + pow(specular, gl_FrontMaterial.shininess) * gl_FrontLightProduct[0].specular; } diff --git a/interface/resources/shaders/blendface.vert b/interface/resources/shaders/blendface.vert index 5556000b0e..16893dfc0b 100644 --- a/interface/resources/shaders/blendface.vert +++ b/interface/resources/shaders/blendface.vert @@ -16,10 +16,6 @@ void main(void) { // transform and store the normal for interpolation normal = normalize(gl_ModelViewMatrix * vec4(gl_Normal, 0.0)); - // compute standard diffuse lighting per-vertex - gl_FrontColor = vec4(gl_Color.rgb * (gl_LightModel.ambient.rgb + gl_LightSource[0].ambient.rgb + - gl_LightSource[0].diffuse.rgb * max(0.0, dot(normal, gl_LightSource[0].position))), gl_Color.a); - // pass along the texture coordinate gl_TexCoord[0] = gl_MultiTexCoord0; diff --git a/interface/resources/shaders/skin_blendface.vert b/interface/resources/shaders/skin_blendface.vert index 2bd1e35525..af2d0103a9 100644 --- a/interface/resources/shaders/skin_blendface.vert +++ b/interface/resources/shaders/skin_blendface.vert @@ -31,10 +31,6 @@ void main(void) { position = gl_ModelViewProjectionMatrix * position; normal = normalize(gl_ModelViewMatrix * normal); - // standard diffuse lighting - gl_FrontColor = (gl_LightModel.ambient + gl_LightSource[0].ambient + - gl_LightSource[0].diffuse * max(0.0, dot(normal, gl_LightSource[0].position))); - // pass along the texture coordinate gl_TexCoord[0] = gl_MultiTexCoord0; diff --git a/interface/src/avatar/BlendFace.cpp b/interface/src/avatar/BlendFace.cpp index b0992488d5..3ecb3caf75 100644 --- a/interface/src/avatar/BlendFace.cpp +++ b/interface/src/avatar/BlendFace.cpp @@ -94,7 +94,7 @@ void BlendFace::simulate(float deltaTime) { glm::quat orientation = static_cast(_owningHead->_owningAvatar)->getOrientation(); glm::vec3 scale = glm::vec3(-1.0f, 1.0f, -1.0f) * _owningHead->getScale() * MODEL_SCALE; - glm::vec3 offset = MODEL_TRANSLATION - _geometry->getFBXGeometry().neckPivot; + glm::vec3 offset = MODEL_TRANSLATION - geometry.neckPivot; glm::mat4 baseTransform = glm::translate(_owningHead->getPosition()) * glm::mat4_cast(orientation) * glm::scale(scale) * glm::translate(offset); @@ -239,14 +239,8 @@ bool BlendFace::render(float alpha) { glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); - // enable normalization under the expectation that the GPU can do it faster - glEnable(GL_NORMALIZE); - glEnable(GL_TEXTURE_2D); glDisable(GL_COLOR_MATERIAL); - // the eye shader uses the color state even though color material is disabled - glColor4f(1.0f, 1.0f, 1.0f, alpha); - for (int i = 0; i < networkMeshes.size(); i++) { const NetworkMesh& networkMesh = networkMeshes.at(i); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, networkMesh.indexBufferID); @@ -361,9 +355,6 @@ bool BlendFace::render(float alpha) { } } - glDisable(GL_NORMALIZE); - glDisable(GL_TEXTURE_2D); - // deactivate vertex arrays after drawing glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index c20a965a24..56961ec41e 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -722,6 +722,13 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) joint.preRotation = model.preRotation; joint.rotation = model.rotation; joint.postRotation = model.postRotation; + if (joint.parentIndex == -1) { + joint.transform = geometry.offset * model.preRotation * glm::mat4_cast(model.rotation) * model.postRotation; + + } else { + joint.transform = geometry.joints.at(joint.parentIndex).transform * + model.preRotation * glm::mat4_cast(model.rotation) * model.postRotation; + } geometry.joints.append(joint); } @@ -730,6 +737,12 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) geometry.rightEyeJointIndex = modelIDs.indexOf(jointEyeRightID); geometry.neckJointIndex = modelIDs.indexOf(jointNeckID); + // extract the translation component of the neck transform + if (geometry.neckJointIndex != -1) { + const glm::mat4& transform = geometry.joints.at(geometry.neckJointIndex).transform; + geometry.neckPivot = glm::vec3(transform[3][0], transform[3][1], transform[3][2]); + } + QVariantHash springs = mapping.value("spring").toHash(); QVariant defaultSpring = springs.value("default"); for (QHash::iterator it = meshes.begin(); it != meshes.end(); it++) { @@ -775,18 +788,11 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) if (jointID == jointEyeLeftID || jointID == jointEyeRightID) { mesh.isEye = true; } + // see http://stackoverflow.com/questions/13566608/loading-skinning-information-from-fbx for a discussion + // of skinning information in FBX fbxCluster.jointIndex = modelIDs.indexOf(jointID); fbxCluster.inverseBindMatrix = glm::inverse(cluster.transformLink) * modelTransform; mesh.clusters.append(fbxCluster); - - // see http://stackoverflow.com/questions/13566608/loading-skinning-information-from-fbx for a discussion - // of skinning information in FBX - glm::mat4 jointTransform = geometry.offset * getGlobalTransform(parentMap, models, jointID); - mesh.transform = jointTransform * glm::inverse(cluster.transformLink) * modelTransform; - - // extract translation component for pivot - glm::mat4 jointTransformScaled = geometry.offset * getGlobalTransform(parentMap, models, jointID); - mesh.pivot = glm::vec3(jointTransformScaled[3][0], jointTransformScaled[3][1], jointTransformScaled[3][2]); } } @@ -865,10 +871,6 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) geometry.meshes.append(mesh); } - // extract translation component for neck pivot - glm::mat4 neckTransform = geometry.offset * getGlobalTransform(parentMap, models, jointNeckID); - geometry.neckPivot = glm::vec3(neckTransform[3][0], neckTransform[3][1], neckTransform[3][2]); - return geometry; } diff --git a/interface/src/renderer/FBXReader.h b/interface/src/renderer/FBXReader.h index 72033813fa..f983715d60 100644 --- a/interface/src/renderer/FBXReader.h +++ b/interface/src/renderer/FBXReader.h @@ -45,6 +45,7 @@ public: glm::mat4 preRotation; glm::quat rotation; glm::mat4 postRotation; + glm::mat4 transform; }; /// A single binding to a joint in an FBX document. @@ -69,9 +70,6 @@ public: QVector clusters; - glm::vec3 pivot; - glm::mat4 transform; - bool isEye; glm::vec3 diffuseColor; From 053c116481a818050a071c2de8cba5c9bc6930fa Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 15 Oct 2013 11:57:42 -0700 Subject: [PATCH 06/13] Removed unused variable. --- interface/src/renderer/FBXReader.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index 56961ec41e..4e98e81813 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -810,7 +810,6 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) for (int i = 0; i < clusterIDs.size(); i++) { qint64 clusterID = clusterIDs.at(i); const Cluster& cluster = clusters[clusterID]; - qint64 jointID = childMap.value(clusterID); for (int j = 0; j < cluster.indices.size(); j++) { int index = cluster.indices.at(j); From ce0d13feb45f59a9c88da7529d73a560665218d4 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 15 Oct 2013 12:59:52 -0700 Subject: [PATCH 07/13] Working on the neck transform. --- interface/src/avatar/BlendFace.cpp | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/interface/src/avatar/BlendFace.cpp b/interface/src/avatar/BlendFace.cpp index 3ecb3caf75..de3456549d 100644 --- a/interface/src/avatar/BlendFace.cpp +++ b/interface/src/avatar/BlendFace.cpp @@ -62,7 +62,7 @@ void BlendFace::reset() { _resetStates = true; } -const glm::vec3 MODEL_TRANSLATION(0.0f, -120.0f, 40.0f); // temporary fudge factor +const glm::vec3 MODEL_TRANSLATION(0.0f, -50.0f, 40.0f); // temporary fudge factor const float MODEL_SCALE = 0.0006f; void BlendFace::simulate(float deltaTime) { @@ -92,18 +92,13 @@ void BlendFace::simulate(float deltaTime) { _resetStates = true; } - glm::quat orientation = static_cast(_owningHead->_owningAvatar)->getOrientation(); + const Skeleton& skeleton = static_cast(_owningHead->_owningAvatar)->getSkeleton(); + glm::quat orientation = skeleton.joint[AVATAR_JOINT_NECK_BASE].absoluteRotation; glm::vec3 scale = glm::vec3(-1.0f, 1.0f, -1.0f) * _owningHead->getScale() * MODEL_SCALE; glm::vec3 offset = MODEL_TRANSLATION - geometry.neckPivot; - glm::mat4 baseTransform = glm::translate(_owningHead->getPosition()) * glm::mat4_cast(orientation) * + glm::mat4 baseTransform = glm::translate(skeleton.joint[AVATAR_JOINT_NECK_BASE].position) * glm::mat4_cast(orientation) * glm::scale(scale) * glm::translate(offset); - // apply the neck rotation - if (geometry.neckJointIndex != -1) { - _jointStates[geometry.neckJointIndex].rotation = glm::quat(glm::radians(glm::vec3( - _owningHead->getPitch(), _owningHead->getYaw(), _owningHead->getRoll()))); - } - // update the world space transforms for all joints for (int i = 0; i < _jointStates.size(); i++) { JointState& state = _jointStates[i]; @@ -111,7 +106,19 @@ void BlendFace::simulate(float deltaTime) { if (joint.parentIndex == -1) { state.transform = baseTransform * geometry.offset * joint.preRotation * glm::mat4_cast(state.rotation) * joint.postRotation; - + + } else if (i == geometry.neckJointIndex) { + glm::vec3 pitchAxis = orientation * glm::vec3(1.0f, 0.0f, 0.0f); + glm::vec3 yawAxis = orientation * glm::vec3(0.0f, 1.0f, 0.0f); + glm::vec3 rollAxis = orientation * glm::vec3(0.0f, 0.0f, 1.0f); + glm::mat4 preTransform = _jointStates[joint.parentIndex].transform * joint.preRotation * glm::mat4_cast(joint.rotation); + glm::mat3 inverse = glm::inverse(glm::mat3(preTransform)); + state.rotation = glm::angleAxis(_owningHead->getPitch(), glm::normalize(inverse * pitchAxis)) * + glm::angleAxis(_owningHead->getYaw(), glm::normalize(inverse * yawAxis)) * + glm::angleAxis(_owningHead->getRoll(), glm::normalize(inverse * rollAxis)) * joint.rotation; + state.transform = _jointStates[joint.parentIndex].transform * joint.preRotation * + glm::mat4_cast(state.rotation) * joint.postRotation; + } else { state.transform = _jointStates[joint.parentIndex].transform * joint.preRotation * glm::mat4_cast(state.rotation) * joint.postRotation; From 66250ed488949963ed8fc88e13821dae1821b812 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 15 Oct 2013 13:43:27 -0700 Subject: [PATCH 08/13] Eye tweak. --- interface/src/avatar/BlendFace.cpp | 38 +++++++++++++++--------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/interface/src/avatar/BlendFace.cpp b/interface/src/avatar/BlendFace.cpp index de3456549d..8615279bbb 100644 --- a/interface/src/avatar/BlendFace.cpp +++ b/interface/src/avatar/BlendFace.cpp @@ -62,7 +62,7 @@ void BlendFace::reset() { _resetStates = true; } -const glm::vec3 MODEL_TRANSLATION(0.0f, -50.0f, 40.0f); // temporary fudge factor +const glm::vec3 MODEL_TRANSLATION(0.0f, -60.0f, 40.0f); // temporary fudge factor const float MODEL_SCALE = 0.0006f; void BlendFace::simulate(float deltaTime) { @@ -107,27 +107,27 @@ void BlendFace::simulate(float deltaTime) { state.transform = baseTransform * geometry.offset * joint.preRotation * glm::mat4_cast(state.rotation) * joint.postRotation; - } else if (i == geometry.neckJointIndex) { - glm::vec3 pitchAxis = orientation * glm::vec3(1.0f, 0.0f, 0.0f); - glm::vec3 yawAxis = orientation * glm::vec3(0.0f, 1.0f, 0.0f); - glm::vec3 rollAxis = orientation * glm::vec3(0.0f, 0.0f, 1.0f); - glm::mat4 preTransform = _jointStates[joint.parentIndex].transform * joint.preRotation * glm::mat4_cast(joint.rotation); - glm::mat3 inverse = glm::inverse(glm::mat3(preTransform)); - state.rotation = glm::angleAxis(_owningHead->getPitch(), glm::normalize(inverse * pitchAxis)) * - glm::angleAxis(_owningHead->getYaw(), glm::normalize(inverse * yawAxis)) * - glm::angleAxis(_owningHead->getRoll(), glm::normalize(inverse * rollAxis)) * joint.rotation; - state.transform = _jointStates[joint.parentIndex].transform * joint.preRotation * - glm::mat4_cast(state.rotation) * joint.postRotation; - } else { + if (i == geometry.neckJointIndex) { + // get the rotation axes in joint space and use them to adjust the rotation + glm::mat3 axes = glm::mat3_cast(orientation); + glm::mat3 inverse = glm::inverse(glm::mat3(_jointStates[joint.parentIndex].transform * + joint.preRotation * glm::mat4_cast(joint.rotation))); + state.rotation = glm::angleAxis(_owningHead->getRoll(), glm::normalize(inverse * axes[2])) * + glm::angleAxis(_owningHead->getYaw(), glm::normalize(inverse * axes[1])) * + glm::angleAxis(_owningHead->getPitch(), glm::normalize(inverse * axes[0])) * joint.rotation; + + } else if (i == geometry.leftEyeJointIndex || i == geometry.rightEyeJointIndex) { + // likewise with the lookat position + glm::mat4 inverse = glm::inverse(_jointStates[joint.parentIndex].transform * + joint.preRotation * glm::mat4_cast(joint.rotation)); + glm::vec3 front = glm::vec3(inverse * glm::vec4(_owningHead->getOrientation() * IDENTITY_FRONT, 0.0f)); + glm::vec3 lookAt = glm::vec3(inverse * glm::vec4(_owningHead->getLookAtPosition() + + _owningHead->getSaccade(), 1.0f)); + state.rotation = rotationBetween(front, lookAt) * joint.rotation; + } state.transform = _jointStates[joint.parentIndex].transform * joint.preRotation * glm::mat4_cast(state.rotation) * joint.postRotation; - if (i == geometry.leftEyeJointIndex || i == geometry.rightEyeJointIndex) { - // get the lookat position relative to the eye matrix - glm::vec3 lookat = glm::vec3(glm::inverse(state.transform) * - glm::vec4(_owningHead->getLookAtPosition() + _owningHead->getSaccade(), 1.0f)); - state.transform = state.transform * glm::mat4_cast(rotationBetween(glm::vec3(0.0f, 0.0f, 1.0f), lookat)); - } } } From ba25087b3ea473060774ca0b61e21d9a5474feb9 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 15 Oct 2013 15:58:34 -0700 Subject: [PATCH 09/13] Support for multiple mesh "parts" (with different materials), fix for untextured meshes. --- interface/src/avatar/BlendFace.cpp | 58 +++++++------ interface/src/avatar/BlendFace.h | 2 +- interface/src/renderer/FBXReader.cpp | 102 +++++++++++++---------- interface/src/renderer/FBXReader.h | 26 ++++-- interface/src/renderer/GeometryCache.cpp | 66 ++++++++++----- interface/src/renderer/GeometryCache.h | 11 ++- interface/src/renderer/TextureCache.cpp | 26 +++++- interface/src/renderer/TextureCache.h | 6 +- 8 files changed, 191 insertions(+), 106 deletions(-) diff --git a/interface/src/avatar/BlendFace.cpp b/interface/src/avatar/BlendFace.cpp index 8615279bbb..8f43752736 100644 --- a/interface/src/avatar/BlendFace.cpp +++ b/interface/src/avatar/BlendFace.cpp @@ -236,10 +236,11 @@ bool BlendFace::render(float alpha) { glBindBuffer(GL_ARRAY_BUFFER, 0); } _blendedVertexBufferIDs.append(id); + + QVector > dilated; + dilated.resize(mesh.parts.size()); + _dilatedTextures.append(dilated); } - - // make sure we have the right number of dilated texture pointers - _dilatedTextures.resize(geometry.meshes.size()); } glEnableClientState(GL_VERTEX_ARRAY); @@ -255,23 +256,6 @@ bool BlendFace::render(float alpha) { const FBXMesh& mesh = geometry.meshes.at(i); int vertexCount = mesh.vertices.size(); - // apply eye rotation if appropriate - Texture* texture = networkMesh.diffuseTexture.data(); - if (mesh.isEye) { - if (texture != NULL) { - texture = (_dilatedTextures[i] = static_cast(texture)->getDilatedTexture( - _owningHead->getPupilDilation())).data(); - } - } - - // apply material properties - glm::vec4 diffuse = glm::vec4(mesh.diffuseColor, alpha); - glm::vec4 specular = glm::vec4(mesh.specularColor, alpha); - glMaterialfv(GL_FRONT, GL_AMBIENT, (const float*)&diffuse); - glMaterialfv(GL_FRONT, GL_DIFFUSE, (const float*)&diffuse); - glMaterialfv(GL_FRONT, GL_SPECULAR, (const float*)&specular); - glMaterialf(GL_FRONT, GL_SHININESS, mesh.shininess); - glBindBuffer(GL_ARRAY_BUFFER, networkMesh.vertexBufferID); const MeshState& state = _meshStates.at(i); @@ -297,8 +281,6 @@ bool BlendFace::render(float alpha) { _program.bind(); } - glBindTexture(GL_TEXTURE_2D, texture == NULL ? 0 : texture->getID()); - if (mesh.blendshapes.isEmpty() && mesh.springiness == 0.0f) { glTexCoordPointer(2, GL_FLOAT, 0, (void*)(vertexCount * 2 * sizeof(glm::vec3))); @@ -343,10 +325,36 @@ bool BlendFace::render(float alpha) { glVertexPointer(3, GL_FLOAT, 0, 0); glNormalPointer(GL_FLOAT, 0, (void*)(vertexCount * sizeof(glm::vec3))); - glDrawRangeElementsEXT(GL_QUADS, 0, vertexCount - 1, mesh.quadIndices.size(), GL_UNSIGNED_INT, 0); - glDrawRangeElementsEXT(GL_TRIANGLES, 0, vertexCount - 1, mesh.triangleIndices.size(), - GL_UNSIGNED_INT, (void*)(mesh.quadIndices.size() * sizeof(int))); + qint64 offset = 0; + for (int j = 0; j < networkMesh.parts.size(); j++) { + const NetworkMeshPart& networkPart = networkMesh.parts.at(j); + const FBXMeshPart& part = mesh.parts.at(j); + // apply material properties + glm::vec4 diffuse = glm::vec4(part.diffuseColor, alpha); + glm::vec4 specular = glm::vec4(part.specularColor, alpha); + glMaterialfv(GL_FRONT, GL_AMBIENT, (const float*)&diffuse); + glMaterialfv(GL_FRONT, GL_DIFFUSE, (const float*)&diffuse); + glMaterialfv(GL_FRONT, GL_SPECULAR, (const float*)&specular); + glMaterialf(GL_FRONT, GL_SHININESS, part.shininess); + + Texture* texture = networkPart.diffuseTexture.data(); + if (mesh.isEye) { + if (texture != NULL) { + texture = (_dilatedTextures[i][j] = static_cast(texture)->getDilatedTexture( + _owningHead->getPupilDilation())).data(); + } + } + glBindTexture(GL_TEXTURE_2D, texture == NULL ? Application::getInstance()->getTextureCache()->getWhiteTextureID() : + texture->getID()); + + glDrawRangeElementsEXT(GL_QUADS, 0, vertexCount - 1, part.quadIndices.size(), GL_UNSIGNED_INT, (void*)offset); + offset += part.quadIndices.size() * sizeof(int); + glDrawRangeElementsEXT(GL_TRIANGLES, 0, vertexCount - 1, part.triangleIndices.size(), + GL_UNSIGNED_INT, (void*)offset); + offset += part.triangleIndices.size() * sizeof(int); + } + if (state.worldSpaceVertices.isEmpty()) { if (state.clusterMatrices.size() > 1) { _skinProgram.disableAttributeArray(_clusterIndicesLocation); diff --git a/interface/src/avatar/BlendFace.h b/interface/src/avatar/BlendFace.h index 6add54d6b8..4721031107 100644 --- a/interface/src/avatar/BlendFace.h +++ b/interface/src/avatar/BlendFace.h @@ -76,7 +76,7 @@ private: QVector _meshStates; QVector _blendedVertexBufferIDs; - QVector > _dilatedTextures; + QVector > > _dilatedTextures; bool _resetStates; QVector _blendedVertices; diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index 4e98e81813..19b502503f 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -426,6 +426,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) QVector normalIndices; QVector texCoords; QVector texCoordIndices; + QVector materials; foreach (const FBXNode& data, object.children) { if (data.name == "Vertices") { mesh.vertices = createVec3Vector(data.properties.at(0).value >()); @@ -459,6 +460,12 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) texCoordIndices = subdata.properties.at(0).value >(); } } + } else if (data.name == "LayerElementMaterial") { + foreach (const FBXNode& subdata, data.children) { + if (subdata.name == "Materials") { + materials = subdata.properties.at(0).value >(); + } + } } } @@ -494,25 +501,30 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) } // convert the polygons to quads and triangles + int polygonIndex = 0; for (const int* beginIndex = polygonIndices.constData(), *end = beginIndex + polygonIndices.size(); - beginIndex != end; ) { + beginIndex != end; polygonIndex++) { const int* endIndex = beginIndex; while (*endIndex++ >= 0); + int materialIndex = (polygonIndex < materials.size()) ? materials.at(polygonIndex) : 0; + mesh.parts.resize(max(mesh.parts.size(), materialIndex + 1)); + FBXMeshPart& part = mesh.parts[materialIndex]; + if (endIndex - beginIndex == 4) { - mesh.quadIndices.append(*beginIndex++); - mesh.quadIndices.append(*beginIndex++); - mesh.quadIndices.append(*beginIndex++); - mesh.quadIndices.append(-*beginIndex++ - 1); + part.quadIndices.append(*beginIndex++); + part.quadIndices.append(*beginIndex++); + part.quadIndices.append(*beginIndex++); + part.quadIndices.append(-*beginIndex++ - 1); } else { for (const int* nextIndex = beginIndex + 1;; ) { - mesh.triangleIndices.append(*beginIndex); - mesh.triangleIndices.append(*nextIndex++); + part.triangleIndices.append(*beginIndex); + part.triangleIndices.append(*nextIndex++); if (*nextIndex >= 0) { - mesh.triangleIndices.append(*nextIndex); + part.triangleIndices.append(*nextIndex); } else { - mesh.triangleIndices.append(-*nextIndex - 1); + part.triangleIndices.append(-*nextIndex - 1); break; } } @@ -754,21 +766,23 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) glm::mat4 modelTransform = getGlobalTransform(parentMap, models, modelID); // look for textures, material properties + int partIndex = 0; foreach (qint64 childID, childMap.values(modelID)) { - if (!materials.contains(childID)) { + if (!materials.contains(childID) || partIndex >= mesh.parts.size()) { continue; } Material material = materials.value(childID); - mesh.diffuseColor = material.diffuse; - mesh.specularColor = material.specular; - mesh.shininess = material.shininess; + FBXMeshPart& part = mesh.parts[partIndex++]; + part.diffuseColor = material.diffuse; + part.specularColor = material.specular; + part.shininess = material.shininess; qint64 diffuseTextureID = diffuseTextures.value(childID); if (diffuseTextureID != 0) { - mesh.diffuseFilename = textureFilenames.value(diffuseTextureID); + part.diffuseFilename = textureFilenames.value(diffuseTextureID); } qint64 bumpTextureID = bumpTextures.value(childID); if (bumpTextureID != 0) { - mesh.normalFilename = textureFilenames.value(bumpTextureID); + part.normalFilename = textureFilenames.value(bumpTextureID); } } @@ -832,34 +846,36 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) QSet > edges; mesh.vertexConnections.resize(mesh.vertices.size()); - for (int i = 0; i < mesh.quadIndices.size(); i += 4) { - int index0 = mesh.quadIndices.at(i); - int index1 = mesh.quadIndices.at(i + 1); - int index2 = mesh.quadIndices.at(i + 2); - int index3 = mesh.quadIndices.at(i + 3); - - edges.insert(QPair(qMin(index0, index1), qMax(index0, index1))); - edges.insert(QPair(qMin(index1, index2), qMax(index1, index2))); - edges.insert(QPair(qMin(index2, index3), qMax(index2, index3))); - edges.insert(QPair(qMin(index3, index0), qMax(index3, index0))); - - mesh.vertexConnections[index0].append(QPair(index3, index1)); - mesh.vertexConnections[index1].append(QPair(index0, index2)); - mesh.vertexConnections[index2].append(QPair(index1, index3)); - mesh.vertexConnections[index3].append(QPair(index2, index0)); - } - for (int i = 0; i < mesh.triangleIndices.size(); i += 3) { - int index0 = mesh.triangleIndices.at(i); - int index1 = mesh.triangleIndices.at(i + 1); - int index2 = mesh.triangleIndices.at(i + 2); - - edges.insert(QPair(qMin(index0, index1), qMax(index0, index1))); - edges.insert(QPair(qMin(index1, index2), qMax(index1, index2))); - edges.insert(QPair(qMin(index2, index0), qMax(index2, index0))); - - mesh.vertexConnections[index0].append(QPair(index2, index1)); - mesh.vertexConnections[index1].append(QPair(index0, index2)); - mesh.vertexConnections[index2].append(QPair(index1, index0)); + foreach (const FBXMeshPart& part, mesh.parts) { + for (int i = 0; i < part.quadIndices.size(); i += 4) { + int index0 = part.quadIndices.at(i); + int index1 = part.quadIndices.at(i + 1); + int index2 = part.quadIndices.at(i + 2); + int index3 = part.quadIndices.at(i + 3); + + edges.insert(QPair(qMin(index0, index1), qMax(index0, index1))); + edges.insert(QPair(qMin(index1, index2), qMax(index1, index2))); + edges.insert(QPair(qMin(index2, index3), qMax(index2, index3))); + edges.insert(QPair(qMin(index3, index0), qMax(index3, index0))); + + mesh.vertexConnections[index0].append(QPair(index3, index1)); + mesh.vertexConnections[index1].append(QPair(index0, index2)); + mesh.vertexConnections[index2].append(QPair(index1, index3)); + mesh.vertexConnections[index3].append(QPair(index2, index0)); + } + for (int i = 0; i < part.triangleIndices.size(); i += 3) { + int index0 = part.triangleIndices.at(i); + int index1 = part.triangleIndices.at(i + 1); + int index2 = part.triangleIndices.at(i + 2); + + edges.insert(QPair(qMin(index0, index1), qMax(index0, index1))); + edges.insert(QPair(qMin(index1, index2), qMax(index1, index2))); + edges.insert(QPair(qMin(index2, index0), qMax(index2, index0))); + + mesh.vertexConnections[index0].append(QPair(index2, index1)); + mesh.vertexConnections[index1].append(QPair(index0, index2)); + mesh.vertexConnections[index2].append(QPair(index1, index0)); + } } for (QSet >::const_iterator edge = edges.constBegin(); edge != edges.constEnd(); edge++) { diff --git a/interface/src/renderer/FBXReader.h b/interface/src/renderer/FBXReader.h index f983715d60..85e463f8bf 100644 --- a/interface/src/renderer/FBXReader.h +++ b/interface/src/renderer/FBXReader.h @@ -56,12 +56,27 @@ public: glm::mat4 inverseBindMatrix; }; -/// A single mesh (with optional blendshapes) extracted from an FBX document. -class FBXMesh { +/// A single part of a mesh (with the same material). +class FBXMeshPart { public: QVector quadIndices; QVector triangleIndices; + + glm::vec3 diffuseColor; + glm::vec3 specularColor; + float shininess; + + QByteArray diffuseFilename; + QByteArray normalFilename; +}; + +/// A single mesh (with optional blendshapes) extracted from an FBX document. +class FBXMesh { +public: + + QVector parts; + QVector vertices; QVector normals; QVector texCoords; @@ -72,13 +87,6 @@ public: bool isEye; - glm::vec3 diffuseColor; - glm::vec3 specularColor; - float shininess; - - QByteArray diffuseFilename; - QByteArray normalFilename; - QVector blendshapes; float springiness; diff --git a/interface/src/renderer/GeometryCache.cpp b/interface/src/renderer/GeometryCache.cpp index 408d159198..ea3f8fa1fc 100644 --- a/interface/src/renderer/GeometryCache.cpp +++ b/interface/src/renderer/GeometryCache.cpp @@ -290,19 +290,26 @@ NetworkGeometry::~NetworkGeometry() { glm::vec4 NetworkGeometry::computeAverageColor() const { glm::vec4 totalColor; - int totalVertices = 0; + int totalTriangles = 0; for (int i = 0; i < _meshes.size(); i++) { - if (_geometry.meshes.at(i).isEye) { + const FBXMesh& mesh = _geometry.meshes.at(i); + if (mesh.isEye) { continue; // skip eyes } - glm::vec4 color = glm::vec4(_geometry.meshes.at(i).diffuseColor, 1.0f); - if (_meshes.at(i).diffuseTexture) { - color *= _meshes.at(i).diffuseTexture->getAverageColor(); + const NetworkMesh& networkMesh = _meshes.at(i); + for (int j = 0; j < mesh.parts.size(); j++) { + const FBXMeshPart& part = mesh.parts.at(j); + const NetworkMeshPart& networkPart = networkMesh.parts.at(j); + glm::vec4 color = glm::vec4(part.diffuseColor, 1.0f); + if (networkPart.diffuseTexture) { + color *= networkPart.diffuseTexture->getAverageColor(); + } + int triangles = part.quadIndices.size() * 2 + part.triangleIndices.size(); + totalColor += color * triangles; + totalTriangles += triangles; } - totalColor += color * _geometry.meshes.at(i).vertices.size(); - totalVertices += _geometry.meshes.at(i).vertices.size(); } - return (totalVertices == 0) ? glm::vec4(1.0f, 1.0f, 1.0f, 1.0f) : totalColor / totalVertices; + return (totalTriangles == 0) ? glm::vec4(1.0f, 1.0f, 1.0f, 1.0f) : totalColor / totalTriangles; } void NetworkGeometry::handleModelReplyError() { @@ -351,13 +358,36 @@ void NetworkGeometry::maybeReadModelWithMapping() { foreach (const FBXMesh& mesh, _geometry.meshes) { NetworkMesh networkMesh; + int totalIndices = 0; + foreach (const FBXMeshPart& part, mesh.parts) { + NetworkMeshPart networkPart; + QString basePath = url.path(); + basePath = basePath.left(basePath.lastIndexOf('/') + 1); + if (!part.diffuseFilename.isEmpty()) { + url.setPath(basePath + part.diffuseFilename); + networkPart.diffuseTexture = Application::getInstance()->getTextureCache()->getTexture(url, mesh.isEye); + } + if (!part.normalFilename.isEmpty()) { + url.setPath(basePath + part.normalFilename); + networkPart.normalTexture = Application::getInstance()->getTextureCache()->getTexture(url); + } + networkMesh.parts.append(networkPart); + + totalIndices += (part.quadIndices.size() + part.triangleIndices.size()); + } + glGenBuffers(1, &networkMesh.indexBufferID); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, networkMesh.indexBufferID); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, (mesh.quadIndices.size() + mesh.triangleIndices.size()) * sizeof(int), - NULL, GL_STATIC_DRAW); - glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, mesh.quadIndices.size() * sizeof(int), mesh.quadIndices.constData()); - glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, mesh.quadIndices.size() * sizeof(int), - mesh.triangleIndices.size() * sizeof(int), mesh.triangleIndices.constData()); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, totalIndices * sizeof(int), NULL, GL_STATIC_DRAW); + int offset = 0; + foreach (const FBXMeshPart& part, mesh.parts) { + glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, offset, part.quadIndices.size() * sizeof(int), + part.quadIndices.constData()); + offset += part.quadIndices.size() * sizeof(int); + glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, offset, part.triangleIndices.size() * sizeof(int), + part.triangleIndices.constData()); + offset += part.triangleIndices.size() * sizeof(int); + } glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glGenBuffers(1, &networkMesh.vertexBufferID); @@ -398,16 +428,6 @@ void NetworkGeometry::maybeReadModelWithMapping() { glBindBuffer(GL_ARRAY_BUFFER, 0); - QString basePath = url.path(); - basePath = basePath.left(basePath.lastIndexOf('/') + 1); - if (!mesh.diffuseFilename.isEmpty()) { - url.setPath(basePath + mesh.diffuseFilename); - networkMesh.diffuseTexture = Application::getInstance()->getTextureCache()->getTexture(url, mesh.isEye); - } - if (!mesh.normalFilename.isEmpty()) { - url.setPath(basePath + mesh.normalFilename); - networkMesh.normalTexture = Application::getInstance()->getTextureCache()->getTexture(url); - } _meshes.append(networkMesh); } } diff --git a/interface/src/renderer/GeometryCache.h b/interface/src/renderer/GeometryCache.h index ba7ef8db0e..ac6669ad2f 100644 --- a/interface/src/renderer/GeometryCache.h +++ b/interface/src/renderer/GeometryCache.h @@ -80,6 +80,14 @@ private: QVector _meshes; }; +/// The state associated with a single mesh part. +class NetworkMeshPart { +public: + + QSharedPointer diffuseTexture; + QSharedPointer normalTexture; +}; + /// The state associated with a single mesh. class NetworkMesh { public: @@ -87,8 +95,7 @@ public: GLuint indexBufferID; GLuint vertexBufferID; - QSharedPointer diffuseTexture; - QSharedPointer normalTexture; + QVector parts; }; #endif /* defined(__interface__GeometryCache__) */ diff --git a/interface/src/renderer/TextureCache.cpp b/interface/src/renderer/TextureCache.cpp index d12b8098da..c20826989c 100644 --- a/interface/src/renderer/TextureCache.cpp +++ b/interface/src/renderer/TextureCache.cpp @@ -17,14 +17,22 @@ #include "Application.h" #include "TextureCache.h" -TextureCache::TextureCache() : _permutationNormalTextureID(0), - _primaryFramebufferObject(NULL), _secondaryFramebufferObject(NULL), _tertiaryFramebufferObject(NULL) { +TextureCache::TextureCache() : + _permutationNormalTextureID(0), + _whiteTextureID(0), + _primaryFramebufferObject(NULL), + _secondaryFramebufferObject(NULL), + _tertiaryFramebufferObject(NULL) +{ } TextureCache::~TextureCache() { if (_permutationNormalTextureID != 0) { glDeleteTextures(1, &_permutationNormalTextureID); } + if (_whiteTextureID != 0) { + glDeleteTextures(1, &_whiteTextureID); + } foreach (GLuint id, _fileTextureIDs) { glDeleteTextures(1, &id); } @@ -66,6 +74,20 @@ GLuint TextureCache::getPermutationNormalTextureID() { return _permutationNormalTextureID; } +GLuint TextureCache::getWhiteTextureID() { + if (_whiteTextureID == 0) { + glGenTextures(1, &_whiteTextureID); + glBindTexture(GL_TEXTURE_2D, _whiteTextureID); + + const char OPAQUE_WHITE[] = { 0xFF, 0xFF, 0xFF, 0xFF }; + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, OPAQUE_WHITE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + glBindTexture(GL_TEXTURE_2D, 0); + } + return _whiteTextureID; +} + GLuint TextureCache::getFileTextureID(const QString& filename) { GLuint id = _fileTextureIDs.value(filename); if (id == 0) { diff --git a/interface/src/renderer/TextureCache.h b/interface/src/renderer/TextureCache.h index 4136d23d86..acd3c70bd8 100644 --- a/interface/src/renderer/TextureCache.h +++ b/interface/src/renderer/TextureCache.h @@ -37,6 +37,9 @@ public: /// the second, a set of random unit vectors to be used as noise gradients. GLuint getPermutationNormalTextureID(); + /// Returns the ID of an opaque white texture (useful for a default). + GLuint getWhiteTextureID(); + /// Returns the ID of a texture containing the contents of the specified file, loading it if necessary. GLuint getFileTextureID(const QString& filename); @@ -65,7 +68,8 @@ private: QOpenGLFramebufferObject* createFramebufferObject(); GLuint _permutationNormalTextureID; - + GLuint _whiteTextureID; + QHash _fileTextureIDs; QHash > _networkTextures; From b04ed72b830cc721da54c6b1958c586bf4633819 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 15 Oct 2013 16:17:56 -0700 Subject: [PATCH 10/13] I forgot that the children are in reverse order. --- interface/src/renderer/FBXReader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index 19b502503f..c5dfc905cb 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -772,7 +772,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) continue; } Material material = materials.value(childID); - FBXMeshPart& part = mesh.parts[partIndex++]; + FBXMeshPart& part = mesh.parts[mesh.parts.size() - ++partIndex]; part.diffuseColor = material.diffuse; part.specularColor = material.specular; part.shininess = material.shininess; From 8631cc32642e1d70527003914f9c7d08e1815845 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 15 Oct 2013 16:29:28 -0700 Subject: [PATCH 11/13] Try to apply the blendshape scale. --- interface/src/renderer/FBXReader.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index c5dfc905cb..b2d193d106 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -712,6 +712,15 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) FBXMesh& mesh = meshes[meshID]; mesh.blendshapes.resize(max(mesh.blendshapes.size(), index.first + 1)); mesh.blendshapes[index.first] = extracted.blendshape; + + // apply scale if non-unity + if (index.second != 1.0f) { + FBXBlendshape& blendshape = mesh.blendshapes[index.first]; + for (int i = 0; i < blendshape.vertices.size(); i++) { + blendshape.vertices[i] *= index.second; + blendshape.normals[i] *= index.second; + } + } } // get offset transform from mapping From 6a265eacf26a3317ebb4580325dd565f9d45fdb1 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 15 Oct 2013 16:40:35 -0700 Subject: [PATCH 12/13] Keep normal and dilatable textures in separate maps to fix crash on manly "man" model. --- interface/src/renderer/TextureCache.cpp | 17 +++++++++++++---- interface/src/renderer/TextureCache.h | 3 ++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/interface/src/renderer/TextureCache.cpp b/interface/src/renderer/TextureCache.cpp index c20826989c..cbab1938dd 100644 --- a/interface/src/renderer/TextureCache.cpp +++ b/interface/src/renderer/TextureCache.cpp @@ -107,10 +107,19 @@ GLuint TextureCache::getFileTextureID(const QString& filename) { } QSharedPointer TextureCache::getTexture(const QUrl& url, bool dilatable) { - QSharedPointer texture = _networkTextures.value(url); - if (texture.isNull()) { - texture = QSharedPointer(dilatable ? new DilatableNetworkTexture(url) : new NetworkTexture(url)); - _networkTextures.insert(url, texture); + QSharedPointer texture; + if (dilatable) { + texture = _dilatableNetworkTextures.value(url); + if (texture.isNull()) { + texture = QSharedPointer(new DilatableNetworkTexture(url)); + _dilatableNetworkTextures.insert(url, texture); + } + } else { + texture = _networkTextures.value(url); + if (texture.isNull()) { + texture = QSharedPointer(new NetworkTexture(url)); + _networkTextures.insert(url, texture); + } } return texture; } diff --git a/interface/src/renderer/TextureCache.h b/interface/src/renderer/TextureCache.h index acd3c70bd8..7ebd4528fb 100644 --- a/interface/src/renderer/TextureCache.h +++ b/interface/src/renderer/TextureCache.h @@ -73,7 +73,8 @@ private: QHash _fileTextureIDs; QHash > _networkTextures; - + QHash > _dilatableNetworkTextures; + GLuint _primaryDepthTextureID; QOpenGLFramebufferObject* _primaryFramebufferObject; QOpenGLFramebufferObject* _secondaryFramebufferObject; From a79c0a9244b636139af7ccb79a06e5161c94b520 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 15 Oct 2013 17:14:20 -0700 Subject: [PATCH 13/13] Removed unused shader. --- interface/resources/shaders/eye.vert | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100644 interface/resources/shaders/eye.vert diff --git a/interface/resources/shaders/eye.vert b/interface/resources/shaders/eye.vert deleted file mode 100644 index aa26b1326f..0000000000 --- a/interface/resources/shaders/eye.vert +++ /dev/null @@ -1,28 +0,0 @@ -#version 120 - -// -// eye.vert -// vertex shader -// -// Created by Andrzej Kapolka on 9/25/13. -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// - -// the interpolated normal -varying vec4 normal; - -void main(void) { - - // transform and store the normal for interpolation - normal = normalize(gl_ModelViewMatrix * vec4(gl_Normal, 0.0)); - - // compute standard diffuse lighting per-vertex - gl_FrontColor = vec4(gl_Color.rgb * (gl_LightModel.ambient.rgb + gl_LightSource[0].ambient.rgb + - gl_LightSource[0].diffuse.rgb * max(0.0, dot(normal, gl_LightSource[0].position))), gl_Color.a); - - // pass along the texture coordinate - gl_TexCoord[0] = gl_MultiTexCoord0; - - // use standard pipeline transform - gl_Position = ftransform(); -}