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/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/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index 8a6abb4844..fa5da82686 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -17,6 +18,10 @@ #include #include +#include + +#include + #include "FBXReader.h" #include "Util.h" @@ -397,6 +402,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", @@ -888,11 +906,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()); } @@ -1021,6 +1040,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()) { @@ -1129,6 +1155,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; } @@ -1142,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 17f743ec64..557574316e 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 @@ -83,6 +84,7 @@ public: QVector vertices; QVector normals; + QVector colors; QVector texCoords; QVector clusterIndices; QVector clusterWeights; @@ -98,6 +100,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,10 +130,15 @@ public: int headJointIndex; glm::vec3 neckPivot; + + QVector attachments; }; /// Reads FBX geometry from the supplied model and mapping data. /// \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 39f1df1654..5bcc0520b9 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,12 @@ 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); + _attachments.append(model); + } _resetStates = true; } @@ -89,6 +100,23 @@ 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 * _scale); + model->setRotation(jointRotation * attachment.rotation); + model->setScale(_scale * attachment.scale); + + 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; } @@ -202,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); @@ -221,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); @@ -239,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()) { @@ -281,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); @@ -311,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); @@ -443,6 +493,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; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 670cbd7487..dc5a9c96d3 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -118,11 +118,8 @@ int AvatarData::getBroadcastData(unsigned char* destinationBuffer) { setSemiNibbleAt(bitItems,KEY_STATE_START_BIT,_keyState); // hand state setSemiNibbleAt(bitItems,HAND_STATE_START_BIT,_handState); - *destinationBuffer++ = bitItems; - - bitItems = 0; + // faceshift state if (_headData->_isFaceshiftConnected) { setAtBit(bitItems, IS_FACESHIFT_CONNECTED); } - *destinationBuffer++ = bitItems; // If it is connected, pack up the data @@ -249,7 +246,6 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) { // hand state, stored as a semi-nibble in the bitItems _handState = getSemiNibbleAt(bitItems,HAND_STATE_START_BIT); - bitItems = (unsigned char)*sourceBuffer++; _headData->_isFaceshiftConnected = oneAtBit(bitItems, IS_FACESHIFT_CONNECTED); // If it is connected, pack up the data diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 2f9ca6483d..04930c5f6c 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -27,11 +27,9 @@ #include "HandData.h" // First bitset -const int KEY_STATE_START_BIT = 3; // 4th and 5th bits -const int HAND_STATE_START_BIT = 5; // 6th and 7th bits - -// Second bitset -const int IS_FACESHIFT_CONNECTED = 0; +const int KEY_STATE_START_BIT = 0; // 1st and 2nd bits +const int HAND_STATE_START_BIT = 2; // 3rd and 4th bits +const int IS_FACESHIFT_CONNECTED = 4; // 5th bit const float MAX_AUDIO_LOUDNESS = 1000.0; // close enough for mouth animation diff --git a/libraries/voxel-server-library/src/VoxelSendThread.cpp b/libraries/voxel-server-library/src/VoxelSendThread.cpp index bd78c507fd..3ecbd6434e 100644 --- a/libraries/voxel-server-library/src/VoxelSendThread.cpp +++ b/libraries/voxel-server-library/src/VoxelSendThread.cpp @@ -223,10 +223,11 @@ void VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* no int clientMaxPacketsPerInterval = nodeData->getMaxVoxelPacketsPerSecond() / INTERVALS_PER_SECOND; int maxPacketsPerInterval = std::max(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval()); - -printf("deepestLevelVoxelDistributor()... packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n", - packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(), - nodeData->getMaxVoxelPacketsPerSecond(), clientMaxPacketsPerInterval); + if (_myServer->wantsDebugVoxelSending()) { + printf("packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n", + packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(), + nodeData->getMaxVoxelPacketsPerSecond(), clientMaxPacketsPerInterval); + } while (packetsSentThisInterval < maxPacketsPerInterval - (shouldSendEnvironments ? 1 : 0)) { // Check to see if we're taking too long, and if so bail early... diff --git a/libraries/voxels/src/VoxelQuery.cpp b/libraries/voxels/src/VoxelQuery.cpp new file mode 100644 index 0000000000..507c086619 --- /dev/null +++ b/libraries/voxels/src/VoxelQuery.cpp @@ -0,0 +1,131 @@ +// +// VoxelQuery.cpp +// hifi +// +// Created by Brad Hefta-Gaub on 10/24/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#include +#include +#include + +#include +#include +#include +#include +#include "VoxelConstants.h" + +#include "VoxelQuery.h" + +using namespace std; + +static const float fingerVectorRadix = 4; // bits of precision when converting from float<->fixed + +VoxelQuery::VoxelQuery(Node* owningNode) : + NodeData(owningNode), + _uuid(), + _cameraPosition(0,0,0), + _cameraOrientation(), + _cameraFov(0.0f), + _cameraAspectRatio(0.0f), + _cameraNearClip(0.0f), + _cameraFarClip(0.0f), + _wantColor(true), + _wantDelta(true), + _wantLowResMoving(true), + _wantOcclusionCulling(true), + _maxVoxelPPS(DEFAULT_MAX_VOXEL_PPS) +{ + +} + +VoxelQuery::~VoxelQuery() { +} + +int VoxelQuery::getBroadcastData(unsigned char* destinationBuffer) { + unsigned char* bufferStart = destinationBuffer; + + // TODO: DRY this up to a shared method + // that can pack any type given the number of bytes + // and return the number of bytes to push the pointer + + // UUID + QByteArray uuidByteArray = _uuid.toRfc4122(); + memcpy(destinationBuffer, uuidByteArray.constData(), uuidByteArray.size()); + destinationBuffer += uuidByteArray.size(); + + // camera details + memcpy(destinationBuffer, &_cameraPosition, sizeof(_cameraPosition)); + destinationBuffer += sizeof(_cameraPosition); + destinationBuffer += packOrientationQuatToBytes(destinationBuffer, _cameraOrientation); + destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _cameraFov); + destinationBuffer += packFloatRatioToTwoByte(destinationBuffer, _cameraAspectRatio); + destinationBuffer += packClipValueToTwoByte(destinationBuffer, _cameraNearClip); + destinationBuffer += packClipValueToTwoByte(destinationBuffer, _cameraFarClip); + memcpy(destinationBuffer, &_cameraEyeOffsetPosition, sizeof(_cameraEyeOffsetPosition)); + destinationBuffer += sizeof(_cameraEyeOffsetPosition); + + // bitMask of less than byte wide items + unsigned char bitItems = 0; + if (_wantLowResMoving) { setAtBit(bitItems, WANT_LOW_RES_MOVING_BIT); } + if (_wantColor) { setAtBit(bitItems, WANT_COLOR_AT_BIT); } + if (_wantDelta) { setAtBit(bitItems, WANT_DELTA_AT_BIT); } + if (_wantOcclusionCulling) { setAtBit(bitItems, WANT_OCCLUSION_CULLING_BIT); } + + *destinationBuffer++ = bitItems; + + // desired Max Voxel PPS + memcpy(destinationBuffer, &_maxVoxelPPS, sizeof(_maxVoxelPPS)); + destinationBuffer += sizeof(_maxVoxelPPS); + + return destinationBuffer - bufferStart; +} + +// called on the other nodes - assigns it to my views of the others +int VoxelQuery::parseData(unsigned char* sourceBuffer, int numBytes) { + + // increment to push past the packet header + int numBytesPacketHeader = numBytesForPacketHeader(sourceBuffer); + sourceBuffer += numBytesPacketHeader; + + unsigned char* startPosition = sourceBuffer; + + // push past the node session UUID + sourceBuffer += NUM_BYTES_RFC4122_UUID; + + // user UUID + _uuid = QUuid::fromRfc4122(QByteArray((char*) sourceBuffer, NUM_BYTES_RFC4122_UUID)); + sourceBuffer += NUM_BYTES_RFC4122_UUID; + + // camera details + memcpy(&_cameraPosition, sourceBuffer, sizeof(_cameraPosition)); + sourceBuffer += sizeof(_cameraPosition); + sourceBuffer += unpackOrientationQuatFromBytes(sourceBuffer, _cameraOrientation); + sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &_cameraFov); + sourceBuffer += unpackFloatRatioFromTwoByte(sourceBuffer,_cameraAspectRatio); + sourceBuffer += unpackClipValueFromTwoByte(sourceBuffer,_cameraNearClip); + sourceBuffer += unpackClipValueFromTwoByte(sourceBuffer,_cameraFarClip); + memcpy(&_cameraEyeOffsetPosition, sourceBuffer, sizeof(_cameraEyeOffsetPosition)); + sourceBuffer += sizeof(_cameraEyeOffsetPosition); + + // voxel sending features... + unsigned char bitItems = 0; + bitItems = (unsigned char)*sourceBuffer++; + _wantLowResMoving = oneAtBit(bitItems, WANT_LOW_RES_MOVING_BIT); + _wantColor = oneAtBit(bitItems, WANT_COLOR_AT_BIT); + _wantDelta = oneAtBit(bitItems, WANT_DELTA_AT_BIT); + _wantOcclusionCulling = oneAtBit(bitItems, WANT_OCCLUSION_CULLING_BIT); + + // desired Max Voxel PPS + memcpy(&_maxVoxelPPS, sourceBuffer, sizeof(_maxVoxelPPS)); + sourceBuffer += sizeof(_maxVoxelPPS); + + return sourceBuffer - startPosition; +} + +glm::vec3 VoxelQuery::calculateCameraDirection() const { + glm::vec3 direction = glm::vec3(_cameraOrientation * glm::vec4(IDENTITY_FRONT, 0.0f)); + return direction; +} + diff --git a/libraries/voxels/src/VoxelQuery.h b/libraries/voxels/src/VoxelQuery.h new file mode 100644 index 0000000000..1573e54061 --- /dev/null +++ b/libraries/voxels/src/VoxelQuery.h @@ -0,0 +1,105 @@ +// +// VoxelQuery.h +// hifi +// +// Created by Stephen Birarda on 4/9/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#ifndef __hifi__VoxelQuery__ +#define __hifi__VoxelQuery__ + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#include + +// First bitset +const int WANT_LOW_RES_MOVING_BIT = 0; +const int WANT_COLOR_AT_BIT = 1; +const int WANT_DELTA_AT_BIT = 2; +const int WANT_OCCLUSION_CULLING_BIT = 3; // 4th bit + +class VoxelQuery : public NodeData { + Q_OBJECT + +public: + VoxelQuery(Node* owningNode = NULL); + ~VoxelQuery(); + + int getBroadcastData(unsigned char* destinationBuffer); + int parseData(unsigned char* sourceBuffer, int numBytes); + + QUuid& getUUID() { return _uuid; } + void setUUID(const QUuid& uuid) { _uuid = uuid; } + + // getters for camera details + const glm::vec3& getCameraPosition() const { return _cameraPosition; } + const glm::quat& getCameraOrientation() const { return _cameraOrientation; } + float getCameraFov() const { return _cameraFov; } + float getCameraAspectRatio() const { return _cameraAspectRatio; } + float getCameraNearClip() const { return _cameraNearClip; } + float getCameraFarClip() const { return _cameraFarClip; } + const glm::vec3& getCameraEyeOffsetPosition() const { return _cameraEyeOffsetPosition; } + + glm::vec3 calculateCameraDirection() const; + + // setters for camera details + void setCameraPosition(const glm::vec3& position) { _cameraPosition = position; } + void setCameraOrientation(const glm::quat& orientation) { _cameraOrientation = orientation; } + void setCameraFov(float fov) { _cameraFov = fov; } + void setCameraAspectRatio(float aspectRatio) { _cameraAspectRatio = aspectRatio; } + void setCameraNearClip(float nearClip) { _cameraNearClip = nearClip; } + void setCameraFarClip(float farClip) { _cameraFarClip = farClip; } + void setCameraEyeOffsetPosition(const glm::vec3& eyeOffsetPosition) { _cameraEyeOffsetPosition = eyeOffsetPosition; } + + // related to Voxel Sending strategies + bool getWantColor() const { return _wantColor; } + bool getWantDelta() const { return _wantDelta; } + bool getWantLowResMoving() const { return _wantLowResMoving; } + bool getWantOcclusionCulling() const { return _wantOcclusionCulling; } + int getMaxVoxelPacketsPerSecond() const { return _maxVoxelPPS; } + +public slots: + void setWantLowResMoving(bool wantLowResMoving) { _wantLowResMoving = wantLowResMoving; } + void setWantColor(bool wantColor) { _wantColor = wantColor; } + void setWantDelta(bool wantDelta) { _wantDelta = wantDelta; } + void setWantOcclusionCulling(bool wantOcclusionCulling) { _wantOcclusionCulling = wantOcclusionCulling; } + void setMaxVoxelPacketsPerSecond(int maxVoxelPPS) { _maxVoxelPPS = maxVoxelPPS; } + +protected: + QUuid _uuid; + + // camera details for the avatar + glm::vec3 _cameraPosition; + glm::quat _cameraOrientation; + float _cameraFov; + float _cameraAspectRatio; + float _cameraNearClip; + float _cameraFarClip; + glm::vec3 _cameraEyeOffsetPosition; + + // voxel server sending items + bool _wantColor; + bool _wantDelta; + bool _wantLowResMoving; + bool _wantOcclusionCulling; + int _maxVoxelPPS; + +private: + // privatize the copy constructor and assignment operator so they cannot be called + VoxelQuery(const VoxelQuery&); + VoxelQuery& operator= (const VoxelQuery&); +}; + +#endif /* defined(__hifi__VoxelQuery__) */