From cec0ee0b22b9508610938cd64feb95339dffc86a Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 11 Oct 2013 17:51:29 -0700 Subject: [PATCH 01/31] 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/31] 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/31] 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/31] 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 746bce86268cbcc0e82347d075215971da32661c Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Tue, 15 Oct 2013 11:01:22 -0700 Subject: [PATCH 05/31] All onscreen text, overlay OFF when stats are off --- interface/src/Application.cpp | 83 ++++++++++++++++++++--------------- 1 file changed, 48 insertions(+), 35 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6a6a9789a3..f9424500a7 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2791,9 +2791,11 @@ void Application::displayOverlay() { } #ifndef _WIN32 - _audio.render(_glWidget->width(), _glWidget->height()); - if (Menu::getInstance()->isOptionChecked(MenuOption::Oscilloscope)) { - _audioScope.render(45, _glWidget->height() - 200); + if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { + _audio.render(_glWidget->width(), _glWidget->height()); + if (Menu::getInstance()->isOptionChecked(MenuOption::Oscilloscope)) { + _audioScope.render(45, _glWidget->height() - 200); + } } #endif @@ -2833,7 +2835,24 @@ void Application::displayOverlay() { glPointSize(1.0f); if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { + // Onscreen text about position, servers, etc displayStats(); + // Bandwidth meter + if (Menu::getInstance()->isOptionChecked(MenuOption::Bandwidth)) { + _bandwidthMeter.render(_glWidget->width(), _glWidget->height()); + } + // Stats at upper right of screen about who domain server is telling us about + glPointSize(1.0f); + char nodes[100]; + + NodeList* nodeList = NodeList::getInstance(); + int totalAvatars = 0, totalServers = 0; + + for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { + node->getType() == NODE_TYPE_AGENT ? totalAvatars++ : totalServers++; + } + sprintf(nodes, "Servers: %d, Avatars: %d\n", totalServers, totalAvatars); + drawtext(_glWidget->width() - 150, 20, 0.10, 0, 1.0, 0, nodes, 1, 0, 0); } // testing rendering coverage map @@ -2845,9 +2864,6 @@ void Application::displayOverlay() { renderCoverageMap(); } - if (Menu::getInstance()->isOptionChecked(MenuOption::Bandwidth)) { - _bandwidthMeter.render(_glWidget->width(), _glWidget->height()); - } if (Menu::getInstance()->isOptionChecked(MenuOption::Log)) { LogDisplay::instance.render(_glWidget->width(), _glWidget->height()); @@ -2867,19 +2883,6 @@ void Application::displayOverlay() { drawtext(_glWidget->width() - 102, _glWidget->height() - 22, 0.30, 0, 1.0, 0, frameTimer, 1, 1, 1); } - // Stats at upper right of screen about who domain server is telling us about - glPointSize(1.0f); - char nodes[100]; - - NodeList* nodeList = NodeList::getInstance(); - int totalAvatars = 0, totalServers = 0; - - for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { - node->getType() == NODE_TYPE_AGENT ? totalAvatars++ : totalServers++; - } - - sprintf(nodes, "Servers: %d, Avatars: %d\n", totalServers, totalAvatars); - drawtext(_glWidget->width() - 150, 20, 0.10, 0, 1.0, 0, nodes, 1, 0, 0); // render the webcam input frame _webcam.renderPreview(_glWidget->width(), _glWidget->height()); @@ -2941,11 +2944,12 @@ void Application::displayOverlay() { void Application::displayStats() { int statsVerticalOffset = 8; - + const int PELS_PER_LINE = 15; char stats[200]; + statsVerticalOffset += PELS_PER_LINE; sprintf(stats, "%3.0f FPS, %d Pkts/sec, %3.2f Mbps ", _fps, _packetsPerSecond, (float)_bytesPerSecond * 8.f / 1000000.f); - drawtext(10, statsVerticalOffset + 15, 0.10f, 0, 1.0, 0, stats); + drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, stats); if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { int pingAudio = 0, pingAvatar = 0, pingVoxel = 0, pingVoxelMax = 0; @@ -2975,14 +2979,16 @@ void Application::displayStats() { } char pingStats[200]; + statsVerticalOffset += PELS_PER_LINE; sprintf(pingStats, "Ping audio/avatar/voxel: %d / %d / %d avg %d max ", pingAudio, pingAvatar, pingVoxel, pingVoxelMax); - drawtext(10, statsVerticalOffset + 35, 0.10f, 0, 1.0, 0, pingStats); + drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, pingStats); } char avatarStats[200]; + statsVerticalOffset += PELS_PER_LINE; glm::vec3 avatarPos = _myAvatar.getPosition(); sprintf(avatarStats, "Avatar: pos %.3f, %.3f, %.3f, vel %.1f, yaw = %.2f", avatarPos.x, avatarPos.y, avatarPos.z, glm::length(_myAvatar.getVelocity()), _myAvatar.getBodyYaw()); - drawtext(10, statsVerticalOffset + 55, 0.10f, 0, 1.0, 0, avatarStats); + drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, avatarStats); std::stringstream voxelStats; @@ -2990,7 +2996,8 @@ void Application::displayStats() { voxelStats << "Voxels Rendered: " << _voxels.getVoxelsRendered() / 1000.f << "K " << "Updated: " << _voxels.getVoxelsUpdated()/1000.f << "K " << "Max: " << _voxels.getMaxVoxels()/1000.f << "K "; - drawtext(10, statsVerticalOffset + 230, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); + statsVerticalOffset += PELS_PER_LINE; + drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); voxelStats.str(""); voxelStats << @@ -3006,27 +3013,32 @@ void Application::displayStats() { //voxelStats << "VoxelNode size: " << sizeof(VoxelNode) << " bytes "; //voxelStats << "AABox size: " << sizeof(AABox) << " bytes "; - drawtext(10, statsVerticalOffset + 250, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); + statsVerticalOffset += PELS_PER_LINE; + drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); voxelStats.str(""); char* voxelDetails = _voxelSceneStats.getItemValue(VoxelSceneStats::ITEM_VOXELS); voxelStats << "Voxels Sent from Server: " << voxelDetails; - drawtext(10, statsVerticalOffset + 270, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); + statsVerticalOffset += PELS_PER_LINE; + drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); voxelStats.str(""); voxelDetails = _voxelSceneStats.getItemValue(VoxelSceneStats::ITEM_ELAPSED); voxelStats << "Scene Send Time from Server: " << voxelDetails; - drawtext(10, statsVerticalOffset + 290, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); + statsVerticalOffset += PELS_PER_LINE; + drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); voxelStats.str(""); voxelDetails = _voxelSceneStats.getItemValue(VoxelSceneStats::ITEM_ENCODE); voxelStats << "Encode Time on Server: " << voxelDetails; - drawtext(10, statsVerticalOffset + 310, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); + statsVerticalOffset += PELS_PER_LINE; + drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); voxelStats.str(""); voxelDetails = _voxelSceneStats.getItemValue(VoxelSceneStats::ITEM_MODE); voxelStats << "Sending Mode: " << voxelDetails; - drawtext(10, statsVerticalOffset + 330, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); + statsVerticalOffset += PELS_PER_LINE; + drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); Node *avatarMixer = NodeList::getInstance()->soloNodeOfType(NODE_TYPE_AVATAR_MIXER); char avatarMixerStats[200]; @@ -3038,20 +3050,21 @@ void Application::displayStats() { } else { sprintf(avatarMixerStats, "No Avatar Mixer"); } - - drawtext(10, statsVerticalOffset + 350, 0.10f, 0, 1.0, 0, avatarMixerStats); - drawtext(10, statsVerticalOffset + 450, 0.10f, 0, 1.0, 0, (char *)LeapManager::statusString().c_str()); + statsVerticalOffset += PELS_PER_LINE; + drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, avatarMixerStats); + statsVerticalOffset += PELS_PER_LINE; + drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, (char *)LeapManager::statusString().c_str()); if (_perfStatsOn) { // Get the PerfStats group details. We need to allocate and array of char* long enough to hold 1+groups char** perfStatLinesArray = new char*[PerfStat::getGroupCount()+1]; int lines = PerfStat::DumpStats(perfStatLinesArray); - int atZ = 150; // arbitrary place on screen that looks good + for (int line=0; line < lines; line++) { - drawtext(10, statsVerticalOffset + atZ, 0.10f, 0, 1.0, 0, perfStatLinesArray[line]); + statsVerticalOffset += PELS_PER_LINE; + drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, perfStatLinesArray[line]); delete perfStatLinesArray[line]; // we're responsible for cleanup perfStatLinesArray[line]=NULL; - atZ+=20; // height of a line } delete []perfStatLinesArray; // we're responsible for cleanup } From d06acdd1695e36e6f49738778d35710b1be54fd1 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 15 Oct 2013 11:50:29 -0700 Subject: [PATCH 06/31] 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 07/31] 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 08/31] 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 09/31] 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 41e1ec180cb6e5c2a9320f053e87e5452111a6d3 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 15 Oct 2013 13:45:40 -0700 Subject: [PATCH 10/31] add a JSON endpoint for current assignments --- cmake/macros/SetupHifiProject.cmake | 8 +- domain-server/CMakeLists.txt | 12 +- domain-server/external/cJSON/LICENSE | 20 + domain-server/external/cJSON/include/cJSON.h | 143 +++++ domain-server/external/cJSON/src/cJSON.c | 596 ++++++++++++++++++ .../civetweb/{src => include}/md5.inl | 0 domain-server/src/DomainServer.cpp | 54 +- domain-server/src/DomainServer.h | 2 +- 8 files changed, 823 insertions(+), 12 deletions(-) create mode 100644 domain-server/external/cJSON/LICENSE create mode 100644 domain-server/external/cJSON/include/cJSON.h create mode 100644 domain-server/external/cJSON/src/cJSON.c rename domain-server/external/civetweb/{src => include}/md5.inl (100%) diff --git a/cmake/macros/SetupHifiProject.cmake b/cmake/macros/SetupHifiProject.cmake index e828e68be2..be77d7e244 100644 --- a/cmake/macros/SetupHifiProject.cmake +++ b/cmake/macros/SetupHifiProject.cmake @@ -1,4 +1,4 @@ -MACRO(SETUP_HIFI_PROJECT TARGET INCLUDE_QT) +macro(SETUP_HIFI_PROJECT TARGET INCLUDE_QT) project(${TARGET}) # grab the implemenation and header files @@ -12,8 +12,8 @@ MACRO(SETUP_HIFI_PROJECT TARGET INCLUDE_QT) endif() endforeach() - # add the executable - add_executable(${TARGET} ${TARGET_SRCS}) + # add the executable, include additional optional sources + add_executable(${TARGET} ${TARGET_SRCS} ${ARGN}) IF (${INCLUDE_QT}) find_package(Qt5Core REQUIRED) @@ -21,4 +21,4 @@ MACRO(SETUP_HIFI_PROJECT TARGET INCLUDE_QT) ENDIF() target_link_libraries(${TARGET} ${QT_LIBRARIES}) -ENDMACRO(SETUP_HIFI_PROJECT _target _include_qt) \ No newline at end of file +endmacro() \ No newline at end of file diff --git a/domain-server/CMakeLists.txt b/domain-server/CMakeLists.txt index 3c54a2d7f0..92fa090150 100644 --- a/domain-server/CMakeLists.txt +++ b/domain-server/CMakeLists.txt @@ -6,13 +6,13 @@ set(MACRO_DIR ${ROOT_DIR}/cmake/macros) set(TARGET_NAME domain-server) include(${MACRO_DIR}/SetupHifiProject.cmake) -setup_hifi_project(${TARGET_NAME} TRUE) -# setup a library for civetweb and link it to the domain-server -FILE(GLOB CIVETWEB_SRCS external/civetweb/src/*.c) -add_library(civetweb ${CIVETWEB_SRCS}) -include_directories(external/civetweb/include) -target_link_libraries(${TARGET_NAME} civetweb) +# grab cJSON and civetweb sources to pass as OPTIONAL_SRCS +FILE(GLOB OPTIONAL_SRCS external/civetweb/src/* external/cJSON/src/*) + +setup_hifi_project(${TARGET_NAME} TRUE ${OPTIONAL_SRCS}) + +include_directories(external/civetweb/include external/cJSON/include) # remove and then copy the files for the webserver add_custom_command(TARGET ${TARGET_NAME} POST_BUILD diff --git a/domain-server/external/cJSON/LICENSE b/domain-server/external/cJSON/LICENSE new file mode 100644 index 0000000000..fa0a438e2c --- /dev/null +++ b/domain-server/external/cJSON/LICENSE @@ -0,0 +1,20 @@ + Copyright (c) 2009 Dave Gamble + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + diff --git a/domain-server/external/cJSON/include/cJSON.h b/domain-server/external/cJSON/include/cJSON.h new file mode 100644 index 0000000000..867b7c32f3 --- /dev/null +++ b/domain-server/external/cJSON/include/cJSON.h @@ -0,0 +1,143 @@ +/* + Copyright (c) 2009 Dave Gamble + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef cJSON__h +#define cJSON__h + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* cJSON Types: */ +#define cJSON_False 0 +#define cJSON_True 1 +#define cJSON_NULL 2 +#define cJSON_Number 3 +#define cJSON_String 4 +#define cJSON_Array 5 +#define cJSON_Object 6 + +#define cJSON_IsReference 256 + +/* The cJSON structure: */ +typedef struct cJSON { + struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + + int type; /* The type of the item, as above. */ + + char *valuestring; /* The item's string, if type==cJSON_String */ + int valueint; /* The item's number, if type==cJSON_Number */ + double valuedouble; /* The item's number, if type==cJSON_Number */ + + char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ +} cJSON; + +typedef struct cJSON_Hooks { + void *(*malloc_fn)(size_t sz); + void (*free_fn)(void *ptr); +} cJSON_Hooks; + +/* Supply malloc, realloc and free functions to cJSON */ +extern void cJSON_InitHooks(cJSON_Hooks* hooks); + + +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */ +extern cJSON *cJSON_Parse(const char *value); +/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */ +extern char *cJSON_Print(cJSON *item); +/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */ +extern char *cJSON_PrintUnformatted(cJSON *item); +/* Delete a cJSON entity and all subentities. */ +extern void cJSON_Delete(cJSON *c); + +/* Returns the number of items in an array (or object). */ +extern int cJSON_GetArraySize(cJSON *array); +/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */ +extern cJSON *cJSON_GetArrayItem(cJSON *array,int item); +/* Get item "string" from object. Case insensitive. */ +extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string); + +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ +extern const char *cJSON_GetErrorPtr(void); + +/* These calls create a cJSON item of the appropriate type. */ +extern cJSON *cJSON_CreateNull(void); +extern cJSON *cJSON_CreateTrue(void); +extern cJSON *cJSON_CreateFalse(void); +extern cJSON *cJSON_CreateBool(int b); +extern cJSON *cJSON_CreateNumber(double num); +extern cJSON *cJSON_CreateString(const char *string); +extern cJSON *cJSON_CreateArray(void); +extern cJSON *cJSON_CreateObject(void); + +/* These utilities create an Array of count items. */ +extern cJSON *cJSON_CreateIntArray(const int *numbers,int count); +extern cJSON *cJSON_CreateFloatArray(const float *numbers,int count); +extern cJSON *cJSON_CreateDoubleArray(const double *numbers,int count); +extern cJSON *cJSON_CreateStringArray(const char **strings,int count); + +/* Append item to the specified array/object. */ +extern void cJSON_AddItemToArray(cJSON *array, cJSON *item); +extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item); +/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ +extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item); + +/* Remove/Detatch items from Arrays/Objects. */ +extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which); +extern void cJSON_DeleteItemFromArray(cJSON *array,int which); +extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string); +extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string); + +/* Update array items. */ +extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem); +extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); + +/* Duplicate a cJSON item */ +extern cJSON *cJSON_Duplicate(cJSON *item,int recurse); +/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will +need to be released. With recurse!=0, it will duplicate any children connected to the item. +The item->next and ->prev pointers are always zero on return from Duplicate. */ + +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ +extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated); + +extern void cJSON_Minify(char *json); + +/* Macros for creating things quickly. */ +#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull()) +#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue()) +#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse()) +#define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b)) +#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n)) +#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) + +/* When assigning an integer value, it needs to be propagated to valuedouble too. */ +#define cJSON_SetIntValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val)) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/domain-server/external/cJSON/src/cJSON.c b/domain-server/external/cJSON/src/cJSON.c new file mode 100644 index 0000000000..31c43dd2e9 --- /dev/null +++ b/domain-server/external/cJSON/src/cJSON.c @@ -0,0 +1,596 @@ +/* + Copyright (c) 2009 Dave Gamble + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +/* cJSON */ +/* JSON parser in C. */ + +#include +#include +#include +#include +#include +#include +#include +#include "cJSON.h" + +static const char *ep; + +const char *cJSON_GetErrorPtr(void) {return ep;} + +static int cJSON_strcasecmp(const char *s1,const char *s2) +{ + if (!s1) return (s1==s2)?0:1;if (!s2) return 1; + for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0; + return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2); +} + +static void *(*cJSON_malloc)(size_t sz) = malloc; +static void (*cJSON_free)(void *ptr) = free; + +static char* cJSON_strdup(const char* str) +{ + size_t len; + char* copy; + + len = strlen(str) + 1; + if (!(copy = (char*)cJSON_malloc(len))) return 0; + memcpy(copy,str,len); + return copy; +} + +void cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (!hooks) { /* Reset hooks */ + cJSON_malloc = malloc; + cJSON_free = free; + return; + } + + cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc; + cJSON_free = (hooks->free_fn)?hooks->free_fn:free; +} + +/* Internal constructor. */ +static cJSON *cJSON_New_Item(void) +{ + cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON)); + if (node) memset(node,0,sizeof(cJSON)); + return node; +} + +/* Delete a cJSON structure. */ +void cJSON_Delete(cJSON *c) +{ + cJSON *next; + while (c) + { + next=c->next; + if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child); + if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring); + if (c->string) cJSON_free(c->string); + cJSON_free(c); + c=next; + } +} + +/* Parse the input text to generate a number, and populate the result into item. */ +static const char *parse_number(cJSON *item,const char *num) +{ + double n=0,sign=1,scale=0;int subscale=0,signsubscale=1; + + if (*num=='-') sign=-1,num++; /* Has sign? */ + if (*num=='0') num++; /* is zero */ + if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */ + if (*num=='.' && num[1]>='0' && num[1]<='9') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} /* Fractional part? */ + if (*num=='e' || *num=='E') /* Exponent? */ + { num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */ + while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */ + } + + n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */ + + item->valuedouble=n; + item->valueint=(int)n; + item->type=cJSON_Number; + return num; +} + +/* Render the number nicely from the given item into a string. */ +static char *print_number(cJSON *item) +{ + char *str; + double d=item->valuedouble; + if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN) + { + str=(char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */ + if (str) sprintf(str,"%d",item->valueint); + } + else + { + str=(char*)cJSON_malloc(64); /* This is a nice tradeoff. */ + if (str) + { + if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60)sprintf(str,"%.0f",d); + else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d); + else sprintf(str,"%f",d); + } + } + return str; +} + +static unsigned parse_hex4(const char *str) +{ + unsigned h=0; + if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; + h=h<<4;str++; + if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; + h=h<<4;str++; + if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; + h=h<<4;str++; + if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; + return h; +} + +/* Parse the input text into an unescaped cstring, and populate item. */ +static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; +static const char *parse_string(cJSON *item,const char *str) +{ + const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2; + if (*str!='\"') {ep=str;return 0;} /* not a string! */ + + while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */ + + out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */ + if (!out) return 0; + + ptr=str+1;ptr2=out; + while (*ptr!='\"' && *ptr) + { + if (*ptr!='\\') *ptr2++=*ptr++; + else + { + ptr++; + switch (*ptr) + { + case 'b': *ptr2++='\b'; break; + case 'f': *ptr2++='\f'; break; + case 'n': *ptr2++='\n'; break; + case 'r': *ptr2++='\r'; break; + case 't': *ptr2++='\t'; break; + case 'u': /* transcode utf16 to utf8. */ + uc=parse_hex4(ptr+1);ptr+=4; /* get the unicode char. */ + + if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; /* check for invalid. */ + + if (uc>=0xD800 && uc<=0xDBFF) /* UTF16 surrogate pairs. */ + { + if (ptr[1]!='\\' || ptr[2]!='u') break; /* missing second-half of surrogate. */ + uc2=parse_hex4(ptr+3);ptr+=6; + if (uc2<0xDC00 || uc2>0xDFFF) break; /* invalid second-half of surrogate. */ + uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF)); + } + + len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len; + + switch (len) { + case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 1: *--ptr2 =(uc | firstByteMark[len]); + } + ptr2+=len; + break; + default: *ptr2++=*ptr; break; + } + ptr++; + } + } + *ptr2=0; + if (*ptr=='\"') ptr++; + item->valuestring=out; + item->type=cJSON_String; + return ptr; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static char *print_string_ptr(const char *str) +{ + const char *ptr;char *ptr2,*out;int len=0;unsigned char token; + + if (!str) return cJSON_strdup(""); + ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;} + + out=(char*)cJSON_malloc(len+3); + if (!out) return 0; + + ptr2=out;ptr=str; + *ptr2++='\"'; + while (*ptr) + { + if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++; + else + { + *ptr2++='\\'; + switch (token=*ptr++) + { + case '\\': *ptr2++='\\'; break; + case '\"': *ptr2++='\"'; break; + case '\b': *ptr2++='b'; break; + case '\f': *ptr2++='f'; break; + case '\n': *ptr2++='n'; break; + case '\r': *ptr2++='r'; break; + case '\t': *ptr2++='t'; break; + default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */ + } + } + } + *ptr2++='\"';*ptr2++=0; + return out; +} +/* Invote print_string_ptr (which is useful) on an item. */ +static char *print_string(cJSON *item) {return print_string_ptr(item->valuestring);} + +/* Predeclare these prototypes. */ +static const char *parse_value(cJSON *item,const char *value); +static char *print_value(cJSON *item,int depth,int fmt); +static const char *parse_array(cJSON *item,const char *value); +static char *print_array(cJSON *item,int depth,int fmt); +static const char *parse_object(cJSON *item,const char *value); +static char *print_object(cJSON *item,int depth,int fmt); + +/* Utility to jump whitespace and cr/lf */ +static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;} + +/* Parse an object - create a new root, and populate. */ +cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated) +{ + const char *end=0; + cJSON *c=cJSON_New_Item(); + ep=0; + if (!c) return 0; /* memory fail */ + + end=parse_value(c,skip(value)); + if (!end) {cJSON_Delete(c);return 0;} /* parse failure. ep is set. */ + + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ + if (require_null_terminated) {end=skip(end);if (*end) {cJSON_Delete(c);ep=end;return 0;}} + if (return_parse_end) *return_parse_end=end; + return c; +} +/* Default options for cJSON_Parse */ +cJSON *cJSON_Parse(const char *value) {return cJSON_ParseWithOpts(value,0,0);} + +/* Render a cJSON item/entity/structure to text. */ +char *cJSON_Print(cJSON *item) {return print_value(item,0,1);} +char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0);} + +/* Parser core - when encountering text, process appropriately. */ +static const char *parse_value(cJSON *item,const char *value) +{ + if (!value) return 0; /* Fail on null. */ + if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; } + if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; } + if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; } + if (*value=='\"') { return parse_string(item,value); } + if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); } + if (*value=='[') { return parse_array(item,value); } + if (*value=='{') { return parse_object(item,value); } + + ep=value;return 0; /* failure. */ +} + +/* Render a value to text. */ +static char *print_value(cJSON *item,int depth,int fmt) +{ + char *out=0; + if (!item) return 0; + switch ((item->type)&255) + { + case cJSON_NULL: out=cJSON_strdup("null"); break; + case cJSON_False: out=cJSON_strdup("false");break; + case cJSON_True: out=cJSON_strdup("true"); break; + case cJSON_Number: out=print_number(item);break; + case cJSON_String: out=print_string(item);break; + case cJSON_Array: out=print_array(item,depth,fmt);break; + case cJSON_Object: out=print_object(item,depth,fmt);break; + } + return out; +} + +/* Build an array from input text. */ +static const char *parse_array(cJSON *item,const char *value) +{ + cJSON *child; + if (*value!='[') {ep=value;return 0;} /* not an array! */ + + item->type=cJSON_Array; + value=skip(value+1); + if (*value==']') return value+1; /* empty array. */ + + item->child=child=cJSON_New_Item(); + if (!item->child) return 0; /* memory fail */ + value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */ + if (!value) return 0; + + while (*value==',') + { + cJSON *new_item; + if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */ + child->next=new_item;new_item->prev=child;child=new_item; + value=skip(parse_value(child,skip(value+1))); + if (!value) return 0; /* memory fail */ + } + + if (*value==']') return value+1; /* end of array */ + ep=value;return 0; /* malformed. */ +} + +/* Render an array to text */ +static char *print_array(cJSON *item,int depth,int fmt) +{ + char **entries; + char *out=0,*ptr,*ret;int len=5; + cJSON *child=item->child; + int numentries=0,i=0,fail=0; + + /* How many entries in the array? */ + while (child) numentries++,child=child->next; + /* Explicitly handle numentries==0 */ + if (!numentries) + { + out=(char*)cJSON_malloc(3); + if (out) strcpy(out,"[]"); + return out; + } + /* Allocate an array to hold the values for each */ + entries=(char**)cJSON_malloc(numentries*sizeof(char*)); + if (!entries) return 0; + memset(entries,0,numentries*sizeof(char*)); + /* Retrieve all the results: */ + child=item->child; + while (child && !fail) + { + ret=print_value(child,depth+1,fmt); + entries[i++]=ret; + if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1; + child=child->next; + } + + /* If we didn't fail, try to malloc the output string */ + if (!fail) out=(char*)cJSON_malloc(len); + /* If that fails, we fail. */ + if (!out) fail=1; + + /* Handle failure. */ + if (fail) + { + for (i=0;itype=cJSON_Object; + value=skip(value+1); + if (*value=='}') return value+1; /* empty array. */ + + item->child=child=cJSON_New_Item(); + if (!item->child) return 0; + value=skip(parse_string(child,skip(value))); + if (!value) return 0; + child->string=child->valuestring;child->valuestring=0; + if (*value!=':') {ep=value;return 0;} /* fail! */ + value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ + if (!value) return 0; + + while (*value==',') + { + cJSON *new_item; + if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */ + child->next=new_item;new_item->prev=child;child=new_item; + value=skip(parse_string(child,skip(value+1))); + if (!value) return 0; + child->string=child->valuestring;child->valuestring=0; + if (*value!=':') {ep=value;return 0;} /* fail! */ + value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ + if (!value) return 0; + } + + if (*value=='}') return value+1; /* end of array */ + ep=value;return 0; /* malformed. */ +} + +/* Render an object to text. */ +static char *print_object(cJSON *item,int depth,int fmt) +{ + char **entries=0,**names=0; + char *out=0,*ptr,*ret,*str;int len=7,i=0,j; + cJSON *child=item->child; + int numentries=0,fail=0; + /* Count the number of entries. */ + while (child) numentries++,child=child->next; + /* Explicitly handle empty object case */ + if (!numentries) + { + out=(char*)cJSON_malloc(fmt?depth+4:3); + if (!out) return 0; + ptr=out;*ptr++='{'; + if (fmt) {*ptr++='\n';for (i=0;ichild;depth++;if (fmt) len+=depth; + while (child) + { + names[i]=str=print_string_ptr(child->string); + entries[i++]=ret=print_value(child,depth,fmt); + if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1; + child=child->next; + } + + /* Try to allocate the output string */ + if (!fail) out=(char*)cJSON_malloc(len); + if (!out) fail=1; + + /* Handle failure */ + if (fail) + { + for (i=0;ichild;int i=0;while(c)i++,c=c->next;return i;} +cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;} +cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;} + +/* Utility for array list handling. */ +static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;} +/* Utility for handling references. */ +static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;} + +/* Add item to array/object. */ +void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}} +void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);} +void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));} +void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));} + +cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0; + if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;} +void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));} +cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;} +void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));} + +/* Replace array/object items with new ones. */ +void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return; + newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem; + if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);} +void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}} + +/* Create basic types: */ +cJSON *cJSON_CreateNull(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;} +cJSON *cJSON_CreateTrue(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;} +cJSON *cJSON_CreateFalse(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;} +cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;} +cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;} +cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;} +cJSON *cJSON_CreateArray(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;} +cJSON *cJSON_CreateObject(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;} + +/* Create Arrays: */ +cJSON *cJSON_CreateIntArray(const int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} +cJSON *cJSON_CreateFloatArray(const float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} +cJSON *cJSON_CreateDoubleArray(const double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} +cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} + +/* Duplication */ +cJSON *cJSON_Duplicate(cJSON *item,int recurse) +{ + cJSON *newitem,*cptr,*nptr=0,*newchild; + /* Bail on bad ptr */ + if (!item) return 0; + /* Create new item */ + newitem=cJSON_New_Item(); + if (!newitem) return 0; + /* Copy over all vars */ + newitem->type=item->type&(~cJSON_IsReference),newitem->valueint=item->valueint,newitem->valuedouble=item->valuedouble; + if (item->valuestring) {newitem->valuestring=cJSON_strdup(item->valuestring); if (!newitem->valuestring) {cJSON_Delete(newitem);return 0;}} + if (item->string) {newitem->string=cJSON_strdup(item->string); if (!newitem->string) {cJSON_Delete(newitem);return 0;}} + /* If non-recursive, then we're done! */ + if (!recurse) return newitem; + /* Walk the ->next chain for the child. */ + cptr=item->child; + while (cptr) + { + newchild=cJSON_Duplicate(cptr,1); /* Duplicate (with recurse) each item in the ->next chain */ + if (!newchild) {cJSON_Delete(newitem);return 0;} + if (nptr) {nptr->next=newchild,newchild->prev=nptr;nptr=newchild;} /* If newitem->child already set, then crosswire ->prev and ->next and move on */ + else {newitem->child=newchild;nptr=newchild;} /* Set newitem->child and move to it */ + cptr=cptr->next; + } + return newitem; +} + +void cJSON_Minify(char *json) +{ + char *into=json; + while (*json) + { + if (*json==' ') json++; + else if (*json=='\t') json++; // Whitespace characters. + else if (*json=='\r') json++; + else if (*json=='\n') json++; + else if (*json=='/' && json[1]=='/') while (*json && *json!='\n') json++; // double-slash comments, to end of line. + else if (*json=='/' && json[1]=='*') {while (*json && !(*json=='*' && json[1]=='/')) json++;json+=2;} // multiline comments. + else if (*json=='\"'){*into++=*json++;while (*json && *json!='\"'){if (*json=='\\') *into++=*json++;*into++=*json++;}*into++=*json++;} // string literals, which are \" sensitive. + else *into++=*json++; // All other characters. + } + *into=0; // and null-terminate. +} \ No newline at end of file diff --git a/domain-server/external/civetweb/src/md5.inl b/domain-server/external/civetweb/include/md5.inl similarity index 100% rename from domain-server/external/civetweb/src/md5.inl rename to domain-server/external/civetweb/include/md5.inl diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 62dfea3857..d2bea2a3a1 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -6,6 +6,7 @@ // Copyright (c) 2013 HighFidelity, Inc. All rights reserved. // +#include #include #include @@ -14,6 +15,8 @@ #include #include +#include "cJSON.h" + #include "DomainServer.h" DomainServer* DomainServer::domainServerInstance = NULL; @@ -30,12 +33,61 @@ void DomainServer::setDomainServerInstance(DomainServer* domainServer) { int DomainServer::civetwebRequestHandler(struct mg_connection *connection) { const struct mg_request_info* ri = mg_get_request_info(connection); + const char TWO_HUNDRED_RESPONSE[] = "HTTP/1.0 200 OK\r\n\r\n"; + if (strcmp(ri->uri, "/assignment") == 0 && strcmp(ri->request_method, "POST") == 0) { // return a 200 - mg_printf(connection, "%s", "HTTP/1.0 200 OK\r\n\r\n"); + mg_printf(connection, "%s", TWO_HUNDRED_RESPONSE); // upload the file mg_upload(connection, "/tmp"); + return 1; + } else if (strcmp(ri->uri, "/assignments.json") == 0) { + // user is asking for json list of assignments + + // start with a 200 response + mg_printf(connection, "%s", TWO_HUNDRED_RESPONSE); + + // setup the JSON + cJSON *assignedNodesJSON = cJSON_CreateObject(); + + // enumerate the NodeList to find the assigned nodes + NodeList* nodeList = NodeList::getInstance(); + + for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { + if (node->getLinkedData()) { + // this is a node with assignment + cJSON *assignedNodeJSON = cJSON_CreateObject(); + + // add the assignment UUID + QString assignmentUUID = uuidStringWithoutCurlyBraces(((Assignment*) node->getLinkedData())->getUUID()); + cJSON_AddStringToObject(assignedNodeJSON, "UUID", assignmentUUID.toLocal8Bit().constData()); + + cJSON *nodePublicSocketJSON = cJSON_CreateObject(); + + // add the public socket information + sockaddr_in* nodePublicSocket = (sockaddr_in*) node->getPublicSocket(); + cJSON_AddStringToObject(nodePublicSocketJSON, "ip", inet_ntoa(nodePublicSocket->sin_addr)); + cJSON_AddNumberToObject(nodePublicSocketJSON, "port", nodePublicSocket->sin_port); + + cJSON_AddItemToObject(assignedNodeJSON, "public", nodePublicSocketJSON); + + // re-format the type name so it matches the target name + QString nodeTypeName(node->getTypeName()); + nodeTypeName = nodeTypeName.toLower(); + nodeTypeName.replace(' ', '-'); + + cJSON_AddItemToObject(assignedNodesJSON, nodeTypeName.toLocal8Bit().constData(), assignedNodeJSON); + } + } + + // print out the created JSON + mg_printf(connection, "%s", cJSON_Print(assignedNodesJSON)); + + // cleanup JSON + cJSON_Delete(assignedNodesJSON); + + // we've processed this request return 1; } else { // have mongoose process this request from the document_root diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 05ab3bd074..540ca67acd 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -18,7 +18,7 @@ #include #include -#include +#include "civetweb.h" const int MAX_STATIC_ASSIGNMENT_FILE_ASSIGNMENTS = 1000; From 2ec42a2a5dd972e158f44902daea3b156376ad2c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 15 Oct 2013 14:24:54 -0700 Subject: [PATCH 11/31] revise linkage of civetweb for both DS and VS --- assignment-client/CMakeLists.txt | 2 ++ cmake/macros/SetupHifiLibrary.cmake | 2 +- domain-server/CMakeLists.txt | 4 ++-- .../external => externals}/civetweb/LICENSE.md | 0 .../civetweb/include/civetweb.h | 0 .../external => externals}/civetweb/include/md5.inl | 0 .../external => externals}/civetweb/src/civetweb.c | 0 libraries/voxel-server-library/CMakeLists.txt | 13 +++++++------ libraries/voxel-server-library/src/VoxelServer.h | 4 ++-- 9 files changed, 14 insertions(+), 11 deletions(-) rename {domain-server/external => externals}/civetweb/LICENSE.md (100%) rename {domain-server/external => externals}/civetweb/include/civetweb.h (100%) rename {domain-server/external => externals}/civetweb/include/md5.inl (100%) rename {domain-server/external => externals}/civetweb/src/civetweb.c (100%) diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index ba307501b1..a374149121 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -27,6 +27,8 @@ link_hifi_library(avatars ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(voxels ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(voxel-server-library ${TARGET_NAME} ${ROOT_DIR}) +include_directories(${ROOT_DIR}/externals/civetweb/include) + # link the stk library set(STK_ROOT_DIR ${ROOT_DIR}/externals/stk) find_package(STK REQUIRED) diff --git a/cmake/macros/SetupHifiLibrary.cmake b/cmake/macros/SetupHifiLibrary.cmake index 79bf16e1a0..217be6b3a0 100644 --- a/cmake/macros/SetupHifiLibrary.cmake +++ b/cmake/macros/SetupHifiLibrary.cmake @@ -6,7 +6,7 @@ MACRO(SETUP_HIFI_LIBRARY TARGET) set(LIB_SRCS ${LIB_SRCS} ${WRAPPED_SRCS}) # create a library and set the property so it can be referenced later - add_library(${TARGET} ${LIB_SRCS}) + add_library(${TARGET} ${LIB_SRCS} ${ARGN}) find_package(Qt5Core REQUIRED) qt5_use_modules(${TARGET} Core) diff --git a/domain-server/CMakeLists.txt b/domain-server/CMakeLists.txt index 92fa090150..46319819b6 100644 --- a/domain-server/CMakeLists.txt +++ b/domain-server/CMakeLists.txt @@ -8,11 +8,11 @@ set(TARGET_NAME domain-server) include(${MACRO_DIR}/SetupHifiProject.cmake) # grab cJSON and civetweb sources to pass as OPTIONAL_SRCS -FILE(GLOB OPTIONAL_SRCS external/civetweb/src/* external/cJSON/src/*) +FILE(GLOB OPTIONAL_SRCS ${ROOT_DIR}/externals/civetweb/src/* external/cJSON/src/*) setup_hifi_project(${TARGET_NAME} TRUE ${OPTIONAL_SRCS}) -include_directories(external/civetweb/include external/cJSON/include) +include_directories(${ROOT_DIR}/externals/civetweb/include external/cJSON/include) # remove and then copy the files for the webserver add_custom_command(TARGET ${TARGET_NAME} POST_BUILD diff --git a/domain-server/external/civetweb/LICENSE.md b/externals/civetweb/LICENSE.md similarity index 100% rename from domain-server/external/civetweb/LICENSE.md rename to externals/civetweb/LICENSE.md diff --git a/domain-server/external/civetweb/include/civetweb.h b/externals/civetweb/include/civetweb.h similarity index 100% rename from domain-server/external/civetweb/include/civetweb.h rename to externals/civetweb/include/civetweb.h diff --git a/domain-server/external/civetweb/include/md5.inl b/externals/civetweb/include/md5.inl similarity index 100% rename from domain-server/external/civetweb/include/md5.inl rename to externals/civetweb/include/md5.inl diff --git a/domain-server/external/civetweb/src/civetweb.c b/externals/civetweb/src/civetweb.c similarity index 100% rename from domain-server/external/civetweb/src/civetweb.c rename to externals/civetweb/src/civetweb.c diff --git a/libraries/voxel-server-library/CMakeLists.txt b/libraries/voxel-server-library/CMakeLists.txt index 1298ef1bae..2239731f3c 100644 --- a/libraries/voxel-server-library/CMakeLists.txt +++ b/libraries/voxel-server-library/CMakeLists.txt @@ -11,18 +11,19 @@ set(TARGET_NAME voxel-server-library) find_package(Qt5Widgets REQUIRED) include(${MACRO_DIR}/SetupHifiLibrary.cmake) -setup_hifi_library(${TARGET_NAME}) + +# grab cJSON and civetweb sources to pass as OPTIONAL_SRCS +FILE(GLOB OPTIONAL_SRCS ${ROOT_DIR}/externals/civetweb/src/*) + +setup_hifi_library(${TARGET_NAME} ${OPTIONAL_SRCS}) + +include_directories(${ROOT_DIR}/externals/civetweb/include) qt5_use_modules(${TARGET_NAME} Widgets) include(${MACRO_DIR}/IncludeGLM.cmake) include_glm(${TARGET_NAME} ${ROOT_DIR}) -# setup a library for civetweb and link it to the voxel-server-library -# this assumes that the domain-server cmake has already correctly set up the civetweb library -include_directories(../../domain-server/external/civetweb/include) -target_link_libraries(${TARGET_NAME} civetweb) - include(${MACRO_DIR}/LinkHifiLibrary.cmake) link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR}) diff --git a/libraries/voxel-server-library/src/VoxelServer.h b/libraries/voxel-server-library/src/VoxelServer.h index a52abaec07..282943e28d 100644 --- a/libraries/voxel-server-library/src/VoxelServer.h +++ b/libraries/voxel-server-library/src/VoxelServer.h @@ -10,14 +10,14 @@ #ifndef __voxel_server__VoxelServer__ #define __voxel_server__VoxelServer__ -#include "../../domain-server/external/civetweb/include/civetweb.h" - #include #include #include #include +#include "civetweb.h" + #include "NodeWatcher.h" #include "VoxelPersistThread.h" #include "VoxelSendThread.h" From 4cb657fa24fa8f1b3743da8f706c723cca9ab41d Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 15 Oct 2013 15:29:51 -0700 Subject: [PATCH 12/31] remove dependency on cJSON and use Qt instead --- domain-server/CMakeLists.txt | 4 +- domain-server/external/cJSON/LICENSE | 20 - domain-server/external/cJSON/include/cJSON.h | 143 ----- domain-server/external/cJSON/src/cJSON.c | 596 ------------------- domain-server/src/DomainServer.cpp | 32 +- 5 files changed, 19 insertions(+), 776 deletions(-) delete mode 100644 domain-server/external/cJSON/LICENSE delete mode 100644 domain-server/external/cJSON/include/cJSON.h delete mode 100644 domain-server/external/cJSON/src/cJSON.c diff --git a/domain-server/CMakeLists.txt b/domain-server/CMakeLists.txt index 46319819b6..35f7bcc8d6 100644 --- a/domain-server/CMakeLists.txt +++ b/domain-server/CMakeLists.txt @@ -8,11 +8,11 @@ set(TARGET_NAME domain-server) include(${MACRO_DIR}/SetupHifiProject.cmake) # grab cJSON and civetweb sources to pass as OPTIONAL_SRCS -FILE(GLOB OPTIONAL_SRCS ${ROOT_DIR}/externals/civetweb/src/* external/cJSON/src/*) +FILE(GLOB OPTIONAL_SRCS ${ROOT_DIR}/externals/civetweb/src/*) setup_hifi_project(${TARGET_NAME} TRUE ${OPTIONAL_SRCS}) -include_directories(${ROOT_DIR}/externals/civetweb/include external/cJSON/include) +include_directories(${ROOT_DIR}/externals/civetweb/include) # remove and then copy the files for the webserver add_custom_command(TARGET ${TARGET_NAME} POST_BUILD diff --git a/domain-server/external/cJSON/LICENSE b/domain-server/external/cJSON/LICENSE deleted file mode 100644 index fa0a438e2c..0000000000 --- a/domain-server/external/cJSON/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ - Copyright (c) 2009 Dave Gamble - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - diff --git a/domain-server/external/cJSON/include/cJSON.h b/domain-server/external/cJSON/include/cJSON.h deleted file mode 100644 index 867b7c32f3..0000000000 --- a/domain-server/external/cJSON/include/cJSON.h +++ /dev/null @@ -1,143 +0,0 @@ -/* - Copyright (c) 2009 Dave Gamble - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. -*/ - -#ifndef cJSON__h -#define cJSON__h - -#ifdef __cplusplus -extern "C" -{ -#endif - -/* cJSON Types: */ -#define cJSON_False 0 -#define cJSON_True 1 -#define cJSON_NULL 2 -#define cJSON_Number 3 -#define cJSON_String 4 -#define cJSON_Array 5 -#define cJSON_Object 6 - -#define cJSON_IsReference 256 - -/* The cJSON structure: */ -typedef struct cJSON { - struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ - struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ - - int type; /* The type of the item, as above. */ - - char *valuestring; /* The item's string, if type==cJSON_String */ - int valueint; /* The item's number, if type==cJSON_Number */ - double valuedouble; /* The item's number, if type==cJSON_Number */ - - char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ -} cJSON; - -typedef struct cJSON_Hooks { - void *(*malloc_fn)(size_t sz); - void (*free_fn)(void *ptr); -} cJSON_Hooks; - -/* Supply malloc, realloc and free functions to cJSON */ -extern void cJSON_InitHooks(cJSON_Hooks* hooks); - - -/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */ -extern cJSON *cJSON_Parse(const char *value); -/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */ -extern char *cJSON_Print(cJSON *item); -/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */ -extern char *cJSON_PrintUnformatted(cJSON *item); -/* Delete a cJSON entity and all subentities. */ -extern void cJSON_Delete(cJSON *c); - -/* Returns the number of items in an array (or object). */ -extern int cJSON_GetArraySize(cJSON *array); -/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */ -extern cJSON *cJSON_GetArrayItem(cJSON *array,int item); -/* Get item "string" from object. Case insensitive. */ -extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string); - -/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ -extern const char *cJSON_GetErrorPtr(void); - -/* These calls create a cJSON item of the appropriate type. */ -extern cJSON *cJSON_CreateNull(void); -extern cJSON *cJSON_CreateTrue(void); -extern cJSON *cJSON_CreateFalse(void); -extern cJSON *cJSON_CreateBool(int b); -extern cJSON *cJSON_CreateNumber(double num); -extern cJSON *cJSON_CreateString(const char *string); -extern cJSON *cJSON_CreateArray(void); -extern cJSON *cJSON_CreateObject(void); - -/* These utilities create an Array of count items. */ -extern cJSON *cJSON_CreateIntArray(const int *numbers,int count); -extern cJSON *cJSON_CreateFloatArray(const float *numbers,int count); -extern cJSON *cJSON_CreateDoubleArray(const double *numbers,int count); -extern cJSON *cJSON_CreateStringArray(const char **strings,int count); - -/* Append item to the specified array/object. */ -extern void cJSON_AddItemToArray(cJSON *array, cJSON *item); -extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item); -/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ -extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); -extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item); - -/* Remove/Detatch items from Arrays/Objects. */ -extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which); -extern void cJSON_DeleteItemFromArray(cJSON *array,int which); -extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string); -extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string); - -/* Update array items. */ -extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem); -extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); - -/* Duplicate a cJSON item */ -extern cJSON *cJSON_Duplicate(cJSON *item,int recurse); -/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will -need to be released. With recurse!=0, it will duplicate any children connected to the item. -The item->next and ->prev pointers are always zero on return from Duplicate. */ - -/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ -extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated); - -extern void cJSON_Minify(char *json); - -/* Macros for creating things quickly. */ -#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull()) -#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue()) -#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse()) -#define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b)) -#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n)) -#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) - -/* When assigning an integer value, it needs to be propagated to valuedouble too. */ -#define cJSON_SetIntValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val)) - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/domain-server/external/cJSON/src/cJSON.c b/domain-server/external/cJSON/src/cJSON.c deleted file mode 100644 index 31c43dd2e9..0000000000 --- a/domain-server/external/cJSON/src/cJSON.c +++ /dev/null @@ -1,596 +0,0 @@ -/* - Copyright (c) 2009 Dave Gamble - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. -*/ - -/* cJSON */ -/* JSON parser in C. */ - -#include -#include -#include -#include -#include -#include -#include -#include "cJSON.h" - -static const char *ep; - -const char *cJSON_GetErrorPtr(void) {return ep;} - -static int cJSON_strcasecmp(const char *s1,const char *s2) -{ - if (!s1) return (s1==s2)?0:1;if (!s2) return 1; - for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0; - return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2); -} - -static void *(*cJSON_malloc)(size_t sz) = malloc; -static void (*cJSON_free)(void *ptr) = free; - -static char* cJSON_strdup(const char* str) -{ - size_t len; - char* copy; - - len = strlen(str) + 1; - if (!(copy = (char*)cJSON_malloc(len))) return 0; - memcpy(copy,str,len); - return copy; -} - -void cJSON_InitHooks(cJSON_Hooks* hooks) -{ - if (!hooks) { /* Reset hooks */ - cJSON_malloc = malloc; - cJSON_free = free; - return; - } - - cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc; - cJSON_free = (hooks->free_fn)?hooks->free_fn:free; -} - -/* Internal constructor. */ -static cJSON *cJSON_New_Item(void) -{ - cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON)); - if (node) memset(node,0,sizeof(cJSON)); - return node; -} - -/* Delete a cJSON structure. */ -void cJSON_Delete(cJSON *c) -{ - cJSON *next; - while (c) - { - next=c->next; - if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child); - if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring); - if (c->string) cJSON_free(c->string); - cJSON_free(c); - c=next; - } -} - -/* Parse the input text to generate a number, and populate the result into item. */ -static const char *parse_number(cJSON *item,const char *num) -{ - double n=0,sign=1,scale=0;int subscale=0,signsubscale=1; - - if (*num=='-') sign=-1,num++; /* Has sign? */ - if (*num=='0') num++; /* is zero */ - if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */ - if (*num=='.' && num[1]>='0' && num[1]<='9') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} /* Fractional part? */ - if (*num=='e' || *num=='E') /* Exponent? */ - { num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */ - while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */ - } - - n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */ - - item->valuedouble=n; - item->valueint=(int)n; - item->type=cJSON_Number; - return num; -} - -/* Render the number nicely from the given item into a string. */ -static char *print_number(cJSON *item) -{ - char *str; - double d=item->valuedouble; - if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN) - { - str=(char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */ - if (str) sprintf(str,"%d",item->valueint); - } - else - { - str=(char*)cJSON_malloc(64); /* This is a nice tradeoff. */ - if (str) - { - if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60)sprintf(str,"%.0f",d); - else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d); - else sprintf(str,"%f",d); - } - } - return str; -} - -static unsigned parse_hex4(const char *str) -{ - unsigned h=0; - if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; - h=h<<4;str++; - if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; - h=h<<4;str++; - if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; - h=h<<4;str++; - if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; - return h; -} - -/* Parse the input text into an unescaped cstring, and populate item. */ -static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; -static const char *parse_string(cJSON *item,const char *str) -{ - const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2; - if (*str!='\"') {ep=str;return 0;} /* not a string! */ - - while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */ - - out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */ - if (!out) return 0; - - ptr=str+1;ptr2=out; - while (*ptr!='\"' && *ptr) - { - if (*ptr!='\\') *ptr2++=*ptr++; - else - { - ptr++; - switch (*ptr) - { - case 'b': *ptr2++='\b'; break; - case 'f': *ptr2++='\f'; break; - case 'n': *ptr2++='\n'; break; - case 'r': *ptr2++='\r'; break; - case 't': *ptr2++='\t'; break; - case 'u': /* transcode utf16 to utf8. */ - uc=parse_hex4(ptr+1);ptr+=4; /* get the unicode char. */ - - if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; /* check for invalid. */ - - if (uc>=0xD800 && uc<=0xDBFF) /* UTF16 surrogate pairs. */ - { - if (ptr[1]!='\\' || ptr[2]!='u') break; /* missing second-half of surrogate. */ - uc2=parse_hex4(ptr+3);ptr+=6; - if (uc2<0xDC00 || uc2>0xDFFF) break; /* invalid second-half of surrogate. */ - uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF)); - } - - len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len; - - switch (len) { - case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; - case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; - case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; - case 1: *--ptr2 =(uc | firstByteMark[len]); - } - ptr2+=len; - break; - default: *ptr2++=*ptr; break; - } - ptr++; - } - } - *ptr2=0; - if (*ptr=='\"') ptr++; - item->valuestring=out; - item->type=cJSON_String; - return ptr; -} - -/* Render the cstring provided to an escaped version that can be printed. */ -static char *print_string_ptr(const char *str) -{ - const char *ptr;char *ptr2,*out;int len=0;unsigned char token; - - if (!str) return cJSON_strdup(""); - ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;} - - out=(char*)cJSON_malloc(len+3); - if (!out) return 0; - - ptr2=out;ptr=str; - *ptr2++='\"'; - while (*ptr) - { - if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++; - else - { - *ptr2++='\\'; - switch (token=*ptr++) - { - case '\\': *ptr2++='\\'; break; - case '\"': *ptr2++='\"'; break; - case '\b': *ptr2++='b'; break; - case '\f': *ptr2++='f'; break; - case '\n': *ptr2++='n'; break; - case '\r': *ptr2++='r'; break; - case '\t': *ptr2++='t'; break; - default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */ - } - } - } - *ptr2++='\"';*ptr2++=0; - return out; -} -/* Invote print_string_ptr (which is useful) on an item. */ -static char *print_string(cJSON *item) {return print_string_ptr(item->valuestring);} - -/* Predeclare these prototypes. */ -static const char *parse_value(cJSON *item,const char *value); -static char *print_value(cJSON *item,int depth,int fmt); -static const char *parse_array(cJSON *item,const char *value); -static char *print_array(cJSON *item,int depth,int fmt); -static const char *parse_object(cJSON *item,const char *value); -static char *print_object(cJSON *item,int depth,int fmt); - -/* Utility to jump whitespace and cr/lf */ -static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;} - -/* Parse an object - create a new root, and populate. */ -cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated) -{ - const char *end=0; - cJSON *c=cJSON_New_Item(); - ep=0; - if (!c) return 0; /* memory fail */ - - end=parse_value(c,skip(value)); - if (!end) {cJSON_Delete(c);return 0;} /* parse failure. ep is set. */ - - /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ - if (require_null_terminated) {end=skip(end);if (*end) {cJSON_Delete(c);ep=end;return 0;}} - if (return_parse_end) *return_parse_end=end; - return c; -} -/* Default options for cJSON_Parse */ -cJSON *cJSON_Parse(const char *value) {return cJSON_ParseWithOpts(value,0,0);} - -/* Render a cJSON item/entity/structure to text. */ -char *cJSON_Print(cJSON *item) {return print_value(item,0,1);} -char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0);} - -/* Parser core - when encountering text, process appropriately. */ -static const char *parse_value(cJSON *item,const char *value) -{ - if (!value) return 0; /* Fail on null. */ - if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; } - if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; } - if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; } - if (*value=='\"') { return parse_string(item,value); } - if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); } - if (*value=='[') { return parse_array(item,value); } - if (*value=='{') { return parse_object(item,value); } - - ep=value;return 0; /* failure. */ -} - -/* Render a value to text. */ -static char *print_value(cJSON *item,int depth,int fmt) -{ - char *out=0; - if (!item) return 0; - switch ((item->type)&255) - { - case cJSON_NULL: out=cJSON_strdup("null"); break; - case cJSON_False: out=cJSON_strdup("false");break; - case cJSON_True: out=cJSON_strdup("true"); break; - case cJSON_Number: out=print_number(item);break; - case cJSON_String: out=print_string(item);break; - case cJSON_Array: out=print_array(item,depth,fmt);break; - case cJSON_Object: out=print_object(item,depth,fmt);break; - } - return out; -} - -/* Build an array from input text. */ -static const char *parse_array(cJSON *item,const char *value) -{ - cJSON *child; - if (*value!='[') {ep=value;return 0;} /* not an array! */ - - item->type=cJSON_Array; - value=skip(value+1); - if (*value==']') return value+1; /* empty array. */ - - item->child=child=cJSON_New_Item(); - if (!item->child) return 0; /* memory fail */ - value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */ - if (!value) return 0; - - while (*value==',') - { - cJSON *new_item; - if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */ - child->next=new_item;new_item->prev=child;child=new_item; - value=skip(parse_value(child,skip(value+1))); - if (!value) return 0; /* memory fail */ - } - - if (*value==']') return value+1; /* end of array */ - ep=value;return 0; /* malformed. */ -} - -/* Render an array to text */ -static char *print_array(cJSON *item,int depth,int fmt) -{ - char **entries; - char *out=0,*ptr,*ret;int len=5; - cJSON *child=item->child; - int numentries=0,i=0,fail=0; - - /* How many entries in the array? */ - while (child) numentries++,child=child->next; - /* Explicitly handle numentries==0 */ - if (!numentries) - { - out=(char*)cJSON_malloc(3); - if (out) strcpy(out,"[]"); - return out; - } - /* Allocate an array to hold the values for each */ - entries=(char**)cJSON_malloc(numentries*sizeof(char*)); - if (!entries) return 0; - memset(entries,0,numentries*sizeof(char*)); - /* Retrieve all the results: */ - child=item->child; - while (child && !fail) - { - ret=print_value(child,depth+1,fmt); - entries[i++]=ret; - if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1; - child=child->next; - } - - /* If we didn't fail, try to malloc the output string */ - if (!fail) out=(char*)cJSON_malloc(len); - /* If that fails, we fail. */ - if (!out) fail=1; - - /* Handle failure. */ - if (fail) - { - for (i=0;itype=cJSON_Object; - value=skip(value+1); - if (*value=='}') return value+1; /* empty array. */ - - item->child=child=cJSON_New_Item(); - if (!item->child) return 0; - value=skip(parse_string(child,skip(value))); - if (!value) return 0; - child->string=child->valuestring;child->valuestring=0; - if (*value!=':') {ep=value;return 0;} /* fail! */ - value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ - if (!value) return 0; - - while (*value==',') - { - cJSON *new_item; - if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */ - child->next=new_item;new_item->prev=child;child=new_item; - value=skip(parse_string(child,skip(value+1))); - if (!value) return 0; - child->string=child->valuestring;child->valuestring=0; - if (*value!=':') {ep=value;return 0;} /* fail! */ - value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ - if (!value) return 0; - } - - if (*value=='}') return value+1; /* end of array */ - ep=value;return 0; /* malformed. */ -} - -/* Render an object to text. */ -static char *print_object(cJSON *item,int depth,int fmt) -{ - char **entries=0,**names=0; - char *out=0,*ptr,*ret,*str;int len=7,i=0,j; - cJSON *child=item->child; - int numentries=0,fail=0; - /* Count the number of entries. */ - while (child) numentries++,child=child->next; - /* Explicitly handle empty object case */ - if (!numentries) - { - out=(char*)cJSON_malloc(fmt?depth+4:3); - if (!out) return 0; - ptr=out;*ptr++='{'; - if (fmt) {*ptr++='\n';for (i=0;ichild;depth++;if (fmt) len+=depth; - while (child) - { - names[i]=str=print_string_ptr(child->string); - entries[i++]=ret=print_value(child,depth,fmt); - if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1; - child=child->next; - } - - /* Try to allocate the output string */ - if (!fail) out=(char*)cJSON_malloc(len); - if (!out) fail=1; - - /* Handle failure */ - if (fail) - { - for (i=0;ichild;int i=0;while(c)i++,c=c->next;return i;} -cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;} -cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;} - -/* Utility for array list handling. */ -static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;} -/* Utility for handling references. */ -static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;} - -/* Add item to array/object. */ -void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}} -void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);} -void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));} -void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));} - -cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0; - if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;} -void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));} -cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;} -void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));} - -/* Replace array/object items with new ones. */ -void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return; - newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem; - if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);} -void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}} - -/* Create basic types: */ -cJSON *cJSON_CreateNull(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;} -cJSON *cJSON_CreateTrue(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;} -cJSON *cJSON_CreateFalse(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;} -cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;} -cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;} -cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;} -cJSON *cJSON_CreateArray(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;} -cJSON *cJSON_CreateObject(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;} - -/* Create Arrays: */ -cJSON *cJSON_CreateIntArray(const int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} -cJSON *cJSON_CreateFloatArray(const float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} -cJSON *cJSON_CreateDoubleArray(const double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} -cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} - -/* Duplication */ -cJSON *cJSON_Duplicate(cJSON *item,int recurse) -{ - cJSON *newitem,*cptr,*nptr=0,*newchild; - /* Bail on bad ptr */ - if (!item) return 0; - /* Create new item */ - newitem=cJSON_New_Item(); - if (!newitem) return 0; - /* Copy over all vars */ - newitem->type=item->type&(~cJSON_IsReference),newitem->valueint=item->valueint,newitem->valuedouble=item->valuedouble; - if (item->valuestring) {newitem->valuestring=cJSON_strdup(item->valuestring); if (!newitem->valuestring) {cJSON_Delete(newitem);return 0;}} - if (item->string) {newitem->string=cJSON_strdup(item->string); if (!newitem->string) {cJSON_Delete(newitem);return 0;}} - /* If non-recursive, then we're done! */ - if (!recurse) return newitem; - /* Walk the ->next chain for the child. */ - cptr=item->child; - while (cptr) - { - newchild=cJSON_Duplicate(cptr,1); /* Duplicate (with recurse) each item in the ->next chain */ - if (!newchild) {cJSON_Delete(newitem);return 0;} - if (nptr) {nptr->next=newchild,newchild->prev=nptr;nptr=newchild;} /* If newitem->child already set, then crosswire ->prev and ->next and move on */ - else {newitem->child=newchild;nptr=newchild;} /* Set newitem->child and move to it */ - cptr=cptr->next; - } - return newitem; -} - -void cJSON_Minify(char *json) -{ - char *into=json; - while (*json) - { - if (*json==' ') json++; - else if (*json=='\t') json++; // Whitespace characters. - else if (*json=='\r') json++; - else if (*json=='\n') json++; - else if (*json=='/' && json[1]=='/') while (*json && *json!='\n') json++; // double-slash comments, to end of line. - else if (*json=='/' && json[1]=='*') {while (*json && !(*json=='*' && json[1]=='/')) json++;json+=2;} // multiline comments. - else if (*json=='\"'){*into++=*json++;while (*json && *json!='\"'){if (*json=='\\') *into++=*json++;*into++=*json++;}*into++=*json++;} // string literals, which are \" sensitive. - else *into++=*json++; // All other characters. - } - *into=0; // and null-terminate. -} \ No newline at end of file diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index d2bea2a3a1..36cc3a5a97 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -9,14 +9,14 @@ #include #include -#include +#include +#include +#include #include #include #include -#include "cJSON.h" - #include "DomainServer.h" DomainServer* DomainServer::domainServerInstance = NULL; @@ -49,43 +49,45 @@ int DomainServer::civetwebRequestHandler(struct mg_connection *connection) { mg_printf(connection, "%s", TWO_HUNDRED_RESPONSE); // setup the JSON - cJSON *assignedNodesJSON = cJSON_CreateObject(); + QJsonObject assignmentJSON; + + QJsonObject assignedNodesJSON; // enumerate the NodeList to find the assigned nodes NodeList* nodeList = NodeList::getInstance(); + const char ASSIGNMENT_JSON_UUID_KEY[] = "UUID"; + for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { if (node->getLinkedData()) { // this is a node with assignment - cJSON *assignedNodeJSON = cJSON_CreateObject(); + QJsonObject assignedNodeJSON; // add the assignment UUID QString assignmentUUID = uuidStringWithoutCurlyBraces(((Assignment*) node->getLinkedData())->getUUID()); - cJSON_AddStringToObject(assignedNodeJSON, "UUID", assignmentUUID.toLocal8Bit().constData()); + assignedNodeJSON[ASSIGNMENT_JSON_UUID_KEY] = assignmentUUID; - cJSON *nodePublicSocketJSON = cJSON_CreateObject(); + QJsonObject nodePublicSocketJSON; // add the public socket information sockaddr_in* nodePublicSocket = (sockaddr_in*) node->getPublicSocket(); - cJSON_AddStringToObject(nodePublicSocketJSON, "ip", inet_ntoa(nodePublicSocket->sin_addr)); - cJSON_AddNumberToObject(nodePublicSocketJSON, "port", nodePublicSocket->sin_port); + nodePublicSocketJSON["ip"] = QString(inet_ntoa(nodePublicSocket->sin_addr)); + nodePublicSocketJSON["port"] = (int) ntohs(nodePublicSocket->sin_port); - cJSON_AddItemToObject(assignedNodeJSON, "public", nodePublicSocketJSON); + assignedNodeJSON["public"] = nodePublicSocketJSON; // re-format the type name so it matches the target name QString nodeTypeName(node->getTypeName()); nodeTypeName = nodeTypeName.toLower(); nodeTypeName.replace(' ', '-'); - cJSON_AddItemToObject(assignedNodesJSON, nodeTypeName.toLocal8Bit().constData(), assignedNodeJSON); + assignedNodesJSON[nodeTypeName] = assignedNodeJSON; } } // print out the created JSON - mg_printf(connection, "%s", cJSON_Print(assignedNodesJSON)); - - // cleanup JSON - cJSON_Delete(assignedNodesJSON); + QJsonDocument assignmentDocument(assignedNodesJSON); + mg_printf(connection, "%s", assignmentDocument.toJson().constData()); // we've processed this request return 1; From 1ed2b3d8fe6738a13e1427962b1c41bdb6c76b16 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 15 Oct 2013 15:38:43 -0700 Subject: [PATCH 13/31] add both fulfilled and queued assignments to DS json --- domain-server/src/DomainServer.cpp | 30 +++++++++++++++++++++++++---- libraries/shared/src/Assignment.cpp | 15 +++++++++++++++ libraries/shared/src/Assignment.h | 2 ++ 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 36cc3a5a97..270608d07c 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -33,11 +33,11 @@ void DomainServer::setDomainServerInstance(DomainServer* domainServer) { int DomainServer::civetwebRequestHandler(struct mg_connection *connection) { const struct mg_request_info* ri = mg_get_request_info(connection); - const char TWO_HUNDRED_RESPONSE[] = "HTTP/1.0 200 OK\r\n\r\n"; + const char RESPONSE_200[] = "HTTP/1.0 200 OK\r\n\r\n"; if (strcmp(ri->uri, "/assignment") == 0 && strcmp(ri->request_method, "POST") == 0) { // return a 200 - mg_printf(connection, "%s", TWO_HUNDRED_RESPONSE); + mg_printf(connection, "%s", RESPONSE_200); // upload the file mg_upload(connection, "/tmp"); @@ -46,7 +46,7 @@ int DomainServer::civetwebRequestHandler(struct mg_connection *connection) { // user is asking for json list of assignments // start with a 200 response - mg_printf(connection, "%s", TWO_HUNDRED_RESPONSE); + mg_printf(connection, "%s", RESPONSE_200); // setup the JSON QJsonObject assignmentJSON; @@ -85,8 +85,30 @@ int DomainServer::civetwebRequestHandler(struct mg_connection *connection) { } } + assignmentJSON["fulfilled"] = assignedNodesJSON; + + QJsonObject queuedAssignmentsJSON; + + // add the queued but unfilled assignments to the json + std::deque::iterator assignment = domainServerInstance->_assignmentQueue.begin(); + + while (assignment != domainServerInstance->_assignmentQueue.end()) { + QJsonObject queuedAssignmentJSON; + + QString uuidString = uuidStringWithoutCurlyBraces((*assignment)->getUUID()); + queuedAssignmentJSON[ASSIGNMENT_JSON_UUID_KEY] = uuidString; + + // add this queued assignment to the JSON + queuedAssignmentsJSON[(*assignment)->getTypeName()] = queuedAssignmentJSON; + + // push forward the iterator to check the next assignment + assignment++; + } + + assignmentJSON["queued"] = queuedAssignmentsJSON; + // print out the created JSON - QJsonDocument assignmentDocument(assignedNodesJSON); + QJsonDocument assignmentDocument(assignmentJSON); mg_printf(connection, "%s", assignmentDocument.toJson().constData()); // we've processed this request diff --git a/libraries/shared/src/Assignment.cpp b/libraries/shared/src/Assignment.cpp index 6829a8b266..6a7f62c91b 100644 --- a/libraries/shared/src/Assignment.cpp +++ b/libraries/shared/src/Assignment.cpp @@ -139,6 +139,21 @@ void Assignment::setPayload(const uchar* payload, int numBytes) { memcpy(_payload, payload, _numPayloadBytes); } +const char* Assignment::getTypeName() const { + switch (_type) { + case Assignment::AudioMixerType: + return "audio-mixer"; + case Assignment::AvatarMixerType: + return "avatar-mixer"; + case Assignment::AgentType: + return "agent"; + case Assignment::VoxelServerType: + return "voxel-server"; + default: + return "unknown"; + } +} + int Assignment::packToBuffer(unsigned char* buffer) { int numPackedBytes = 0; diff --git a/libraries/shared/src/Assignment.h b/libraries/shared/src/Assignment.h index 694be61e0e..cc528e9bd4 100644 --- a/libraries/shared/src/Assignment.h +++ b/libraries/shared/src/Assignment.h @@ -73,6 +73,8 @@ public: int getNumberOfInstances() const { return _numberOfInstances; } void setNumberOfInstances(int numberOfInstances) { _numberOfInstances = numberOfInstances; } void decrementNumberOfInstances() { --_numberOfInstances; } + + const char* getTypeName() const; /// Packs the assignment to the passed buffer /// \param buffer the buffer in which to pack the assignment From ba25087b3ea473060774ca0b61e21d9a5474feb9 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 15 Oct 2013 15:58:34 -0700 Subject: [PATCH 14/31] 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 15/31] 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 16/31] 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 17/31] 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 18/31] 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(); -} From 36f06720d09fb89e75992da1f25f4359da7327d0 Mon Sep 17 00:00:00 2001 From: Eric Johnston Date: Wed, 16 Oct 2013 08:21:35 -0700 Subject: [PATCH 19/31] Add left-arm movement to remote avatars without changing network packet structure. Move MyAvatar leap hand arm-movement into Avatar::updateLeapHandPositions(), to enable remote avatars to position fingertips and IK-arms based on Leap hand data. --- interface/src/avatar/Avatar.cpp | 47 +++++++++++++++++++++++++++++++ interface/src/avatar/Avatar.h | 1 + interface/src/avatar/MyAvatar.cpp | 38 +------------------------ 3 files changed, 49 insertions(+), 37 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 7fcca723f6..9c755a24c1 100755 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -460,6 +460,8 @@ void Avatar::updateHandMovementAndTouching(float deltaTime, bool enableHandMovem _skeleton.joint[AVATAR_JOINT_RIGHT_FINGERTIPS].position += transformedHandMovement; } + enableHandMovement |= updateLeapHandPositions(); + //constrain right arm length and re-adjust elbow position as it bends // NOTE - the following must be called on all avatars - not just _isMine if (enableHandMovement) { @@ -641,6 +643,51 @@ void Avatar::updateBodyBalls(float deltaTime) { _bodyBall[BODY_BALL_HEAD_TOP].rotation * _skeleton.joint[BODY_BALL_HEAD_TOP].bindPosePosition; } +// returns true if the Leap controls any of the avatar's hands. +bool Avatar::updateLeapHandPositions() { + bool returnValue = false; + // If there are leap-interaction hands visible, see if we can use them as the endpoints for IK + if (getHand().getPalms().size() > 0) { + PalmData const* leftLeapHand = NULL; + PalmData const* rightLeapHand = NULL; + // Look through all of the palms available (there may be more than two), and pick + // the leftmost and rightmost. If there's only one, we'll use a heuristic below + // to decode whether it's the left or right. + for (size_t i = 0; i < getHand().getPalms().size(); ++i) { + PalmData& palm = getHand().getPalms()[i]; + if (palm.isActive()) { + if (!rightLeapHand || !leftLeapHand) { + rightLeapHand = leftLeapHand = &palm; + } + else if (palm.getRawPosition().x > rightLeapHand->getRawPosition().x) { + rightLeapHand = &palm; + } + else if (palm.getRawPosition().x < leftLeapHand->getRawPosition().x) { + leftLeapHand = &palm; + } + } + } + // If there's only one palm visible. Decide if it's the left or right + if (leftLeapHand == rightLeapHand && leftLeapHand) { + if (leftLeapHand->getRawPosition().x > 0) { + leftLeapHand = NULL; + } + else { + rightLeapHand = NULL; + } + } + if (leftLeapHand) { + _skeleton.joint[ AVATAR_JOINT_LEFT_FINGERTIPS ].position = leftLeapHand->getPosition(); + returnValue = true; + } + if (rightLeapHand) { + _skeleton.joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position = rightLeapHand->getPosition(); + returnValue = true; + } + } + return returnValue; +} + void Avatar::updateArmIKAndConstraints(float deltaTime, AvatarJointID fingerTipJointID) { Skeleton::AvatarJoint& fingerJoint = _skeleton.joint[fingerTipJointID]; Skeleton::AvatarJoint& wristJoint = _skeleton.joint[fingerJoint.parent]; diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 7e11a0b9df..b46f461389 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -231,6 +231,7 @@ protected: glm::vec3 getBodyFrontDirection() const { return getOrientation() * IDENTITY_FRONT; } glm::quat computeRotationFromBodyToWorldUp(float proportion = 1.0f) const; void updateBodyBalls(float deltaTime); + bool updateLeapHandPositions(); void updateArmIKAndConstraints(float deltaTime, AvatarJointID fingerTipJointID); void setScale(const float scale); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 509935b3cf..8cbcabe6e8 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -912,43 +912,7 @@ void MyAvatar::updateHandMovementAndTouching(float deltaTime, bool enableHandMov _avatarTouch.setHasInteractingOther(false); } - // If there are leap-interaction hands visible, see if we can use them as the endpoints for IK - if (getHand().getPalms().size() > 0) { - PalmData const* leftLeapHand = NULL; - PalmData const* rightLeapHand = NULL; - // Look through all of the palms available (there may be more than two), and pick - // the leftmost and rightmost. If there's only one, we'll use a heuristic below - // to decode whether it's the left or right. - for (size_t i = 0; i < getHand().getPalms().size(); ++i) { - PalmData& palm = getHand().getPalms()[i]; - if (palm.isActive()) { - if (!rightLeapHand || !leftLeapHand) { - rightLeapHand = leftLeapHand = &palm; - } - else if (palm.getRawPosition().x > rightLeapHand->getRawPosition().x) { - rightLeapHand = &palm; - } - else if (palm.getRawPosition().x < leftLeapHand->getRawPosition().x) { - leftLeapHand = &palm; - } - } - } - // If there's only one palm visible. Decide if it's the left or right - if (leftLeapHand == rightLeapHand && leftLeapHand) { - if (leftLeapHand->getRawPosition().x > 0) { - leftLeapHand = NULL; - } - else { - rightLeapHand = NULL; - } - } - if (leftLeapHand) { - _skeleton.joint[ AVATAR_JOINT_LEFT_FINGERTIPS ].position = leftLeapHand->getPosition(); - } - if (rightLeapHand) { - _skeleton.joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position = rightLeapHand->getPosition(); - } - } + enableHandMovement |= updateLeapHandPositions(); //constrain right arm length and re-adjust elbow position as it bends // NOTE - the following must be called on all avatars - not just _isMine From c9344d43824ea17c338a6740581d1b255c8035e9 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 16 Oct 2013 11:00:41 -0700 Subject: [PATCH 20/31] Have textures default to white when they fail to load. --- interface/src/renderer/TextureCache.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/interface/src/renderer/TextureCache.cpp b/interface/src/renderer/TextureCache.cpp index cbab1938dd..c24f1fad16 100644 --- a/interface/src/renderer/TextureCache.cpp +++ b/interface/src/renderer/TextureCache.cpp @@ -74,15 +74,17 @@ GLuint TextureCache::getPermutationNormalTextureID() { return _permutationNormalTextureID; } +static void loadWhiteTexture() { + 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); +} + 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); - + loadWhiteTexture(); glBindTexture(GL_TEXTURE_2D, 0); } return _whiteTextureID; @@ -214,6 +216,11 @@ NetworkTexture::NetworkTexture(const QUrl& url) : _reply(NULL), _averageColor(1. connect(_reply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(handleDownloadProgress(qint64,qint64))); connect(_reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleReplyError())); + + // default to white + glBindTexture(GL_TEXTURE_2D, getID()); + loadWhiteTexture(); + glBindTexture(GL_TEXTURE_2D, 0); } NetworkTexture::~NetworkTexture() { From ef655f5e787983c159cbea21858685be7dd44ec6 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 16 Oct 2013 11:50:19 -0700 Subject: [PATCH 21/31] clear agent audio injector after send, don't send if empty --- assignment-client/src/Agent.cpp | 5 ++++- libraries/audio/src/AudioInjector.cpp | 8 +++++++- libraries/audio/src/AudioInjector.h | 4 ++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index d9cf479b17..8e8835de45 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -161,13 +161,16 @@ void Agent::run() { emit willSendAudioDataCallback(); - if (audioMixer) { + if (audioMixer && scriptedAudioInjector.hasSamplesToInject()) { int usecToSleep = usecTimestamp(&startTime) + (thisFrame++ * INJECT_INTERVAL_USECS) - usecTimestampNow(); if (usecToSleep > 0) { usleep(usecToSleep); } scriptedAudioInjector.injectAudio(NodeList::getInstance()->getNodeSocket(), audioMixer->getPublicSocket()); + + // clear out the audio injector so that it doesn't re-send what we just sent + scriptedAudioInjector.clear(); } // allow the scripter's call back to setup visual data diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index acdee03e8f..e3b137fe4d 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -64,7 +64,7 @@ AudioInjector::~AudioInjector() { } void AudioInjector::injectAudio(UDPSocket* injectorSocket, sockaddr* destinationSocket) { - if (_audioSampleArray) { + if (_audioSampleArray && _indexOfNextSlot > 0) { _isInjectingAudio = true; timeval startTime; @@ -139,6 +139,11 @@ void AudioInjector::addSamples(int16_t* sampleBuffer, int numSamples) { } } +void AudioInjector::clear() { + _indexOfNextSlot = 0; + memset(_audioSampleArray, 0, _numTotalSamples * sizeof(int16_t)); +} + int16_t& AudioInjector::sampleAt(const int index) { assert(index >= 0 && index < _numTotalSamples); @@ -149,4 +154,5 @@ void AudioInjector::insertSample(const int index, int sample) { assert (index >= 0 && index < _numTotalSamples); _audioSampleArray[index] = (int16_t) sample; + _indexOfNextSlot = index + 1; } diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index 626cdc3149..8847ce14b9 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -51,8 +51,12 @@ public: float getRadius() const { return _radius; } void setRadius(float radius) { _radius = radius; } + bool hasSamplesToInject() const { return _indexOfNextSlot > 0; } + void addSample(const int16_t sample); void addSamples(int16_t* sampleBuffer, int numSamples); + + void clear(); public slots: int16_t& sampleAt(const int index); void insertSample(const int index, int sample); From 11fea386b68554138c48e7d408fa76c39b0afdc2 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 16 Oct 2013 14:07:36 -0700 Subject: [PATCH 22/31] First stab at chat circling mechanism. --- interface/src/avatar/MyAvatar.cpp | 77 +++++++++++++++++++++++++++++++ interface/src/avatar/MyAvatar.h | 1 + 2 files changed, 78 insertions(+) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 8cbcabe6e8..d4664168a7 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -351,6 +351,8 @@ void MyAvatar::simulate(float deltaTime, Transmitter* transmitter) { } } + updateChatCircle(deltaTime); + _position += _velocity * deltaTime; // Zero thrust out now that we've added it to velocity in this frame @@ -1092,6 +1094,81 @@ void MyAvatar::applyCollisionWithOtherAvatar(Avatar * otherAvatar, float deltaTi _velocity += bodyPushForce; } +void MyAvatar::updateChatCircle(float deltaTime) { + // find the number of members, their center of mass, and the average up vector + glm::vec3 center = _position; + glm::vec3 up = getWorldAlignedOrientation() * IDENTITY_UP; + int memberCount = 1; + NodeList* nodeList = NodeList::getInstance(); + const float MAX_CHAT_DISTANCE = 10.0f; + for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { + if (node->getLinkedData() && node->getType() == NODE_TYPE_AGENT) { + Avatar* otherAvatar = (Avatar*)node->getLinkedData(); + if (glm::distance(_position, otherAvatar->getPosition()) < MAX_CHAT_DISTANCE) { + center += otherAvatar->getPosition(); + up += otherAvatar->getWorldAlignedOrientation() * IDENTITY_UP; + memberCount++; + } + } + } + if (memberCount == 1) { + return; + } + center /= memberCount; + up = glm::normalize(up); + + // find reasonable corresponding right/front vectors + glm::vec3 front = glm::cross(up, IDENTITY_RIGHT); + if (glm::length(front) < EPSILON) { + front = glm::cross(up, IDENTITY_FRONT); + } + front = glm::normalize(front); + glm::vec3 right = glm::cross(front, up); + + // find our angle and the angular distances to our closest neighbors + glm::vec3 delta = _position - center; + glm::vec3 projected = glm::vec3(glm::dot(right, delta), glm::dot(front, delta), 0.0f); + float myAngle = glm::length(projected) > EPSILON ? atan2f(projected.y, projected.x) : 0.0f; + float leftDistance = PIf; + float rightDistance = PIf; + for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { + if (node->getLinkedData() && node->getType() == NODE_TYPE_AGENT) { + Avatar* otherAvatar = (Avatar*)node->getLinkedData(); + if (glm::distance(_position, otherAvatar->getPosition()) < MAX_CHAT_DISTANCE) { + delta = otherAvatar->getPosition() - center; + projected = glm::vec3(glm::dot(right, delta), glm::dot(front, delta), 0.0f); + float angle = glm::length(projected) > EPSILON ? atan2f(projected.y, projected.x) : 0.0f; + if (angle < myAngle) { + leftDistance = min(myAngle - angle, leftDistance); + rightDistance = min(PI_TIMES_TWO - (myAngle - angle), rightDistance); + + } else { + leftDistance = min(PI_TIMES_TWO - (angle - myAngle), leftDistance); + rightDistance = min(angle - myAngle, rightDistance); + } + } + } + } + + // determine the chat circle radius + const float CIRCUMFERENCE_PER_MEMBER = 2.0f; + float radius = (CIRCUMFERENCE_PER_MEMBER * memberCount) / PI_TIMES_TWO; + + // split the difference between our neighbors + float targetAngle = myAngle + (rightDistance - leftDistance) / 2.0f; + glm::vec3 targetPosition = center + (front * sinf(targetAngle) + right * cosf(targetAngle)) * radius; + + // face the center of the circle + glm::quat orientation = getOrientation(); + glm::quat targetOrientation = rotationBetween(orientation * IDENTITY_FRONT, center - targetPosition) * orientation; + targetOrientation = rotationBetween(targetOrientation * IDENTITY_UP, up) * targetOrientation; + + // approach the target position/orientation + const float APPROACH_RATE = 0.1f; + _position = glm::mix(_position, targetPosition, APPROACH_RATE); + setOrientation(safeMix(orientation, targetOrientation, APPROACH_RATE)); +} + void MyAvatar::setGravity(glm::vec3 gravity) { _gravity = gravity; _head.setGravity(_gravity); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 4dc04a978e..5e25a17fb8 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -94,6 +94,7 @@ private: void applyHardCollision(const glm::vec3& penetration, float elasticity, float damping); void updateCollisionSound(const glm::vec3& penetration, float deltaTime, float frequency); void applyCollisionWithOtherAvatar( Avatar * other, float deltaTime ); + void updateChatCircle(float deltaTime); void checkForMouseRayTouching(); }; From 50216ca4ade1e04cb22ceb5855c6b72bf4f8bc49 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 16 Oct 2013 15:37:59 -0700 Subject: [PATCH 23/31] Base the number of people we include in the circle on whether we would actually fall in its radius (plus an expansion factor). --- interface/src/avatar/MyAvatar.cpp | 92 +++++++++++++++++++------------ 1 file changed, 58 insertions(+), 34 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index d4664168a7..149aa504f5 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1094,27 +1094,60 @@ void MyAvatar::applyCollisionWithOtherAvatar(Avatar * otherAvatar, float deltaTi _velocity += bodyPushForce; } +class SortedAvatar { +public: + Avatar* avatar; + float distance; + glm::vec3 accumulatedCenter; +}; + +bool operator<(const SortedAvatar& s1, const SortedAvatar& s2) { + return s1.distance < s2.distance; +} + void MyAvatar::updateChatCircle(float deltaTime) { - // find the number of members, their center of mass, and the average up vector - glm::vec3 center = _position; - glm::vec3 up = getWorldAlignedOrientation() * IDENTITY_UP; - int memberCount = 1; + // find all members and sort by distance + QVector sortedAvatars; NodeList* nodeList = NodeList::getInstance(); - const float MAX_CHAT_DISTANCE = 10.0f; for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { if (node->getLinkedData() && node->getType() == NODE_TYPE_AGENT) { - Avatar* otherAvatar = (Avatar*)node->getLinkedData(); - if (glm::distance(_position, otherAvatar->getPosition()) < MAX_CHAT_DISTANCE) { - center += otherAvatar->getPosition(); - up += otherAvatar->getWorldAlignedOrientation() * IDENTITY_UP; - memberCount++; - } + SortedAvatar sortedAvatar; + sortedAvatar.avatar = (Avatar*)node->getLinkedData(); + sortedAvatar.distance = glm::distance(_position, sortedAvatar.avatar->getPosition()); + sortedAvatars.append(sortedAvatar); } } - if (memberCount == 1) { + qSort(sortedAvatars.begin(), sortedAvatars.end()); + + // compute the accumulated centers + glm::vec3 center = _position; + for (int i = 0; i < sortedAvatars.size(); i++) { + SortedAvatar& sortedAvatar = sortedAvatars[i]; + sortedAvatar.accumulatedCenter = (center += sortedAvatar.avatar->getPosition()) / (i + 2.0f); + } + + // remove members whose accumulated circles are too far away to influence us + const float CIRCUMFERENCE_PER_MEMBER = 2.0f; + const float CIRCLE_INFLUENCE_SCALE = 1.1f; + for (int i = sortedAvatars.size() - 1; i >= 0; i--) { + float radius = (CIRCUMFERENCE_PER_MEMBER * (i + 2)) / PI_TIMES_TWO; + if (glm::distance(_position, sortedAvatars[i].accumulatedCenter) > radius * CIRCLE_INFLUENCE_SCALE) { + sortedAvatars.remove(i); + } else { + break; + } + } + if (sortedAvatars.isEmpty()) { return; } - center /= memberCount; + center = sortedAvatars.last().accumulatedCenter; + float radius = (CIRCUMFERENCE_PER_MEMBER * (sortedAvatars.size() + 1)) / PI_TIMES_TWO; + + // compute the average up vector + glm::vec3 up = getWorldAlignedOrientation() * IDENTITY_UP; + foreach (const SortedAvatar& sortedAvatar, sortedAvatars) { + up += sortedAvatar.avatar->getWorldAlignedOrientation() * IDENTITY_UP; + } up = glm::normalize(up); // find reasonable corresponding right/front vectors @@ -1131,29 +1164,20 @@ void MyAvatar::updateChatCircle(float deltaTime) { float myAngle = glm::length(projected) > EPSILON ? atan2f(projected.y, projected.x) : 0.0f; float leftDistance = PIf; float rightDistance = PIf; - for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { - if (node->getLinkedData() && node->getType() == NODE_TYPE_AGENT) { - Avatar* otherAvatar = (Avatar*)node->getLinkedData(); - if (glm::distance(_position, otherAvatar->getPosition()) < MAX_CHAT_DISTANCE) { - delta = otherAvatar->getPosition() - center; - projected = glm::vec3(glm::dot(right, delta), glm::dot(front, delta), 0.0f); - float angle = glm::length(projected) > EPSILON ? atan2f(projected.y, projected.x) : 0.0f; - if (angle < myAngle) { - leftDistance = min(myAngle - angle, leftDistance); - rightDistance = min(PI_TIMES_TWO - (myAngle - angle), rightDistance); - - } else { - leftDistance = min(PI_TIMES_TWO - (angle - myAngle), leftDistance); - rightDistance = min(angle - myAngle, rightDistance); - } - } + foreach (const SortedAvatar& sortedAvatar, sortedAvatars) { + delta = sortedAvatar.avatar->getPosition() - center; + projected = glm::vec3(glm::dot(right, delta), glm::dot(front, delta), 0.0f); + float angle = glm::length(projected) > EPSILON ? atan2f(projected.y, projected.x) : 0.0f; + if (angle < myAngle) { + leftDistance = min(myAngle - angle, leftDistance); + rightDistance = min(PI_TIMES_TWO - (myAngle - angle), rightDistance); + + } else { + leftDistance = min(PI_TIMES_TWO - (angle - myAngle), leftDistance); + rightDistance = min(angle - myAngle, rightDistance); } } - // determine the chat circle radius - const float CIRCUMFERENCE_PER_MEMBER = 2.0f; - float radius = (CIRCUMFERENCE_PER_MEMBER * memberCount) / PI_TIMES_TWO; - // split the difference between our neighbors float targetAngle = myAngle + (rightDistance - leftDistance) / 2.0f; glm::vec3 targetPosition = center + (front * sinf(targetAngle) + right * cosf(targetAngle)) * radius; @@ -1164,7 +1188,7 @@ void MyAvatar::updateChatCircle(float deltaTime) { targetOrientation = rotationBetween(targetOrientation * IDENTITY_UP, up) * targetOrientation; // approach the target position/orientation - const float APPROACH_RATE = 0.1f; + const float APPROACH_RATE = 0.05f; _position = glm::mix(_position, targetPosition, APPROACH_RATE); setOrientation(safeMix(orientation, targetOrientation, APPROACH_RATE)); } From 6df245908c702b107dbf993db39461581c91e755 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 16 Oct 2013 15:41:38 -0700 Subject: [PATCH 24/31] Reduce the approach rate. --- interface/src/avatar/MyAvatar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 149aa504f5..10beec4a12 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1188,7 +1188,7 @@ void MyAvatar::updateChatCircle(float deltaTime) { targetOrientation = rotationBetween(targetOrientation * IDENTITY_UP, up) * targetOrientation; // approach the target position/orientation - const float APPROACH_RATE = 0.05f; + const float APPROACH_RATE = 0.01f; _position = glm::mix(_position, targetPosition, APPROACH_RATE); setOrientation(safeMix(orientation, targetOrientation, APPROACH_RATE)); } From 7a1f8451890626d5b673c91c34d5b836be6e23b3 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 16 Oct 2013 15:48:25 -0700 Subject: [PATCH 25/31] Another rate adjustment. --- interface/src/avatar/MyAvatar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 10beec4a12..7fc625072e 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1188,7 +1188,7 @@ void MyAvatar::updateChatCircle(float deltaTime) { targetOrientation = rotationBetween(targetOrientation * IDENTITY_UP, up) * targetOrientation; // approach the target position/orientation - const float APPROACH_RATE = 0.01f; + const float APPROACH_RATE = 0.025f; _position = glm::mix(_position, targetPosition, APPROACH_RATE); setOrientation(safeMix(orientation, targetOrientation, APPROACH_RATE)); } From c324933b8a22f9ff2dfae8df80068734b9d2fbc2 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Wed, 16 Oct 2013 16:49:33 -0700 Subject: [PATCH 26/31] Move to 'european' comfort distance for chat circle --- interface/src/avatar/MyAvatar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 7fc625072e..1ce02ea4e2 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1127,7 +1127,7 @@ void MyAvatar::updateChatCircle(float deltaTime) { } // remove members whose accumulated circles are too far away to influence us - const float CIRCUMFERENCE_PER_MEMBER = 2.0f; + const float CIRCUMFERENCE_PER_MEMBER = 1.0f; const float CIRCLE_INFLUENCE_SCALE = 1.1f; for (int i = sortedAvatars.size() - 1; i >= 0; i--) { float radius = (CIRCUMFERENCE_PER_MEMBER * (i + 2)) / PI_TIMES_TWO; From 89d6c7905bb443cb21cf922e961ef7760b2c5a85 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 16 Oct 2013 17:21:09 -0700 Subject: [PATCH 27/31] Decrease the distance between members, increase the influence of the circle, don't rotate members. --- interface/src/avatar/MyAvatar.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 1ce02ea4e2..55752339fa 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1127,8 +1127,8 @@ void MyAvatar::updateChatCircle(float deltaTime) { } // remove members whose accumulated circles are too far away to influence us - const float CIRCUMFERENCE_PER_MEMBER = 1.0f; - const float CIRCLE_INFLUENCE_SCALE = 1.1f; + const float CIRCUMFERENCE_PER_MEMBER = 0.5f; + const float CIRCLE_INFLUENCE_SCALE = 1.5f; for (int i = sortedAvatars.size() - 1; i >= 0; i--) { float radius = (CIRCUMFERENCE_PER_MEMBER * (i + 2)) / PI_TIMES_TWO; if (glm::distance(_position, sortedAvatars[i].accumulatedCenter) > radius * CIRCLE_INFLUENCE_SCALE) { @@ -1182,15 +1182,9 @@ void MyAvatar::updateChatCircle(float deltaTime) { float targetAngle = myAngle + (rightDistance - leftDistance) / 2.0f; glm::vec3 targetPosition = center + (front * sinf(targetAngle) + right * cosf(targetAngle)) * radius; - // face the center of the circle - glm::quat orientation = getOrientation(); - glm::quat targetOrientation = rotationBetween(orientation * IDENTITY_FRONT, center - targetPosition) * orientation; - targetOrientation = rotationBetween(targetOrientation * IDENTITY_UP, up) * targetOrientation; - - // approach the target position/orientation + // approach the target position const float APPROACH_RATE = 0.025f; _position = glm::mix(_position, targetPosition, APPROACH_RATE); - setOrientation(safeMix(orientation, targetOrientation, APPROACH_RATE)); } void MyAvatar::setGravity(glm::vec3 gravity) { From a5a81a585e78c757339e0cbc66edbb7514e8ce4c Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 16 Oct 2013 17:47:39 -0700 Subject: [PATCH 28/31] Fix for glow flickering. --- interface/src/renderer/GlowEffect.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/renderer/GlowEffect.cpp b/interface/src/renderer/GlowEffect.cpp index fc95ed24d6..fe46e02688 100644 --- a/interface/src/renderer/GlowEffect.cpp +++ b/interface/src/renderer/GlowEffect.cpp @@ -133,7 +133,7 @@ QOpenGLFramebufferObject* GlowEffect::render(bool toTexture) { QOpenGLFramebufferObject* destFBO = toTexture ? Application::getInstance()->getTextureCache()->getSecondaryFramebufferObject() : NULL; - if (_isEmpty) { + if (_isEmpty && _renderMode != DIFFUSE_ADD_MODE) { // copy the primary to the screen if (QOpenGLFramebufferObject::hasOpenGLFramebufferBlit()) { QOpenGLFramebufferObject::blitFramebuffer(destFBO, primaryFBO); From 121a7f82703471473e99b952caf89f2371b2636e Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 16 Oct 2013 17:48:28 -0700 Subject: [PATCH 29/31] Widen the circle influence, increase the approach rate. --- interface/src/avatar/MyAvatar.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 55752339fa..120ee2c09b 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1128,7 +1128,7 @@ void MyAvatar::updateChatCircle(float deltaTime) { // remove members whose accumulated circles are too far away to influence us const float CIRCUMFERENCE_PER_MEMBER = 0.5f; - const float CIRCLE_INFLUENCE_SCALE = 1.5f; + const float CIRCLE_INFLUENCE_SCALE = 2.0f; for (int i = sortedAvatars.size() - 1; i >= 0; i--) { float radius = (CIRCUMFERENCE_PER_MEMBER * (i + 2)) / PI_TIMES_TWO; if (glm::distance(_position, sortedAvatars[i].accumulatedCenter) > radius * CIRCLE_INFLUENCE_SCALE) { @@ -1183,7 +1183,7 @@ void MyAvatar::updateChatCircle(float deltaTime) { glm::vec3 targetPosition = center + (front * sinf(targetAngle) + right * cosf(targetAngle)) * radius; // approach the target position - const float APPROACH_RATE = 0.025f; + const float APPROACH_RATE = 0.05f; _position = glm::mix(_position, targetPosition, APPROACH_RATE); } From b25020b84af7ed9b094dce02fe9aea25df608fc8 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 17 Oct 2013 10:58:33 -0700 Subject: [PATCH 30/31] Need to halve the target angle, because both participants will be moving. Use randomness to resolve cases where two participants are on top of one another. --- interface/src/avatar/MyAvatar.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 120ee2c09b..bbdd414494 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1178,8 +1178,13 @@ void MyAvatar::updateChatCircle(float deltaTime) { } } + // if we're on top of a neighbor, we need to randomize so that they don't both go in the same direction + if (rightDistance == 0.0f && randomBoolean()) { + swap(leftDistance, rightDistance); + } + // split the difference between our neighbors - float targetAngle = myAngle + (rightDistance - leftDistance) / 2.0f; + float targetAngle = myAngle + (rightDistance - leftDistance) / 4.0f; glm::vec3 targetPosition = center + (front * sinf(targetAngle) + right * cosf(targetAngle)) * radius; // approach the target position From 36d4be4e3b9ad28a93d878dea3106c850babc7ee Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 17 Oct 2013 11:05:52 -0700 Subject: [PATCH 31/31] These should actually start at two pi (that's the maximum distance). --- interface/src/avatar/MyAvatar.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index bbdd414494..e1c18089fd 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1162,8 +1162,8 @@ void MyAvatar::updateChatCircle(float deltaTime) { glm::vec3 delta = _position - center; glm::vec3 projected = glm::vec3(glm::dot(right, delta), glm::dot(front, delta), 0.0f); float myAngle = glm::length(projected) > EPSILON ? atan2f(projected.y, projected.x) : 0.0f; - float leftDistance = PIf; - float rightDistance = PIf; + float leftDistance = PI_TIMES_TWO; + float rightDistance = PI_TIMES_TWO; foreach (const SortedAvatar& sortedAvatar, sortedAvatars) { delta = sortedAvatar.avatar->getPosition() - center; projected = glm::vec3(glm::dot(right, delta), glm::dot(front, delta), 0.0f);