From 41f73b5e9d4dfaa2a87bea47ba0df16207fa2199 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 23 Oct 2013 15:16:29 -0700 Subject: [PATCH 1/4] Support for attaching models to joints. --- interface/src/renderer/FBXReader.cpp | 42 ++++++++++++++++++++++++++++ interface/src/renderer/FBXReader.h | 14 ++++++++++ interface/src/renderer/Model.cpp | 38 ++++++++++++++++++++++++- interface/src/renderer/Model.h | 4 ++- 4 files changed, 96 insertions(+), 2 deletions(-) diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index 8a6abb4844..fb58b007e1 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -397,6 +398,19 @@ glm::vec3 getVec3(const QVariantList& properties, int index) { properties.at(index + 2).value()); } +glm::vec3 parseVec3(const QString& string) { + QStringList elements = string.split(','); + if (elements.isEmpty()) { + return glm::vec3(); + } + glm::vec3 value; + for (int i = 0; i < 3; i++) { + // duplicate last value if there aren't three elements + value[i] = elements.at(min(i, elements.size() - 1)).trimmed().toFloat(); + } + return value; +} + const char* FACESHIFT_BLENDSHAPES[] = { "EyeBlink_L", "EyeBlink_R", @@ -1129,6 +1143,34 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) geometry.meshes.append(mesh); } + // process attachments + QVariantHash attachments = mapping.value("attach").toHash(); + for (QVariantHash::const_iterator it = attachments.constBegin(); it != attachments.constEnd(); it++) { + FBXAttachment attachment; + attachment.jointIndex = modelIDs.indexOf(it.key()); + attachment.scale = glm::vec3(1.0f, 1.0f, 1.0f); + + QVariantList properties = it->toList(); + if (properties.isEmpty()) { + attachment.url = it->toUrl(); + } else { + attachment.url = properties.at(0).toString(); + + if (properties.size() >= 2) { + attachment.translation = parseVec3(properties.at(1).toString()); + + if (properties.size() >= 3) { + attachment.rotation = glm::quat(glm::radians(parseVec3(properties.at(2).toString()))); + + if (properties.size() >= 4) { + attachment.scale = parseVec3(properties.at(3).toString()); + } + } + } + } + geometry.attachments.append(attachment); + } + return geometry; } diff --git a/interface/src/renderer/FBXReader.h b/interface/src/renderer/FBXReader.h index 17f743ec64..4a16ba19b9 100644 --- a/interface/src/renderer/FBXReader.h +++ b/interface/src/renderer/FBXReader.h @@ -9,6 +9,7 @@ #ifndef __interface__FBXReader__ #define __interface__FBXReader__ +#include #include #include #include @@ -98,6 +99,17 @@ public: QVector, 4> > vertexConnections; }; +/// An attachment to an FBX document. +class FBXAttachment { +public: + + int jointIndex; + QUrl url; + glm::vec3 translation; + glm::quat rotation; + glm::vec3 scale; +}; + /// A set of meshes extracted from an FBX document. class FBXGeometry { public: @@ -117,6 +129,8 @@ public: int headJointIndex; glm::vec3 neckPivot; + + QVector attachments; }; /// Reads FBX geometry from the supplied model and mapping data. diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 39f1df1654..ba43a0b214 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -13,7 +13,8 @@ using namespace std; -Model::Model() : +Model::Model(QObject* parent) : + QObject(parent), _pupilDilation(0.0f) { // we may have been created in the network thread, but we live in the main thread @@ -56,6 +57,10 @@ void Model::init() { void Model::reset() { _resetStates = true; + + foreach (Model* attachment, _attachments) { + attachment->reset(); + } } void Model::simulate(float deltaTime) { @@ -81,6 +86,13 @@ void Model::simulate(float deltaTime) { } _meshStates.append(state); } + foreach (const FBXAttachment& attachment, geometry.attachments) { + Model* model = new Model(this); + model->init(); + model->setURL(attachment.url); + model->setScale(attachment.scale); + _attachments.append(model); + } _resetStates = true; } @@ -89,6 +101,22 @@ void Model::simulate(float deltaTime) { updateJointState(i); } + // update the attachment transforms and simulate them + for (int i = 0; i < _attachments.size(); i++) { + const FBXAttachment& attachment = geometry.attachments.at(i); + Model* model = _attachments.at(i); + + glm::vec3 jointTranslation = _translation; + glm::quat jointRotation = _rotation; + getJointPosition(attachment.jointIndex, jointTranslation); + getJointRotation(attachment.jointIndex, jointRotation); + + model->setTranslation(jointTranslation + jointRotation * attachment.translation); + model->setRotation(jointRotation * attachment.rotation); + + model->simulate(deltaTime); + } + for (int i = 0; i < _meshStates.size(); i++) { MeshState& state = _meshStates[i]; const FBXMesh& mesh = geometry.meshes.at(i); @@ -175,6 +203,10 @@ void Model::simulate(float deltaTime) { } bool Model::render(float alpha) { + // render the attachments + foreach (Model* attachment, _attachments) { + attachment->render(alpha); + } if (_meshStates.isEmpty()) { return false; } @@ -443,6 +475,10 @@ bool Model::getJointRotation(int jointIndex, glm::quat& rotation) const { } void Model::deleteGeometry() { + foreach (Model* attachment, _attachments) { + delete attachment; + } + _attachments.clear(); foreach (GLuint id, _blendedVertexBufferIDs) { glDeleteBuffers(1, &id); } diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 19864c3b31..be1e49a8be 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -23,7 +23,7 @@ class Model : public QObject { public: - Model(); + Model(QObject* parent = NULL); virtual ~Model(); void setTranslation(const glm::vec3& translation) { _translation = translation; } @@ -126,6 +126,8 @@ private: QVector _blendedVertices; QVector _blendedNormals; + QVector _attachments; + static ProgramObject _program; static ProgramObject _skinProgram; static int _clusterMatricesLocation; From a9df8a6b2955a3e5d5e5942c69b716f2c4e289b2 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 23 Oct 2013 15:56:09 -0700 Subject: [PATCH 2/4] Partial fix for textures in exports from 3D Studio Max. --- interface/src/renderer/FBXReader.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index fb58b007e1..e899d847af 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -902,11 +902,12 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) foreach (const FBXNode& connection, child.children) { if (connection.name == "C" || connection.name == "Connect") { if (connection.properties.at(0) == "OP") { - if (connection.properties.at(3) == "DiffuseColor") { + QByteArray type = connection.properties.at(3).toByteArray().toLower(); + if (type.contains("diffuse")) { diffuseTextures.insert(connection.properties.at(2).toString(), connection.properties.at(1).toString()); - } else if (connection.properties.at(3) == "Bump") { + } else if (type.contains("bump")) { bumpTextures.insert(connection.properties.at(2).toString(), connection.properties.at(1).toString()); } @@ -1035,6 +1036,13 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) QString diffuseTextureID = diffuseTextures.value(childID); if (!diffuseTextureID.isNull()) { part.diffuseFilename = textureFilenames.value(diffuseTextureID); + + // FBX files generated by 3DSMax have an intermediate texture parent, apparently + foreach (const QString& childTextureID, childMap.values(diffuseTextureID)) { + if (textureFilenames.contains(childTextureID)) { + part.diffuseFilename = textureFilenames.value(childTextureID); + } + } } QString bumpTextureID = bumpTextures.value(childID); if (!bumpTextureID.isNull()) { From 240f65b7c513c15e20a01b2b46fff9dd9f9436fc Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 23 Oct 2013 16:15:03 -0700 Subject: [PATCH 3/4] Scale fixes. --- interface/src/avatar/SkeletonModel.cpp | 3 ++- interface/src/renderer/Model.cpp | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 9ccc63d587..c76100b316 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -66,7 +66,8 @@ bool SkeletonModel::render(float alpha) { glm::vec3 parentPosition; getJointPosition(parentIndex, parentPosition); const float STICK_RADIUS = BALL_RADIUS * 0.5f; - Avatar::renderJointConnectingCone(parentPosition, position, STICK_RADIUS, STICK_RADIUS); + Avatar::renderJointConnectingCone(parentPosition, position, STICK_RADIUS * _owningAvatar->getScale(), + STICK_RADIUS * _owningAvatar->getScale()); } Model::render(alpha); diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index ba43a0b214..c1222a8796 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -90,7 +90,6 @@ void Model::simulate(float deltaTime) { Model* model = new Model(this); model->init(); model->setURL(attachment.url); - model->setScale(attachment.scale); _attachments.append(model); } _resetStates = true; @@ -111,8 +110,9 @@ void Model::simulate(float deltaTime) { getJointPosition(attachment.jointIndex, jointTranslation); getJointRotation(attachment.jointIndex, jointRotation); - model->setTranslation(jointTranslation + jointRotation * attachment.translation); + model->setTranslation(jointTranslation + jointRotation * attachment.translation * _scale); model->setRotation(jointRotation * attachment.rotation); + model->setScale(_scale * attachment.scale); model->simulate(deltaTime); } From bae4a176050d94c633a1a7d1f6398113f6e26f88 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 24 Oct 2013 13:59:04 -0700 Subject: [PATCH 4/4] Voxel attachments. --- interface/resources/shaders/model.frag | 4 +- interface/resources/shaders/model.vert | 5 +- interface/resources/shaders/skin_model.vert | 5 +- interface/src/renderer/FBXReader.cpp | 103 ++++++++++++++++++++ interface/src/renderer/FBXReader.h | 4 + interface/src/renderer/GeometryCache.cpp | 52 +++++----- interface/src/renderer/Model.cpp | 28 +++++- 7 files changed, 170 insertions(+), 31 deletions(-) diff --git a/interface/resources/shaders/model.frag b/interface/resources/shaders/model.frag index 7358c7dba9..12b81485e5 100644 --- a/interface/resources/shaders/model.frag +++ b/interface/resources/shaders/model.frag @@ -17,8 +17,8 @@ varying vec4 normal; void main(void) { // 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)); + vec4 base = gl_Color * (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)); diff --git a/interface/resources/shaders/model.vert b/interface/resources/shaders/model.vert index 16893dfc0b..b9c1a35e11 100644 --- a/interface/resources/shaders/model.vert +++ b/interface/resources/shaders/model.vert @@ -16,7 +16,10 @@ void main(void) { // transform and store the normal for interpolation normal = normalize(gl_ModelViewMatrix * vec4(gl_Normal, 0.0)); - // pass along the texture coordinate + // pass along the vertex color + gl_FrontColor = gl_Color; + + // and the texture coordinates gl_TexCoord[0] = gl_MultiTexCoord0; // use standard pipeline transform diff --git a/interface/resources/shaders/skin_model.vert b/interface/resources/shaders/skin_model.vert index d968d17df0..950283107f 100644 --- a/interface/resources/shaders/skin_model.vert +++ b/interface/resources/shaders/skin_model.vert @@ -31,7 +31,10 @@ void main(void) { position = gl_ModelViewProjectionMatrix * position; normal = normalize(gl_ModelViewMatrix * normal); - // pass along the texture coordinate + // pass along the vertex color + gl_FrontColor = gl_Color; + + // and the texture coordinates gl_TexCoord[0] = gl_MultiTexCoord0; gl_Position = position; diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index e899d847af..fa5da82686 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -18,6 +18,10 @@ #include #include +#include + +#include + #include "FBXReader.h" #include "Util.h" @@ -1192,3 +1196,102 @@ FBXGeometry readFBX(const QByteArray& model, const QByteArray& mapping) { return extractFBXGeometry(parseFBX(&modelBuffer), parseMapping(&mappingBuffer)); } +bool addMeshVoxelsOperation(VoxelNode* node, void* extraData) { + if (!node->isLeaf()) { + return true; + } + FBXMesh& mesh = *static_cast(extraData); + FBXMeshPart& part = mesh.parts[0]; + + const int FACE_COUNT = 6; + const int VERTICES_PER_FACE = 4; + const int VERTEX_COUNT = FACE_COUNT * VERTICES_PER_FACE; + const float EIGHT_BIT_MAXIMUM = 255.0f; + glm::vec3 color = glm::vec3(node->getColor()[0], node->getColor()[1], node->getColor()[2]) / EIGHT_BIT_MAXIMUM; + for (int i = 0; i < VERTEX_COUNT; i++) { + part.quadIndices.append(part.quadIndices.size()); + mesh.colors.append(color); + } + glm::vec3 corner = node->getCorner(); + float scale = node->getScale(); + + mesh.vertices.append(glm::vec3(corner.x, corner.y, corner.z)); + mesh.vertices.append(glm::vec3(corner.x, corner.y, corner.z + scale)); + mesh.vertices.append(glm::vec3(corner.x, corner.y + scale, corner.z + scale)); + mesh.vertices.append(glm::vec3(corner.x, corner.y + scale, corner.z)); + for (int i = 0; i < VERTICES_PER_FACE; i++) { + mesh.normals.append(glm::vec3(-1.0f, 0.0f, 0.0f)); + } + + mesh.vertices.append(glm::vec3(corner.x + scale, corner.y, corner.z)); + mesh.vertices.append(glm::vec3(corner.x + scale, corner.y + scale, corner.z)); + mesh.vertices.append(glm::vec3(corner.x + scale, corner.y + scale, corner.z + scale)); + mesh.vertices.append(glm::vec3(corner.x + scale, corner.y, corner.z + scale)); + for (int i = 0; i < VERTICES_PER_FACE; i++) { + mesh.normals.append(glm::vec3(1.0f, 0.0f, 0.0f)); + } + + mesh.vertices.append(glm::vec3(corner.x + scale, corner.y, corner.z)); + mesh.vertices.append(glm::vec3(corner.x + scale, corner.y, corner.z + scale)); + mesh.vertices.append(glm::vec3(corner.x, corner.y, corner.z + scale)); + mesh.vertices.append(glm::vec3(corner.x, corner.y, corner.z)); + for (int i = 0; i < VERTICES_PER_FACE; i++) { + mesh.normals.append(glm::vec3(0.0f, -1.0f, 0.0f)); + } + + mesh.vertices.append(glm::vec3(corner.x, corner.y + scale, corner.z)); + mesh.vertices.append(glm::vec3(corner.x, corner.y + scale, corner.z + scale)); + mesh.vertices.append(glm::vec3(corner.x + scale, corner.y + scale, corner.z + scale)); + mesh.vertices.append(glm::vec3(corner.x + scale, corner.y + scale, corner.z)); + for (int i = 0; i < VERTICES_PER_FACE; i++) { + mesh.normals.append(glm::vec3(0.0f, 1.0f, 0.0f)); + } + + mesh.vertices.append(glm::vec3(corner.x, corner.y + scale, corner.z)); + mesh.vertices.append(glm::vec3(corner.x + scale, corner.y + scale, corner.z)); + mesh.vertices.append(glm::vec3(corner.x + scale, corner.y, corner.z)); + mesh.vertices.append(glm::vec3(corner.x, corner.y, corner.z)); + for (int i = 0; i < VERTICES_PER_FACE; i++) { + mesh.normals.append(glm::vec3(0.0f, 0.0f, -1.0f)); + } + + mesh.vertices.append(glm::vec3(corner.x, corner.y, corner.z + scale)); + mesh.vertices.append(glm::vec3(corner.x + scale, corner.y, corner.z + scale)); + mesh.vertices.append(glm::vec3(corner.x + scale, corner.y + scale, corner.z + scale)); + mesh.vertices.append(glm::vec3(corner.x, corner.y + scale, corner.z + scale)); + for (int i = 0; i < VERTICES_PER_FACE; i++) { + mesh.normals.append(glm::vec3(0.0f, 0.0f, 1.0f)); + } + + return true; +} + +FBXGeometry readSVO(const QByteArray& model) { + FBXGeometry geometry; + + // we have one joint + FBXJoint joint = { -1 }; + geometry.joints.append(joint); + + // and one mesh with one cluster and one part + FBXMesh mesh; + mesh.isEye = false; + mesh.springiness = 0.0f; + + FBXCluster cluster = { 0 }; + mesh.clusters.append(cluster); + + FBXMeshPart part; + part.diffuseColor = glm::vec3(1.0f, 1.0f, 1.0f); + part.shininess = 96.0f; + mesh.parts.append(part); + + VoxelTree tree; + ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS); + tree.readBitstreamToTree((unsigned char*)model.data(), model.size(), args); + tree.recurseTreeWithOperation(addMeshVoxelsOperation, &mesh); + + geometry.meshes.append(mesh); + + return geometry; +} diff --git a/interface/src/renderer/FBXReader.h b/interface/src/renderer/FBXReader.h index 4a16ba19b9..557574316e 100644 --- a/interface/src/renderer/FBXReader.h +++ b/interface/src/renderer/FBXReader.h @@ -84,6 +84,7 @@ public: QVector vertices; QVector normals; + QVector colors; QVector texCoords; QVector clusterIndices; QVector clusterWeights; @@ -137,4 +138,7 @@ public: /// \exception QString if an error occurs in parsing FBXGeometry readFBX(const QByteArray& model, const QByteArray& mapping); +/// Reads SVO geometry from the supplied model data. +FBXGeometry readSVO(const QByteArray& model); + #endif /* defined(__interface__FBXReader__) */ diff --git a/interface/src/renderer/GeometryCache.cpp b/interface/src/renderer/GeometryCache.cpp index ea3f8fa1fc..7e85c1c895 100644 --- a/interface/src/renderer/GeometryCache.cpp +++ b/interface/src/renderer/GeometryCache.cpp @@ -348,7 +348,7 @@ void NetworkGeometry::maybeReadModelWithMapping() { } try { - _geometry = readFBX(model, mapping); + _geometry = url.path().toLower().endsWith(".svo") ? readSVO(model) : readFBX(model, mapping); } catch (const QString& error) { qDebug() << "Error reading " << url << ": " << error << "\n"; @@ -395,35 +395,43 @@ void NetworkGeometry::maybeReadModelWithMapping() { // 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) + (mesh.clusterIndices.size() + - mesh.clusterWeights.size()) * sizeof(glm::vec4), NULL, GL_STATIC_DRAW); + int normalsOffset = mesh.vertices.size() * sizeof(glm::vec3); + int colorsOffset = normalsOffset + mesh.normals.size() * sizeof(glm::vec3); + int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3); + int clusterIndicesOffset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2); + int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4); + glBufferData(GL_ARRAY_BUFFER, clusterWeightsOffset + 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), + glBufferSubData(GL_ARRAY_BUFFER, normalsOffset, mesh.normals.size() * sizeof(glm::vec3), mesh.normals.constData()); + glBufferSubData(GL_ARRAY_BUFFER, colorsOffset, mesh.colors.size() * sizeof(glm::vec3), mesh.colors.constData()); + glBufferSubData(GL_ARRAY_BUFFER, texCoordsOffset, mesh.texCoords.size() * sizeof(glm::vec2), + mesh.texCoords.constData()); + glBufferSubData(GL_ARRAY_BUFFER, clusterIndicesOffset, 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()); + glBufferSubData(GL_ARRAY_BUFFER, clusterWeightsOffset, 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), + int texCoordsOffset = mesh.colors.size() * sizeof(glm::vec3); + int clusterIndicesOffset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2); + int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4); + glBufferData(GL_ARRAY_BUFFER, clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4), + NULL, GL_STATIC_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, mesh.colors.size() * sizeof(glm::vec3), mesh.colors.constData()); + glBufferSubData(GL_ARRAY_BUFFER, texCoordsOffset, mesh.texCoords.size() * sizeof(glm::vec2), mesh.texCoords.constData()); + glBufferSubData(GL_ARRAY_BUFFER, clusterIndicesOffset, mesh.clusterIndices.size() * sizeof(glm::vec4), + mesh.clusterIndices.constData()); + glBufferSubData(GL_ARRAY_BUFFER, clusterWeightsOffset, mesh.clusterWeights.size() * sizeof(glm::vec4), mesh.clusterWeights.constData()); } else { - 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()); + int texCoordsOffset = mesh.colors.size() * sizeof(glm::vec3); + glBufferData(GL_ARRAY_BUFFER, texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2), NULL, GL_STATIC_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, mesh.colors.size() * sizeof(glm::vec3), mesh.colors.constData()); + glBufferSubData(GL_ARRAY_BUFFER, texCoordsOffset, mesh.texCoords.size() * sizeof(glm::vec2), + mesh.texCoords.constData()); } glBindBuffer(GL_ARRAY_BUFFER, 0); diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index c1222a8796..5bcc0520b9 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -234,7 +234,6 @@ bool Model::render(float alpha) { glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); glDisable(GL_COLOR_MATERIAL); @@ -253,8 +252,8 @@ bool Model::render(float alpha) { _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); + int offset = mesh.colors.size() * sizeof(glm::vec3) + mesh.texCoords.size() * 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); @@ -271,10 +270,13 @@ bool Model::render(float alpha) { } if (mesh.blendshapes.isEmpty() && mesh.springiness == 0.0f) { - glTexCoordPointer(2, GL_FLOAT, 0, (void*)(vertexCount * 2 * sizeof(glm::vec3))); + glColorPointer(3, GL_FLOAT, 0, (void*)(vertexCount * 2 * sizeof(glm::vec3))); + glTexCoordPointer(2, GL_FLOAT, 0, (void*)(vertexCount * 2 * sizeof(glm::vec3) + + mesh.colors.size() * sizeof(glm::vec3))); } else { - glTexCoordPointer(2, GL_FLOAT, 0, 0); + glColorPointer(3, GL_FLOAT, 0, 0); + glTexCoordPointer(2, GL_FLOAT, 0, (void*)(mesh.colors.size() * sizeof(glm::vec3))); glBindBuffer(GL_ARRAY_BUFFER, _blendedVertexBufferIDs.at(i)); if (!state.worldSpaceVertices.isEmpty()) { @@ -313,6 +315,15 @@ bool Model::render(float alpha) { glVertexPointer(3, GL_FLOAT, 0, 0); glNormalPointer(GL_FLOAT, 0, (void*)(vertexCount * sizeof(glm::vec3))); + if (!mesh.colors.isEmpty()) { + glEnableClientState(GL_COLOR_ARRAY); + } else { + glColor3f(1.0f, 1.0f, 1.0f); + } + if (!mesh.texCoords.isEmpty()) { + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + } + qint64 offset = 0; for (int j = 0; j < networkMesh.parts.size(); j++) { const NetworkMeshPart& networkPart = networkMesh.parts.at(j); @@ -343,6 +354,13 @@ bool Model::render(float alpha) { offset += part.triangleIndices.size() * sizeof(int); } + if (!mesh.colors.isEmpty()) { + glDisableClientState(GL_COLOR_ARRAY); + } + if (!mesh.texCoords.isEmpty()) { + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } + if (state.worldSpaceVertices.isEmpty()) { if (state.clusterMatrices.size() > 1) { _skinProgram.disableAttributeArray(_clusterIndicesLocation);