From bd5272d6770a48767c0806c3b2223bdf7f8ef2e2 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 29 Apr 2015 13:35:21 -0700 Subject: [PATCH 01/22] Textures for .obj reader: Refactor so that uv coordinates can be associated with vertices that might otherwise be on different materials. Defer the assignment vertex/normal/uv assignment until later so that it could potentially be moved directly to model geometry instead of fbx geometry. Handle the actual uv coordinates. This version does not handle explicit .mtl files, but it does handle those .obj files that follow the convention that there is a .jpg with the same name as the .obj file. --- libraries/fbx/src/OBJReader.cpp | 351 ++++++++----------- libraries/fbx/src/OBJReader.h | 58 ++- libraries/render-utils/src/GeometryCache.cpp | 25 +- 3 files changed, 213 insertions(+), 221 deletions(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index 0deb241343..28f1e2f1ce 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -26,35 +26,9 @@ QHash COMMENT_SCALE_HINTS = {{"This file uses centimeters as uni {"This file uses millimeters as units", 1.0f / 1000.0f}}; -class OBJTokenizer { -public: - OBJTokenizer(QIODevice* device); - enum SpecialToken { - NO_TOKEN = -1, - NO_PUSHBACKED_TOKEN = -1, - DATUM_TOKEN = 0x100, - COMMENT_TOKEN = 0x101 - }; - int nextToken(); - const QByteArray& getDatum() const { return _datum; } - bool isNextTokenFloat(); - void skipLine() { _device->readLine(); } - void pushBackToken(int token) { _pushedBackToken = token; } - void ungetChar(char ch) { _device->ungetChar(ch); } - const QString getComment() const { return _comment; } - -private: - QIODevice* _device; - QByteArray _datum; - int _pushedBackToken; - QString _comment; -}; - - OBJTokenizer::OBJTokenizer(QIODevice* device) : _device(device), _pushedBackToken(-1) { } - int OBJTokenizer::nextToken() { if (_pushedBackToken != NO_PUSHBACKED_TOKEN) { int token = _pushedBackToken; @@ -116,6 +90,24 @@ bool OBJTokenizer::isNextTokenFloat() { return ok; } +glm::vec3 OBJTokenizer::getVec3() { + auto v = glm::vec3(this->getFloat(), this->getFloat(), this->getFloat()); + while (this->isNextTokenFloat()) { + // the spec(s) get(s) vague here. might be w, might be a color... chop it off. + this->nextToken(); + } + return v; +} +glm::vec2 OBJTokenizer::getVec2() { + auto v = glm::vec2(this->getFloat(), 1.0f - this->getFloat()); // OBJ has an odd sense of u, v + while (this->isNextTokenFloat()) { + // there can be a w, but we don't handle that + this->nextToken(); + } + return v; +} + + void setMeshPartDefaults(FBXMeshPart &meshPart, QString materialID) { meshPart.diffuseColor = glm::vec3(1, 1, 1); meshPart.specularColor = glm::vec3(1, 1, 1); @@ -123,7 +115,7 @@ void setMeshPartDefaults(FBXMeshPart &meshPart, QString materialID) { meshPart.emissiveParams = glm::vec2(0, 1); meshPart.shininess = 40; meshPart.opacity = 1; - + meshPart.materialID = materialID; meshPart.opacity = 1.0; meshPart._material = model::MaterialPointer(new model::Material()); @@ -134,14 +126,57 @@ void setMeshPartDefaults(FBXMeshPart &meshPart, QString materialID) { meshPart._material->setEmissive(glm::vec3(0.0, 0.0, 0.0)); } -bool parseOBJGroup(OBJTokenizer &tokenizer, const QVariantHash& mapping, - FBXGeometry &geometry, QVector& faceNormals, QVector& faceNormalIndexes, - float& scaleGuess) { +// OBJFace +bool OBJFace::add(QByteArray vertexIndex, QByteArray textureIndex, QByteArray normalIndex) { + bool ok; + int index = vertexIndex.toInt(&ok); + if (!ok) { return false; } + vertexIndices.append(index - 1); + if (textureIndex != nullptr) { + index = textureIndex.toInt(&ok); + if (!ok) { return false; } + textureUVIndices.append(index - 1); + } + if (normalIndex != nullptr) { + index = normalIndex.toInt(&ok); + if (!ok) { return false; } + normalIndices.append(index - 1); + } + return true; +} +QVector OBJFace::triangulate() { + QVector newFaces; + if (vertexIndices.count() == 3) { + newFaces.append(*this); + } else { + for (int i = 1; i < vertexIndices.count() - 1; i++) { + OBJFace newFace; // FIXME: also copy materialName, groupName + newFace.addFrom(this, 0); + newFace.addFrom(this, i); + newFace.addFrom(this, i + 1); + newFaces.append(newFace); + } + } + return newFaces; +} +void OBJFace::addFrom(OBJFace const * f, int i) { // add using data from f at index i + vertexIndices.append(f->vertexIndices[i]); + if (f->textureUVIndices.count() > 0) { // Any at all. Runtime error if not consistent. + textureUVIndices.append(f->textureUVIndices[i]); + } + if (f->normalIndices.count() > 0) { + normalIndices.append(f->normalIndices[i]); + } +} + +bool OBJReader::parseOBJGroup(OBJTokenizer &tokenizer, const QVariantHash& mapping, FBXGeometry &geometry, float& scaleGuess) { + FaceGroup faces; FBXMesh &mesh = geometry.meshes[0]; mesh.parts.append(FBXMeshPart()); FBXMeshPart &meshPart = mesh.parts.last(); bool sawG = false; bool result = true; + int originalFaceCountForDebugging = 0; setMeshPartDefaults(meshPart, QString("dontknow") + QString::number(mesh.parts.count())); @@ -165,6 +200,7 @@ bool parseOBJGroup(OBJTokenizer &tokenizer, const QVariantHash& mapping, break; } QByteArray token = tokenizer.getDatum(); + //qCDebug(modelformat) << token; if (token == "g") { if (sawG) { // we've encountered the beginning of the next group. @@ -177,53 +213,18 @@ bool parseOBJGroup(OBJTokenizer &tokenizer, const QVariantHash& mapping, } QByteArray groupName = tokenizer.getDatum(); meshPart.materialID = groupName; + //qCDebug(modelformat) << "new group:" << groupName; } else if (token == "v") { - if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { - break; - } - float x = std::stof(tokenizer.getDatum().data()); - - if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { - break; - } - float y = std::stof(tokenizer.getDatum().data()); - - if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { - break; - } - float z = std::stof(tokenizer.getDatum().data()); - - while (tokenizer.isNextTokenFloat()) { - // the spec(s) get(s) vague here. might be w, might be a color... chop it off. - tokenizer.nextToken(); - } - mesh.vertices.append(glm::vec3(x, y, z)); + vertices.append(tokenizer.getVec3()); } else if (token == "vn") { - if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { - break; - } - float x = std::stof(tokenizer.getDatum().data()); - if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { - break; - } - float y = std::stof(tokenizer.getDatum().data()); - if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { - break; - } - float z = std::stof(tokenizer.getDatum().data()); - - while (tokenizer.isNextTokenFloat()) { - // the spec gets vague here. might be w - tokenizer.nextToken(); - } - faceNormals.append(glm::vec3(x, y, z)); + normals.append(tokenizer.getVec3()); + } else if (token == "vt") { + textureUVs.append(tokenizer.getVec2()); } else if (token == "f") { - // a face can have 3 or more vertices - QVector indices; - QVector normalIndices; + OBJFace face; while (true) { if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { - if (indices.count() == 0) { + if (face.vertexIndices.count() == 0) { // nonsense, bail out. goto done; } @@ -233,131 +234,61 @@ bool parseOBJGroup(OBJTokenizer &tokenizer, const QVariantHash& mapping, // vertex-index // vertex-index/texture-index // vertex-index/texture-index/surface-normal-index - QByteArray token = tokenizer.getDatum(); - QList parts = token.split('/'); - assert(parts.count() >= 1); - assert(parts.count() <= 3); - QByteArray vertIndexBA = parts[ 0 ]; - - bool ok; - int vertexIndex = vertIndexBA.toInt(&ok); - if (!ok) { - // it wasn't #/#/#, put it back and exit this loop. + if (!std::isdigit(token[0])) { // Tokenizer treats line endings as whitespace. Non-digit indicates done; tokenizer.pushBackToken(OBJTokenizer::DATUM_TOKEN); break; } - - // if (parts.count() > 1) { - // QByteArray textureIndexBA = parts[ 1 ]; - // } - - if (parts.count() > 2) { - QByteArray normalIndexBA = parts[ 2 ]; - bool ok; - int normalIndex = normalIndexBA.toInt(&ok); - if (ok) { - normalIndices.append(normalIndex - 1); - } - } - - // negative indexes count backward from the current end of the vertex list - vertexIndex = (vertexIndex >= 0 ? vertexIndex : mesh.vertices.count() + vertexIndex + 1); - // obj index is 1 based - assert(vertexIndex >= 1); - indices.append(vertexIndex - 1); + QList parts = token.split('/'); + assert(parts.count() >= 1); + assert(parts.count() <= 3); + // FIXME: if we want to handle negative indices, it has to be done here. + face.add(parts[0], (parts.count() > 1) ? parts[1] : nullptr, (parts.count() > 2) ? parts[2] : nullptr); + // FIXME: preserve current name, material and such } - - if (indices.count() == 3) { - meshPart.triangleIndices.append(indices[0]); - meshPart.triangleIndices.append(indices[1]); - meshPart.triangleIndices.append(indices[2]); - if (normalIndices.count() == 3) { - faceNormalIndexes.append(normalIndices[0]); - faceNormalIndexes.append(normalIndices[1]); - faceNormalIndexes.append(normalIndices[2]); - } else { - // hmm. - } - } else if (indices.count() == 4) { - meshPart.quadIndices << indices; - } else { - // some obj writers (maya) will write a face with lots of points. - for (int i = 1; i < indices.count() - 1; i++) { - // break the face into triangles - meshPart.triangleIndices.append(indices[0]); - meshPart.triangleIndices.append(indices[i]); - meshPart.triangleIndices.append(indices[i+1]); - } - if (indices.count() == normalIndices.count()) { - for (int i = 1; i < normalIndices.count() - 1; i++) { - faceNormalIndexes.append(normalIndices[0]); - faceNormalIndexes.append(normalIndices[i]); - faceNormalIndexes.append(normalIndices[i+1]); - } - } + originalFaceCountForDebugging++; + foreach(OBJFace face, face.triangulate()) { + faces.append(face); } - } else { + } else { // something we don't (yet) care about // qCDebug(modelformat) << "OBJ parser is skipping a line with" << token; tokenizer.skipLine(); } } - - done: - - if (meshPart.triangleIndices.size() == 0 && meshPart.quadIndices.size() == 0) { - // empty mesh? +done: + if (faces.count() == 0) { // empty mesh mesh.parts.pop_back(); } - + faceGroups.append(faces); // We're done with this group. Add the faces. + //qCDebug(modelformat) << "end group:" << meshPart.materialID << " original faces:" << originalFaceCountForDebugging << " triangles:" << faces.count() << " keep going:" << result; return result; } -FBXGeometry readOBJ(const QByteArray& model, const QVariantHash& mapping) { +FBXGeometry OBJReader::readOBJ(const QByteArray& model, const QVariantHash& mapping) { QBuffer buffer(const_cast(&model)); buffer.open(QIODevice::ReadOnly); - return readOBJ(&buffer, mapping); + return this->readOBJ(&buffer, mapping); } -FBXGeometry readOBJ(QIODevice* device, const QVariantHash& mapping) { +FBXGeometry OBJReader::readOBJ(QIODevice* device, const QVariantHash& mapping) { FBXGeometry geometry; OBJTokenizer tokenizer(device); - QVector faceNormalIndexes; - QVector faceNormals; float scaleGuess = 1.0f; - faceNormalIndexes.clear(); - geometry.meshExtents.reset(); geometry.meshes.append(FBXMesh()); try { // call parseOBJGroup as long as it's returning true. Each successful call will // add a new meshPart to the geometry's single mesh. - bool success = true; - while (success) { - success = parseOBJGroup(tokenizer, mapping, geometry, faceNormals, faceNormalIndexes, scaleGuess); - } + while (parseOBJGroup(tokenizer, mapping, geometry, scaleGuess)) {} FBXMesh &mesh = geometry.meshes[0]; mesh.meshIndex = 0; - - // if we got a hint about units, scale all the points - if (scaleGuess != 1.0f) { - for (int i = 0; i < mesh.vertices.size(); i++) { - mesh.vertices[i] *= scaleGuess; - } - } - - mesh.meshExtents.reset(); - foreach (const glm::vec3& vertex, mesh.vertices) { - mesh.meshExtents.addPoint(vertex); - geometry.meshExtents.addPoint(vertex); - } - + geometry.joints.resize(1); geometry.joints[0].isFree = false; geometry.joints[0].parentIndex = -1; @@ -380,63 +311,57 @@ FBXGeometry readOBJ(QIODevice* device, const QVariantHash& mapping) { 0, 0, 1, 0, 0, 0, 0, 1); mesh.clusters.append(cluster); - - // The OBJ format has normals for faces. The FBXGeometry structure has normals for points. - // run through all the faces, look-up (or determine) a normal and set the normal for the points - // that make up each face. - QVector pointNormalsSums; - - mesh.normals.fill(glm::vec3(0,0,0), mesh.vertices.count()); - pointNormalsSums.fill(glm::vec3(0,0,0), mesh.vertices.count()); - - foreach (FBXMeshPart meshPart, mesh.parts) { - int triCount = meshPart.triangleIndices.count() / 3; - for (int i = 0; i < triCount; i++) { - int p0Index = meshPart.triangleIndices[i*3]; - int p1Index = meshPart.triangleIndices[i*3+1]; - int p2Index = meshPart.triangleIndices[i*3+2]; - - assert(p0Index < mesh.vertices.count()); - assert(p1Index < mesh.vertices.count()); - assert(p2Index < mesh.vertices.count()); - + + int meshPartCount = 0; + for (int i = 0; i < mesh.parts.count(); i++) { + FBXMeshPart & meshPart = mesh.parts[i]; + //qCDebug(modelformat) << "part:" << meshPartCount << " faces:" << faceGroups[meshPartCount].count() << "triangle indices will start with:" << mesh.vertices.count(); + foreach(OBJFace face, faceGroups[meshPartCount]) { + glm::vec3 v0 = vertices[face.vertexIndices[0]]; + glm::vec3 v1 = vertices[face.vertexIndices[1]]; + glm::vec3 v2 = vertices[face.vertexIndices[2]]; + meshPart.triangleIndices.append(mesh.vertices.count()); // not face.vertexIndices into vertices + mesh.vertices << v0; + meshPart.triangleIndices.append(mesh.vertices.count()); + mesh.vertices << v1; + meshPart.triangleIndices.append(mesh.vertices.count()); + mesh.vertices << v2; + glm::vec3 n0, n1, n2; - if (i < faceNormalIndexes.count()) { - int n0Index = faceNormalIndexes[i*3]; - int n1Index = faceNormalIndexes[i*3+1]; - int n2Index = faceNormalIndexes[i*3+2]; - n0 = faceNormals[n0Index]; - n1 = faceNormals[n1Index]; - n2 = faceNormals[n2Index]; - } else { - // We didn't read normals, add bogus normal data for this face - glm::vec3 p0 = mesh.vertices[p0Index]; - glm::vec3 p1 = mesh.vertices[p1Index]; - glm::vec3 p2 = mesh.vertices[p2Index]; - n0 = glm::cross(p1 - p0, p2 - p0); - n1 = n0; - n2 = n0; + if (face.normalIndices.count()) { + n0 = normals[face.normalIndices[0]]; + n1 = normals[face.normalIndices[1]]; + n2 = normals[face.normalIndices[2]]; + } else { // generate normals from triangle plane if not provided + n0 = n1 = n2 = glm::cross(v1 - v0, v2 - v0); } - - // we sum up the normal for each point and then divide by the count to get an average - pointNormalsSums[p0Index] += n0; - pointNormalsSums[p1Index] += n1; - pointNormalsSums[p2Index] += n2; - } - - int vertCount = mesh.vertices.count(); - for (int i = 0; i < vertCount; i++) { - float length = glm::length(pointNormalsSums[i]); - if (length > FLT_EPSILON) { - mesh.normals[i] = glm::normalize(pointNormalsSums[i]); + mesh.normals << n0 << n1 << n2; + if (face.textureUVIndices.count()) { + mesh.texCoords + << textureUVs[face.textureUVIndices[0]] + << textureUVs[face.textureUVIndices[1]] + << textureUVs[face.textureUVIndices[2]]; } } - - // XXX do same normal calculation for quadCount + meshPartCount++; } + + // if we got a hint about units, scale all the points + if (scaleGuess != 1.0f) { + for (int i = 0; i < mesh.vertices.size(); i++) { + mesh.vertices[i] *= scaleGuess; + } + } + + mesh.meshExtents.reset(); + foreach (const glm::vec3& vertex, mesh.vertices) { + mesh.meshExtents.addPoint(vertex); + geometry.meshExtents.addPoint(vertex); + } + //this->fbxDebugDump(geometry); } catch(const std::exception& e) { - qCDebug(modelformat) << "something went wrong in OBJ reader"; + qCDebug(modelformat) << "something went wrong in OBJ reader: " << e.what(); } return geometry; @@ -444,7 +369,7 @@ FBXGeometry readOBJ(QIODevice* device, const QVariantHash& mapping) { -void fbxDebugDump(const FBXGeometry& fbxgeo) { +void OBJReader::fbxDebugDump(const FBXGeometry& fbxgeo) { qCDebug(modelformat) << "---------------- fbxGeometry ----------------"; qCDebug(modelformat) << " hasSkeletonJoints =" << fbxgeo.hasSkeletonJoints; qCDebug(modelformat) << " offset =" << fbxgeo.offset; diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index a272e46f2d..a6ccbb1a2f 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -2,7 +2,57 @@ #include "FBXReader.h" -FBXGeometry readOBJ(const QByteArray& model, const QVariantHash& mapping); -FBXGeometry readOBJ(QIODevice* device, const QVariantHash& mapping); -void fbxDebugDump(const FBXGeometry& fbxgeo); -void setMeshPartDefaults(FBXMeshPart &meshPart, QString materialID); +class OBJTokenizer { +public: + OBJTokenizer(QIODevice* device); + enum SpecialToken { + NO_TOKEN = -1, + NO_PUSHBACKED_TOKEN = -1, + DATUM_TOKEN = 0x100, + COMMENT_TOKEN = 0x101 + }; + int nextToken(); + const QByteArray& getDatum() const { return _datum; } + bool isNextTokenFloat(); + void skipLine() { _device->readLine(); } + void pushBackToken(int token) { _pushedBackToken = token; } + void ungetChar(char ch) { _device->ungetChar(ch); } + const QString getComment() const { return _comment; } + glm::vec3 getVec3(); + glm::vec2 getVec2(); + +private: + float getFloat() { return std::stof((this->nextToken() != OBJTokenizer::DATUM_TOKEN) ? nullptr : this->getDatum().data()); } + QIODevice* _device; + QByteArray _datum; + int _pushedBackToken; + QString _comment; +}; + +class OBJFace { // A single face, with three or more planar vertices. But see triangulate(). +public: + QVector vertexIndices; + QVector textureUVIndices; + QVector normalIndices; + //materialName groupName // FIXME + // Add one more set of vertex data. Answers true if successful + bool add(QByteArray vertexIndex, QByteArray textureIndex = nullptr, QByteArray normalIndex = nullptr); + // Return a set of one or more OBJFaces from this one, in which each is just a triangle. + // Even though FBXMeshPart can handle quads, it would be messy to try to keep track of mixed-size faces, so we treat everything as triangles. + QVector triangulate(); +private: + void addFrom(OBJFace const * f, int i); +}; + +class OBJReader { +public: + typedef QVector FaceGroup; + QVector vertices; // all that we ever encounter while reading + QVector textureUVs; + QVector normals; + QVector faceGroups; + FBXGeometry readOBJ(const QByteArray& model, const QVariantHash& mapping); + FBXGeometry readOBJ(QIODevice* device, const QVariantHash& mapping); + void fbxDebugDump(const FBXGeometry& fbxgeo); + bool parseOBJGroup(OBJTokenizer &tokenizer, const QVariantHash& mapping, FBXGeometry &geometry, float& scaleGuess); +}; diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 9d71ec5cc2..4bc8e8a2f2 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -1796,9 +1796,7 @@ NetworkGeometry::NetworkGeometry(const QUrl& url, const QSharedPointer(), -1, 0.0f, 0.0f, glm::vec3(), glm::mat4(), glm::quat(), glm::quat(), - glm::quat(), glm::mat4(), glm::mat4(), glm::vec3(), glm::vec3(), glm::quat(), glm::quat(), - glm::mat4(), QString(""), glm::vec3(), glm::quat(), SHAPE_TYPE_NONE, false}; + FBXJoint joint = { false, QVector(), -1 }; _geometry.joints.append(joint); _geometry.leftEyeJointIndex = -1; _geometry.rightEyeJointIndex = -1; @@ -2107,7 +2105,26 @@ void GeometryReader::run() { } fbxgeo = readFBX(_reply, _mapping, grabLightmaps, lightmapLevel); } else if (_url.path().toLower().endsWith(".obj")) { - fbxgeo = readOBJ(_reply, _mapping); + fbxgeo = OBJReader().readOBJ(_reply, _mapping); + FBXMesh & mesh = fbxgeo.meshes[0]; // only one, by construction + if (mesh.texCoords.count() > 0) { // If we have uv texture coordinates.... + // ... then ensure that every meshPart has a texture filename. + // For now, that's defined directly, using the popular .obj convention that + // the texture is the same as the model basename. Later, we'll extend that + // with separate material files, too. + QString filename = _url.fileName(); + int extIndex = filename.lastIndexOf('.'); // by construction, this does not fail + QString basename = filename.remove(extIndex + 1, 3); + QByteArray defaultTexture = basename.toUtf8() + "jpg"; + //qCDebug(renderutils) << "basename for " << filename << " is " << basename << ", default:" << defaultTexture; + QVector & meshParts = mesh.parts; + for (int i = 0; i < meshParts.count(); i++) { + FBXMeshPart & meshPart = meshParts[i]; + if (meshPart.diffuseTexture.filename.count() == 0) { + meshPart.diffuseTexture.filename = defaultTexture; + } + } + } } QMetaObject::invokeMethod(geometry.data(), "setGeometry", Q_ARG(const FBXGeometry&, fbxgeo)); } else { From 5d729b98d6e3709b2d0269c92bd78acc11f1f8cb Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 29 Apr 2015 14:05:30 -0700 Subject: [PATCH 02/22] coding standard: brace placement --- libraries/fbx/src/OBJReader.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index 28f1e2f1ce..08dc57780a 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -130,16 +130,22 @@ void setMeshPartDefaults(FBXMeshPart &meshPart, QString materialID) { bool OBJFace::add(QByteArray vertexIndex, QByteArray textureIndex, QByteArray normalIndex) { bool ok; int index = vertexIndex.toInt(&ok); - if (!ok) { return false; } + if (!ok) { + return false; + } vertexIndices.append(index - 1); if (textureIndex != nullptr) { index = textureIndex.toInt(&ok); - if (!ok) { return false; } + if (!ok) { + return false; + } textureUVIndices.append(index - 1); } if (normalIndex != nullptr) { index = normalIndex.toInt(&ok); - if (!ok) { return false; } + if (!ok) { + return false; + } normalIndices.append(index - 1); } return true; From 59ed63027bee18e0b4b79ffa38aa2231fab548f8 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 29 Apr 2015 14:22:31 -0700 Subject: [PATCH 03/22] coding standard: magic numbers --- libraries/render-utils/src/GeometryCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 4bc8e8a2f2..a7bf840d3c 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -2114,7 +2114,7 @@ void GeometryReader::run() { // with separate material files, too. QString filename = _url.fileName(); int extIndex = filename.lastIndexOf('.'); // by construction, this does not fail - QString basename = filename.remove(extIndex + 1, 3); + QString basename = filename.remove(extIndex + 1, sizeof("obj")); QByteArray defaultTexture = basename.toUtf8() + "jpg"; //qCDebug(renderutils) << "basename for " << filename << " is " << basename << ", default:" << defaultTexture; QVector & meshParts = mesh.parts; From 1255d4614062cc417b5d15c74bc2d054d0828576 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 29 Apr 2015 15:19:40 -0700 Subject: [PATCH 04/22] coding standard: type formatting and variable names --- libraries/fbx/src/OBJReader.cpp | 24 ++++++++++---------- libraries/fbx/src/OBJReader.h | 4 ++-- libraries/render-utils/src/GeometryCache.cpp | 6 ++--- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index 08dc57780a..c27e470033 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -108,7 +108,7 @@ glm::vec2 OBJTokenizer::getVec2() { } -void setMeshPartDefaults(FBXMeshPart &meshPart, QString materialID) { +void setMeshPartDefaults(FBXMeshPart& meshPart, QString materialID) { meshPart.diffuseColor = glm::vec3(1, 1, 1); meshPart.specularColor = glm::vec3(1, 1, 1); meshPart.emissiveColor = glm::vec3(0, 0, 0); @@ -165,21 +165,21 @@ QVector OBJFace::triangulate() { } return newFaces; } -void OBJFace::addFrom(OBJFace const * f, int i) { // add using data from f at index i - vertexIndices.append(f->vertexIndices[i]); - if (f->textureUVIndices.count() > 0) { // Any at all. Runtime error if not consistent. - textureUVIndices.append(f->textureUVIndices[i]); +void OBJFace::addFrom(const OBJFace* face, int index) { // add using data from f at index i + vertexIndices.append(face->vertexIndices[index]); + if (face->textureUVIndices.count() > 0) { // Any at all. Runtime error if not consistent. + textureUVIndices.append(face->textureUVIndices[index]); } - if (f->normalIndices.count() > 0) { - normalIndices.append(f->normalIndices[i]); + if (face->normalIndices.count() > 0) { + normalIndices.append(face->normalIndices[index]); } } -bool OBJReader::parseOBJGroup(OBJTokenizer &tokenizer, const QVariantHash& mapping, FBXGeometry &geometry, float& scaleGuess) { +bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, FBXGeometry& geometry, float& scaleGuess) { FaceGroup faces; - FBXMesh &mesh = geometry.meshes[0]; + FBXMesh& mesh = geometry.meshes[0]; mesh.parts.append(FBXMeshPart()); - FBXMeshPart &meshPart = mesh.parts.last(); + FBXMeshPart& meshPart = mesh.parts.last(); bool sawG = false; bool result = true; int originalFaceCountForDebugging = 0; @@ -292,7 +292,7 @@ FBXGeometry OBJReader::readOBJ(QIODevice* device, const QVariantHash& mapping) { // add a new meshPart to the geometry's single mesh. while (parseOBJGroup(tokenizer, mapping, geometry, scaleGuess)) {} - FBXMesh &mesh = geometry.meshes[0]; + FBXMesh& mesh = geometry.meshes[0]; mesh.meshIndex = 0; geometry.joints.resize(1); @@ -320,7 +320,7 @@ FBXGeometry OBJReader::readOBJ(QIODevice* device, const QVariantHash& mapping) { int meshPartCount = 0; for (int i = 0; i < mesh.parts.count(); i++) { - FBXMeshPart & meshPart = mesh.parts[i]; + FBXMeshPart& meshPart = mesh.parts[i]; //qCDebug(modelformat) << "part:" << meshPartCount << " faces:" << faceGroups[meshPartCount].count() << "triangle indices will start with:" << mesh.vertices.count(); foreach(OBJFace face, faceGroups[meshPartCount]) { glm::vec3 v0 = vertices[face.vertexIndices[0]]; diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index a6ccbb1a2f..92feb574f0 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -41,7 +41,7 @@ public: // Even though FBXMeshPart can handle quads, it would be messy to try to keep track of mixed-size faces, so we treat everything as triangles. QVector triangulate(); private: - void addFrom(OBJFace const * f, int i); + void addFrom(const OBJFace* face, int index); }; class OBJReader { @@ -54,5 +54,5 @@ public: FBXGeometry readOBJ(const QByteArray& model, const QVariantHash& mapping); FBXGeometry readOBJ(QIODevice* device, const QVariantHash& mapping); void fbxDebugDump(const FBXGeometry& fbxgeo); - bool parseOBJGroup(OBJTokenizer &tokenizer, const QVariantHash& mapping, FBXGeometry &geometry, float& scaleGuess); + bool parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, FBXGeometry& geometry, float& scaleGuess); }; diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index a7bf840d3c..b5c60a7834 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -2106,7 +2106,7 @@ void GeometryReader::run() { fbxgeo = readFBX(_reply, _mapping, grabLightmaps, lightmapLevel); } else if (_url.path().toLower().endsWith(".obj")) { fbxgeo = OBJReader().readOBJ(_reply, _mapping); - FBXMesh & mesh = fbxgeo.meshes[0]; // only one, by construction + FBXMesh& mesh = fbxgeo.meshes[0]; // only one, by construction if (mesh.texCoords.count() > 0) { // If we have uv texture coordinates.... // ... then ensure that every meshPart has a texture filename. // For now, that's defined directly, using the popular .obj convention that @@ -2117,9 +2117,9 @@ void GeometryReader::run() { QString basename = filename.remove(extIndex + 1, sizeof("obj")); QByteArray defaultTexture = basename.toUtf8() + "jpg"; //qCDebug(renderutils) << "basename for " << filename << " is " << basename << ", default:" << defaultTexture; - QVector & meshParts = mesh.parts; + QVector& meshParts = mesh.parts; for (int i = 0; i < meshParts.count(); i++) { - FBXMeshPart & meshPart = meshParts[i]; + FBXMeshPart& meshPart = meshParts[i]; if (meshPart.diffuseTexture.filename.count() == 0) { meshPart.diffuseTexture.filename = defaultTexture; } From 1e81caab6cc254a66652f2e3b4224e35b09d266d Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 29 Apr 2015 15:37:39 -0700 Subject: [PATCH 05/22] Get rid of superflous this->. Add comments about side-effect of getFloat(). --- libraries/fbx/src/OBJReader.cpp | 16 ++++++++-------- libraries/fbx/src/OBJReader.h | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index c27e470033..f317f8d82e 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -91,18 +91,18 @@ bool OBJTokenizer::isNextTokenFloat() { } glm::vec3 OBJTokenizer::getVec3() { - auto v = glm::vec3(this->getFloat(), this->getFloat(), this->getFloat()); - while (this->isNextTokenFloat()) { + auto v = glm::vec3(getFloat(), getFloat(), getFloat()); // N.B.: getFloat() has side-effect + while (isNextTokenFloat()) { // the spec(s) get(s) vague here. might be w, might be a color... chop it off. - this->nextToken(); + nextToken(); } return v; } glm::vec2 OBJTokenizer::getVec2() { - auto v = glm::vec2(this->getFloat(), 1.0f - this->getFloat()); // OBJ has an odd sense of u, v - while (this->isNextTokenFloat()) { + auto v = glm::vec2(getFloat(), 1.0f - getFloat()); // OBJ has an odd sense of u, v. Also N.B.: getFloat() has side-effect + while (isNextTokenFloat()) { // there can be a w, but we don't handle that - this->nextToken(); + nextToken(); } return v; } @@ -275,7 +275,7 @@ done: FBXGeometry OBJReader::readOBJ(const QByteArray& model, const QVariantHash& mapping) { QBuffer buffer(const_cast(&model)); buffer.open(QIODevice::ReadOnly); - return this->readOBJ(&buffer, mapping); + return readOBJ(&buffer, mapping); } @@ -364,7 +364,7 @@ FBXGeometry OBJReader::readOBJ(QIODevice* device, const QVariantHash& mapping) { mesh.meshExtents.addPoint(vertex); geometry.meshExtents.addPoint(vertex); } - //this->fbxDebugDump(geometry); + //fbxDebugDump(geometry); } catch(const std::exception& e) { qCDebug(modelformat) << "something went wrong in OBJ reader: " << e.what(); diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index 92feb574f0..9bc931086a 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -22,7 +22,7 @@ public: glm::vec2 getVec2(); private: - float getFloat() { return std::stof((this->nextToken() != OBJTokenizer::DATUM_TOKEN) ? nullptr : this->getDatum().data()); } + float getFloat() { return std::stof((nextToken() != OBJTokenizer::DATUM_TOKEN) ? nullptr : getDatum().data()); } QIODevice* _device; QByteArray _datum; int _pushedBackToken; From 3c88146d175a998b7dca7859120aceaccd406a6a Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 29 Apr 2015 15:39:30 -0700 Subject: [PATCH 06/22] Fix indentation. --- libraries/fbx/src/OBJReader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index f317f8d82e..e51962e187 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -256,7 +256,7 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi foreach(OBJFace face, face.triangulate()) { faces.append(face); } - } else { + } else { // something we don't (yet) care about // qCDebug(modelformat) << "OBJ parser is skipping a line with" << token; tokenizer.skipLine(); From fb825f6d1000606909ecb4e9758d9140cb7530d1 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 29 Apr 2015 15:45:38 -0700 Subject: [PATCH 07/22] Noisy constant for number of vertices in a triangle. --- libraries/fbx/src/OBJReader.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index e51962e187..d7eb74dfbb 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -152,7 +152,8 @@ bool OBJFace::add(QByteArray vertexIndex, QByteArray textureIndex, QByteArray no } QVector OBJFace::triangulate() { QVector newFaces; - if (vertexIndices.count() == 3) { + const int nVerticesInATriangle = 3; + if (vertexIndices.count() == nVerticesInATriangle) { newFaces.append(*this); } else { for (int i = 1; i < vertexIndices.count() - 1; i++) { From 810242e182b1238d963d3883b8875b2c33745bca Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 29 Apr 2015 16:12:22 -0700 Subject: [PATCH 08/22] coding standard: comparison for pointer types (replaced with object isEmpty) --- libraries/fbx/src/OBJReader.cpp | 9 +++++---- libraries/fbx/src/OBJReader.h | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index d7eb74dfbb..e2ae739dbe 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -127,21 +127,21 @@ void setMeshPartDefaults(FBXMeshPart& meshPart, QString materialID) { } // OBJFace -bool OBJFace::add(QByteArray vertexIndex, QByteArray textureIndex, QByteArray normalIndex) { +bool OBJFace::add(const QByteArray& vertexIndex, const QByteArray& textureIndex, const QByteArray& normalIndex) { bool ok; int index = vertexIndex.toInt(&ok); if (!ok) { return false; } vertexIndices.append(index - 1); - if (textureIndex != nullptr) { + if (!textureIndex.isEmpty()) { index = textureIndex.toInt(&ok); if (!ok) { return false; } textureUVIndices.append(index - 1); } - if (normalIndex != nullptr) { + if (!normalIndex.isEmpty()) { index = normalIndex.toInt(&ok); if (!ok) { return false; @@ -250,7 +250,8 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi assert(parts.count() >= 1); assert(parts.count() <= 3); // FIXME: if we want to handle negative indices, it has to be done here. - face.add(parts[0], (parts.count() > 1) ? parts[1] : nullptr, (parts.count() > 2) ? parts[2] : nullptr); + const QByteArray noData {}; + face.add(parts[0], (parts.count() > 1) ? parts[1] : noData, (parts.count() > 2) ? parts[2] : noData); // FIXME: preserve current name, material and such } originalFaceCountForDebugging++; diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index 9bc931086a..6a798c9a50 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -36,7 +36,7 @@ public: QVector normalIndices; //materialName groupName // FIXME // Add one more set of vertex data. Answers true if successful - bool add(QByteArray vertexIndex, QByteArray textureIndex = nullptr, QByteArray normalIndex = nullptr); + bool add(const QByteArray& vertexIndex, const QByteArray& textureIndex, const QByteArray& normalIndex); // Return a set of one or more OBJFaces from this one, in which each is just a triangle. // Even though FBXMeshPart can handle quads, it would be messy to try to keep track of mixed-size faces, so we treat everything as triangles. QVector triangulate(); From 242001a589e726a523d3f17944a72f93b754ffbd Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 29 Apr 2015 22:17:44 -0700 Subject: [PATCH 09/22] Restore some very odd modularity used by VHACDUtil. --- libraries/fbx/src/OBJReader.h | 4 ++++ tools/vhacd-util/src/VHACDUtil.cpp | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index 6a798c9a50..1734518493 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -56,3 +56,7 @@ public: void fbxDebugDump(const FBXGeometry& fbxgeo); bool parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, FBXGeometry& geometry, float& scaleGuess); }; + +// What are these utilities doing here? Apparently used by fbx loading code in VHACD Utils. +void setMeshPartDefaults(FBXMeshPart &meshPart, QString materialID); +void fbxDebugDump(const FBXGeometry& fbxgeo); \ No newline at end of file diff --git a/tools/vhacd-util/src/VHACDUtil.cpp b/tools/vhacd-util/src/VHACDUtil.cpp index 92ae62db13..c24d6dc8f2 100644 --- a/tools/vhacd-util/src/VHACDUtil.cpp +++ b/tools/vhacd-util/src/VHACDUtil.cpp @@ -36,7 +36,7 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, FBXGeometry& result) { QByteArray fbxContents = fbx.readAll(); if (filename.toLower().endsWith(".obj")) { - result = readOBJ(fbxContents, QVariantHash()); + result = OBJReader().readOBJ(fbxContents, QVariantHash()); } else if (filename.toLower().endsWith(".fbx")) { result = readFBX(fbxContents, QVariantHash()); } else { From 7245ca4ff3c7484fa6866f9edbbc2f1d4eb5cb44 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 29 Apr 2015 22:57:41 -0700 Subject: [PATCH 10/22] According to the jenkins build report, Windows/MSVC has a different definition of std C++ than ISO does. Fine. Make our own cross-platform isdigit. --- libraries/fbx/src/OBJReader.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index e2ae739dbe..3421b3c366 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -176,6 +176,12 @@ void OBJFace::addFrom(const OBJFace* face, int index) { // add using data from f } } +// Hmmm. Build report for Windows (which I don't have) reports that MSVC thinks isdigit isn't part of std lib. +// Fine. I'll roll my own. +bool fakeIsDigit(int character) { + return ('0' <= character) && (character <= '9'); +} + bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, FBXGeometry& geometry, float& scaleGuess) { FaceGroup faces; FBXMesh& mesh = geometry.meshes[0]; @@ -242,7 +248,7 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi // vertex-index/texture-index // vertex-index/texture-index/surface-normal-index QByteArray token = tokenizer.getDatum(); - if (!std::isdigit(token[0])) { // Tokenizer treats line endings as whitespace. Non-digit indicates done; + if (!fakeIsDigit(token[0])) { // Tokenizer treats line endings as whitespace. Non-digit indicates done; tokenizer.pushBackToken(OBJTokenizer::DATUM_TOKEN); break; } From fd63cb7ed1dfd44a437267b44038b2e4569112e3 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 30 Apr 2015 07:28:13 -0700 Subject: [PATCH 11/22] Attempt to clean up cross-platform isdigit reference. --- libraries/fbx/src/OBJReader.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index 3421b3c366..d18f892c1b 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -15,6 +15,7 @@ #include #include +#include // This is the documented header for isdigit on MSVC. It should be unnecessary but safe on other platforms #include "FBXReader.h" #include "OBJReader.h" @@ -176,12 +177,6 @@ void OBJFace::addFrom(const OBJFace* face, int index) { // add using data from f } } -// Hmmm. Build report for Windows (which I don't have) reports that MSVC thinks isdigit isn't part of std lib. -// Fine. I'll roll my own. -bool fakeIsDigit(int character) { - return ('0' <= character) && (character <= '9'); -} - bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, FBXGeometry& geometry, float& scaleGuess) { FaceGroup faces; FBXMesh& mesh = geometry.meshes[0]; @@ -248,7 +243,7 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi // vertex-index/texture-index // vertex-index/texture-index/surface-normal-index QByteArray token = tokenizer.getDatum(); - if (!fakeIsDigit(token[0])) { // Tokenizer treats line endings as whitespace. Non-digit indicates done; + if (!std::isdigit(token[0])) { // Tokenizer treats line endings as whitespace. Non-digit indicates done; tokenizer.pushBackToken(OBJTokenizer::DATUM_TOKEN); break; } From 2687a7b038f3f655339ea17f4ed9dc7df0729d4f Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 30 Apr 2015 08:25:32 -0700 Subject: [PATCH 12/22] Take 2 on making MSVC happy. --- libraries/fbx/src/OBJReader.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index d18f892c1b..4473075e20 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -15,7 +15,7 @@ #include #include -#include // This is the documented header for isdigit on MSVC. It should be unnecessary but safe on other platforms +#include // .obj files are not locale-specific. The C/ASCII charset applies. #include "FBXReader.h" #include "OBJReader.h" @@ -243,7 +243,7 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi // vertex-index/texture-index // vertex-index/texture-index/surface-normal-index QByteArray token = tokenizer.getDatum(); - if (!std::isdigit(token[0])) { // Tokenizer treats line endings as whitespace. Non-digit indicates done; + if (!isdigit(token[0])) { // Tokenizer treats line endings as whitespace. Non-digit indicates done; tokenizer.pushBackToken(OBJTokenizer::DATUM_TOKEN); break; } From 2da8e1ab30ca509d02599078593fb8a475417e6c Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 30 Apr 2015 08:53:19 -0700 Subject: [PATCH 13/22] Pick up an upstream/master change that didn't get picked up in a merge of my fork/branch. --- libraries/render-utils/src/GeometryCache.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index b5c60a7834..32ea684b7c 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -1796,7 +1796,9 @@ NetworkGeometry::NetworkGeometry(const QUrl& url, const QSharedPointer(), -1 }; + FBXJoint joint = { false, QVector(), -1, 0.0f, 0.0f, glm::vec3(), glm::mat4(), glm::quat(), glm::quat(), + glm::quat(), glm::mat4(), glm::mat4(), glm::vec3(), glm::vec3(), glm::quat(), glm::quat(), + glm::mat4(), QString(""), glm::vec3(), glm::quat(), SHAPE_TYPE_NONE, false}; _geometry.joints.append(joint); _geometry.leftEyeJointIndex = -1; _geometry.rightEyeJointIndex = -1; From bf45f865a4ad4f30c4dd77869eb59afcef77e16d Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 30 Apr 2015 10:07:46 -0700 Subject: [PATCH 14/22] Codding standards, and fix an unintentionally duplicated declaration. --- libraries/fbx/src/OBJReader.cpp | 5 ++--- libraries/fbx/src/OBJReader.h | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index 4473075e20..328dcdca5e 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -368,8 +368,7 @@ FBXGeometry OBJReader::readOBJ(QIODevice* device, const QVariantHash& mapping) { geometry.meshExtents.addPoint(vertex); } //fbxDebugDump(geometry); - } - catch(const std::exception& e) { + } catch(const std::exception& e) { qCDebug(modelformat) << "something went wrong in OBJ reader: " << e.what(); } @@ -378,7 +377,7 @@ FBXGeometry OBJReader::readOBJ(QIODevice* device, const QVariantHash& mapping) { -void OBJReader::fbxDebugDump(const FBXGeometry& fbxgeo) { +void fbxDebugDump(const FBXGeometry& fbxgeo) { qCDebug(modelformat) << "---------------- fbxGeometry ----------------"; qCDebug(modelformat) << " hasSkeletonJoints =" << fbxgeo.hasSkeletonJoints; qCDebug(modelformat) << " offset =" << fbxgeo.offset; diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index 1734518493..0743cdeb89 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -53,10 +53,9 @@ public: QVector faceGroups; FBXGeometry readOBJ(const QByteArray& model, const QVariantHash& mapping); FBXGeometry readOBJ(QIODevice* device, const QVariantHash& mapping); - void fbxDebugDump(const FBXGeometry& fbxgeo); bool parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, FBXGeometry& geometry, float& scaleGuess); }; -// What are these utilities doing here? Apparently used by fbx loading code in VHACD Utils. -void setMeshPartDefaults(FBXMeshPart &meshPart, QString materialID); +// What are these utilities doing here? One is used by fbx loading code in VHACD Utils, and the other a general debugging utility. +void setMeshPartDefaults(FBXMeshPart& meshPart, QString materialID); void fbxDebugDump(const FBXGeometry& fbxgeo); \ No newline at end of file From e1d47413cc6b5e7078778ab3ecc331814e057395 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 1 May 2015 20:54:25 -0700 Subject: [PATCH 15/22] Material library handling. This is now functionally complete, but it needs testing, and the some tidying up. --- libraries/fbx/src/OBJReader.cpp | 183 +++++++++++++++++-- libraries/fbx/src/OBJReader.h | 33 +++- libraries/render-utils/src/GeometryCache.cpp | 21 +-- 3 files changed, 192 insertions(+), 45 deletions(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index 328dcdca5e..45010fb09d 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -15,8 +15,12 @@ #include #include +#include +#include +#include #include // .obj files are not locale-specific. The C/ASCII charset applies. +#include #include "FBXReader.h" #include "OBJReader.h" #include "Shape.h" @@ -26,6 +30,7 @@ QHash COMMENT_SCALE_HINTS = {{"This file uses centimeters as units", 1.0f / 100.0f}, {"This file uses millimeters as units", 1.0f / 1000.0f}}; +const QString SMART_DEFAULT_MATERIAL_NAME = "High Fidelity smart default material name"; OBJTokenizer::OBJTokenizer(QIODevice* device) : _device(device), _pushedBackToken(-1) { } @@ -158,10 +163,12 @@ QVector OBJFace::triangulate() { newFaces.append(*this); } else { for (int i = 1; i < vertexIndices.count() - 1; i++) { - OBJFace newFace; // FIXME: also copy materialName, groupName + OBJFace newFace; newFace.addFrom(this, 0); newFace.addFrom(this, i); newFace.addFrom(this, i + 1); + newFace.groupName = groupName; + newFace.materialName = materialName; newFaces.append(newFace); } } @@ -177,6 +184,69 @@ void OBJFace::addFrom(const OBJFace* face, int index) { // add using data from f } } +void OBJReader::parseMaterialLibrary(QIODevice* device) { + OBJTokenizer tokenizer(device); + QString matName = SMART_DEFAULT_MATERIAL_NAME; + OBJMaterial& currentMaterial = materials[matName]; + while (true) { + switch (tokenizer.nextToken()) { + case OBJTokenizer::COMMENT_TOKEN: + qCDebug(modelformat) << "OBJ Reader MTLLIB comment:" << tokenizer.getComment(); + break; + case OBJTokenizer::DATUM_TOKEN: + break; + default: + materials[matName] = currentMaterial; + qCDebug(modelformat) << "OBJ Reader Last material shininess:" << currentMaterial.shininess << " opacity:" << currentMaterial.opacity << " diffuse color:" << currentMaterial.diffuseColor << " specular color:" << currentMaterial.specularColor << " diffuse texture:" << currentMaterial.diffuseTextureFilename << " specular texture:" << currentMaterial.specularTextureFilename; + return; + } + QByteArray token = tokenizer.getDatum(); + if (token == "newmtl") { + if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { + return; + } + materials[matName] = currentMaterial; + matName = tokenizer.getDatum(); + currentMaterial = materials[matName]; + currentMaterial.diffuseTextureFilename = "test"; + qCDebug(modelformat) << "OBJ Reader Starting new material definition " << matName; + currentMaterial.diffuseTextureFilename = ""; + } else if (token == "Ns") { + currentMaterial.shininess = tokenizer.getFloat(); + } else if ((token == "d") || (token == "Tr")) { + currentMaterial.opacity = tokenizer.getFloat(); + } else if (token == "Ka") { + qCDebug(modelformat) << "OBJ Reader Ignoring material Ka " << tokenizer.getVec3(); + } else if (token == "Kd") { + currentMaterial.diffuseColor = tokenizer.getVec3(); + } else if (token == "Ks") { + currentMaterial.specularColor = tokenizer.getVec3(); + } else if (token == "map_Kd") { + if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { + return; + } + currentMaterial.diffuseTextureFilename = tokenizer.getDatum(); + } else if (token == "map_Ks") { + if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { + return; + } + currentMaterial.diffuseTextureFilename = tokenizer.getDatum(); + } + } +} + +QNetworkReply* OBJReader::request(QUrl& url, bool isTest) { + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + QNetworkRequest netRequest(url); + QNetworkReply* netReply = isTest ? networkAccessManager.head(netRequest) : networkAccessManager.get(netRequest); + QEventLoop loop; // Create an event loop that will quit when we get the finished signal + QObject::connect(netReply, SIGNAL(finished()), &loop, SLOT(quit())); + loop.exec(); // Nothing is going to happen on this whole run thread until we get this + netReply->waitForReadyRead(-1); // so we might as well block this thread waiting for the response, rather than + return netReply; // trying to sync later on. +} + + bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, FBXGeometry& geometry, float& scaleGuess) { FaceGroup faces; FBXMesh& mesh = geometry.meshes[0]; @@ -185,6 +255,7 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi bool sawG = false; bool result = true; int originalFaceCountForDebugging = 0; + QString currentGroup; setMeshPartDefaults(meshPart, QString("dontknow") + QString::number(mesh.parts.count())); @@ -220,8 +291,32 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi break; } QByteArray groupName = tokenizer.getDatum(); - meshPart.materialID = groupName; + currentGroup = groupName; //qCDebug(modelformat) << "new group:" << groupName; + } else if (token == "mtllib") { + if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { + break; + } + QByteArray libraryName = tokenizer.getDatum(); + if (librariesSeen.contains(libraryName)) { + break; // Some files use mtllib over and over again for the same libraryName + } + librariesSeen[libraryName] = true; + QUrl libraryUrl = url->resolved(QUrl(libraryName)); + qCDebug(modelformat) << "OBJ Reader new library:" << libraryName << " at:" << libraryUrl; + QNetworkReply* netReply = request(libraryUrl, false); + if (netReply->isFinished() && (netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200)) { + parseMaterialLibrary(netReply); + } else { + qCDebug(modelformat) << "OBJ Reader " << libraryName << " did not answer. Got " << netReply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString(); + } + netReply->deleteLater(); + } else if (token == "usemtl") { + if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { + break; + } + currentMaterialName = tokenizer.getDatum(); + qCDebug(modelformat) << "OBJ Reader new current material:" << currentMaterialName; } else if (token == "v") { vertices.append(tokenizer.getVec3()); } else if (token == "vn") { @@ -253,7 +348,8 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi // FIXME: if we want to handle negative indices, it has to be done here. const QByteArray noData {}; face.add(parts[0], (parts.count() > 1) ? parts[1] : noData, (parts.count() > 2) ? parts[2] : noData); - // FIXME: preserve current name, material and such + face.groupName = currentGroup; + face.materialName = currentMaterialName; } originalFaceCountForDebugging++; foreach(OBJFace face, face.triangulate()) { @@ -278,18 +374,19 @@ done: FBXGeometry OBJReader::readOBJ(const QByteArray& model, const QVariantHash& mapping) { QBuffer buffer(const_cast(&model)); buffer.open(QIODevice::ReadOnly); - return readOBJ(&buffer, mapping); + return readOBJ(&buffer, mapping, nullptr); } -FBXGeometry OBJReader::readOBJ(QIODevice* device, const QVariantHash& mapping) { +FBXGeometry OBJReader::readOBJ(QIODevice* device, const QVariantHash& mapping, QUrl* url) { FBXGeometry geometry; OBJTokenizer tokenizer(device); float scaleGuess = 1.0f; + this->url = url; geometry.meshExtents.reset(); geometry.meshes.append(FBXMesh()); - + try { // call parseOBJGroup as long as it's returning true. Each successful call will // add a new meshPart to the geometry's single mesh. @@ -321,11 +418,56 @@ FBXGeometry OBJReader::readOBJ(QIODevice* device, const QVariantHash& mapping) { 0, 0, 0, 1); mesh.clusters.append(cluster); - int meshPartCount = 0; - for (int i = 0; i < mesh.parts.count(); i++) { + // Some .obj files use the convention that a group with uv coordinates that doesn't define a material, should use a texture with the same basename as the .obj file. + QString filename = url->fileName(); + int extIndex = filename.lastIndexOf('.'); // by construction, this does not fail + QString basename = filename.remove(extIndex + 1, sizeof("obj")); + OBJMaterial& preDefinedMaterial = materials[SMART_DEFAULT_MATERIAL_NAME]; + preDefinedMaterial.diffuseColor = glm::vec3(1.0f); + QVector extensions = {"jpg", "jpeg", "png", "tga"}; + QByteArray base = basename.toUtf8(), textName = ""; + for (int i = 0; i < extensions.count(); i++) { + QByteArray candidateString = base + extensions[i]; + QUrl candidateUrl = url->resolved(QUrl(candidateString)); + QNetworkReply *netReply = request(candidateUrl, true); + if (netReply->isFinished() && (netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200)) { + textName = candidateString; + netReply->deleteLater(); + break; + } + netReply->deleteLater(); + } + if (!textName.isEmpty()) { + preDefinedMaterial.diffuseTextureFilename = textName; + } + materials[SMART_DEFAULT_MATERIAL_NAME] = preDefinedMaterial; + + for (int i = 0, meshPartCount = 0; i < mesh.parts.count(); i++, meshPartCount++) { FBXMeshPart& meshPart = mesh.parts[i]; - //qCDebug(modelformat) << "part:" << meshPartCount << " faces:" << faceGroups[meshPartCount].count() << "triangle indices will start with:" << mesh.vertices.count(); - foreach(OBJFace face, faceGroups[meshPartCount]) { + FaceGroup faceGroup = faceGroups[meshPartCount]; + OBJFace leadFace = faceGroup[0]; // All the faces in the same group will have the same name and material. + QString groupMaterialName = leadFace.materialName; + if ((leadFace.textureUVIndices.count() > 0) && groupMaterialName.isEmpty()) { + groupMaterialName = SMART_DEFAULT_MATERIAL_NAME; + } + if (!groupMaterialName.isEmpty()) { + if (!materials.contains(groupMaterialName)) { + qCDebug(modelformat) << "OBJ Reader:" << url << " references undefined material " << groupMaterialName << "."; + } else { + OBJMaterial* material = &materials[groupMaterialName]; + // The code behind this is in transition. Some things are set directly in the FXBMeshPart... + meshPart.materialID = groupMaterialName; + meshPart.diffuseTexture.filename = material->diffuseTextureFilename; + meshPart.specularTexture.filename = material->specularTextureFilename; + // ... and some things are set in the underlying material. + meshPart._material->setDiffuse(material->diffuseColor); + meshPart._material->setSpecular(material->specularColor); + meshPart._material->setShininess(material->shininess); + meshPart._material->setOpacity(material->opacity); + } + } + qCDebug(modelformat) << "OBJ Reader part:" << meshPartCount << "name:" << leadFace.groupName << "material:" << groupMaterialName << "diffuse:" << meshPart._material->getDiffuse() << "faces:" << faceGroup.count() << "triangle indices will start with:" << mesh.vertices.count(); + foreach(OBJFace face, faceGroup) { glm::vec3 v0 = vertices[face.vertexIndices[0]]; glm::vec3 v1 = vertices[face.vertexIndices[1]]; glm::vec3 v2 = vertices[face.vertexIndices[2]]; @@ -352,7 +494,6 @@ FBXGeometry OBJReader::readOBJ(QIODevice* device, const QVariantHash& mapping) { << textureUVs[face.textureUVIndices[2]]; } } - meshPartCount++; } // if we got a hint about units, scale all the points @@ -367,9 +508,9 @@ FBXGeometry OBJReader::readOBJ(QIODevice* device, const QVariantHash& mapping) { mesh.meshExtents.addPoint(vertex); geometry.meshExtents.addPoint(vertex); } - //fbxDebugDump(geometry); + fbxDebugDump(geometry); } catch(const std::exception& e) { - qCDebug(modelformat) << "something went wrong in OBJ reader: " << e.what(); + qCDebug(modelformat) << "OBJ reader fail: " << e.what(); } return geometry; @@ -386,11 +527,11 @@ void fbxDebugDump(const FBXGeometry& fbxgeo) { foreach (FBXMesh mesh, fbxgeo.meshes) { qCDebug(modelformat) << " vertices.count() =" << mesh.vertices.count(); qCDebug(modelformat) << " normals.count() =" << mesh.normals.count(); - if (mesh.normals.count() == mesh.vertices.count()) { + /*if (mesh.normals.count() == mesh.vertices.count()) { for (int i = 0; i < mesh.normals.count(); i++) { qCDebug(modelformat) << " " << mesh.vertices[ i ] << mesh.normals[ i ]; } - } + }*/ qCDebug(modelformat) << " tangents.count() =" << mesh.tangents.count(); qCDebug(modelformat) << " colors.count() =" << mesh.colors.count(); qCDebug(modelformat) << " texCoords.count() =" << mesh.texCoords.count(); @@ -403,13 +544,15 @@ void fbxDebugDump(const FBXGeometry& fbxgeo) { foreach (FBXMeshPart meshPart, mesh.parts) { qCDebug(modelformat) << " quadIndices.count() =" << meshPart.quadIndices.count(); qCDebug(modelformat) << " triangleIndices.count() =" << meshPart.triangleIndices.count(); - qCDebug(modelformat) << " diffuseColor =" << meshPart.diffuseColor; - qCDebug(modelformat) << " specularColor =" << meshPart.specularColor; - qCDebug(modelformat) << " emissiveColor =" << meshPart.emissiveColor; + qCDebug(modelformat) << " diffuseColor =" << meshPart.diffuseColor << "mat =" << meshPart._material->getDiffuse(); + qCDebug(modelformat) << " specularColor =" << meshPart.specularColor << "mat =" << meshPart._material->getSpecular(); + qCDebug(modelformat) << " emissiveColor =" << meshPart.emissiveColor << "mat =" << meshPart._material->getEmissive(); qCDebug(modelformat) << " emissiveParams =" << meshPart.emissiveParams; - qCDebug(modelformat) << " shininess =" << meshPart.shininess; - qCDebug(modelformat) << " opacity =" << meshPart.opacity; + qCDebug(modelformat) << " shininess =" << meshPart.shininess << "mat =" << meshPart._material->getShininess(); + qCDebug(modelformat) << " opacity =" << meshPart.opacity << "mat =" << meshPart._material->getOpacity(); qCDebug(modelformat) << " materialID =" << meshPart.materialID; + qCDebug(modelformat) << " diffuse texture =" << meshPart.diffuseTexture.filename; + qCDebug(modelformat) << " specular texture =" << meshPart.specularTexture.filename; } qCDebug(modelformat) << " clusters.count() =" << mesh.clusters.count(); foreach (FBXCluster cluster, mesh.clusters) { diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index 0743cdeb89..16b13e00bf 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -1,5 +1,5 @@ - +#include #include "FBXReader.h" class OBJTokenizer { @@ -20,9 +20,9 @@ public: const QString getComment() const { return _comment; } glm::vec3 getVec3(); glm::vec2 getVec2(); + float getFloat() { return std::stof((nextToken() != OBJTokenizer::DATUM_TOKEN) ? nullptr : getDatum().data()); } private: - float getFloat() { return std::stof((nextToken() != OBJTokenizer::DATUM_TOKEN) ? nullptr : getDatum().data()); } QIODevice* _device; QByteArray _datum; int _pushedBackToken; @@ -34,7 +34,8 @@ public: QVector vertexIndices; QVector textureUVIndices; QVector normalIndices; - //materialName groupName // FIXME + QString groupName; // We don't make use of hierarchical structure, but it can be preserved for debugging and future use. + QString materialName; // Add one more set of vertex data. Answers true if successful bool add(const QByteArray& vertexIndex, const QByteArray& textureIndex, const QByteArray& normalIndex); // Return a set of one or more OBJFaces from this one, in which each is just a triangle. @@ -44,16 +45,38 @@ private: void addFrom(const OBJFace* face, int index); }; -class OBJReader { +// Materials and references to material names can come in any order, and different mesh parts can refer to the same material. +// Therefore it would get pretty hacky to try to use FBXMeshPart to store these as we traverse the files. +class OBJMaterial { +public: + float shininess; + float opacity; + glm::vec3 diffuseColor; + glm::vec3 specularColor; + QByteArray diffuseTextureFilename; + QByteArray specularTextureFilename; + OBJMaterial() : opacity(1.0f) {} +}; + +class OBJReader: public QObject { // QObject so we can make network requests. + Q_OBJECT public: typedef QVector FaceGroup; QVector vertices; // all that we ever encounter while reading QVector textureUVs; QVector normals; QVector faceGroups; + QString currentMaterialName; + QHash materials; + QUrl* url; + + QNetworkReply* request(QUrl& url, bool isTest); FBXGeometry readOBJ(const QByteArray& model, const QVariantHash& mapping); - FBXGeometry readOBJ(QIODevice* device, const QVariantHash& mapping); + FBXGeometry readOBJ(QIODevice* device, const QVariantHash& mapping, QUrl* url); +private: + QHash librariesSeen; bool parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, FBXGeometry& geometry, float& scaleGuess); + void parseMaterialLibrary(QIODevice* device); }; // What are these utilities doing here? One is used by fbx loading code in VHACD Utils, and the other a general debugging utility. diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 32ea684b7c..b341fe7dd5 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -2107,26 +2107,7 @@ void GeometryReader::run() { } fbxgeo = readFBX(_reply, _mapping, grabLightmaps, lightmapLevel); } else if (_url.path().toLower().endsWith(".obj")) { - fbxgeo = OBJReader().readOBJ(_reply, _mapping); - FBXMesh& mesh = fbxgeo.meshes[0]; // only one, by construction - if (mesh.texCoords.count() > 0) { // If we have uv texture coordinates.... - // ... then ensure that every meshPart has a texture filename. - // For now, that's defined directly, using the popular .obj convention that - // the texture is the same as the model basename. Later, we'll extend that - // with separate material files, too. - QString filename = _url.fileName(); - int extIndex = filename.lastIndexOf('.'); // by construction, this does not fail - QString basename = filename.remove(extIndex + 1, sizeof("obj")); - QByteArray defaultTexture = basename.toUtf8() + "jpg"; - //qCDebug(renderutils) << "basename for " << filename << " is " << basename << ", default:" << defaultTexture; - QVector& meshParts = mesh.parts; - for (int i = 0; i < meshParts.count(); i++) { - FBXMeshPart& meshPart = meshParts[i]; - if (meshPart.diffuseTexture.filename.count() == 0) { - meshPart.diffuseTexture.filename = defaultTexture; - } - } - } + fbxgeo = OBJReader().readOBJ(_reply, _mapping, &_url); } QMetaObject::invokeMethod(geometry.data(), "setGeometry", Q_ARG(const FBXGeometry&, fbxgeo)); } else { From eb05099261dfc87d14b32067f7302b09e4597db0 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 1 May 2015 21:39:19 -0700 Subject: [PATCH 16/22] (Try to) Address header file-location-discrepency between xcode and the jenkinds build environment. --- libraries/fbx/src/OBJReader.cpp | 4 ++-- libraries/fbx/src/OBJReader.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index 45010fb09d..86f75f614c 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -15,8 +15,8 @@ #include #include -#include -#include +#include +#include #include #include // .obj files are not locale-specific. The C/ASCII charset applies. diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index 16b13e00bf..65a65c70ee 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -1,5 +1,5 @@ -#include +#include #include "FBXReader.h" class OBJTokenizer { From 302aa9db47b4f44da478ab68c06e6587fc2add18 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sat, 2 May 2015 00:49:52 -0700 Subject: [PATCH 17/22] Removing access to the gl widget and display managers --- interface/src/Application.cpp | 125 ++++++++--- interface/src/Application.h | 31 ++- interface/src/Camera.cpp | 50 +---- interface/src/Camera.h | 18 -- interface/src/GLCanvas.cpp | 26 --- interface/src/audio/AudioToolBox.cpp | 31 ++- interface/src/audio/AudioToolBox.h | 7 +- interface/src/avatar/Avatar.cpp | 1 - interface/src/avatar/Head.cpp | 3 +- interface/src/avatar/MyAvatar.cpp | 51 +++-- interface/src/devices/OculusManager.cpp | 21 +- interface/src/devices/OculusManager.h | 3 +- interface/src/devices/SixenseManager.cpp | 1 - interface/src/devices/TV3DManager.cpp | 19 +- interface/src/main.cpp | 7 +- .../ControllerScriptingInterface.cpp | 3 +- .../src/scripting/HMDScriptingInterface.h | 1 - .../scripting/WindowScriptingInterface.cpp | 2 +- interface/src/ui/ApplicationOverlay.cpp | 204 +++++++++--------- interface/src/ui/ApplicationOverlay.h | 43 ++-- interface/src/ui/HMDToolsDialog.cpp | 2 - interface/src/ui/NodeBounds.cpp | 4 +- interface/src/ui/PreferencesDialog.cpp | 9 +- interface/src/ui/RearMirrorTools.cpp | 34 +-- interface/src/ui/RearMirrorTools.h | 19 +- interface/src/ui/Snapshot.cpp | 12 +- interface/src/ui/Snapshot.h | 6 +- interface/src/ui/Stats.cpp | 20 +- .../src/AbstractViewStateInterface.h | 2 +- libraries/render-utils/src/GlowEffect.cpp | 19 +- libraries/render-utils/src/GlowEffect.h | 6 +- libraries/render-utils/src/TextureCache.cpp | 41 ++-- libraries/render-utils/src/TextureCache.h | 9 +- libraries/shared/src/GLMHelpers.cpp | 4 + libraries/shared/src/GLMHelpers.h | 5 + 35 files changed, 400 insertions(+), 439 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d0d01f4534..0acb41c3c5 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -801,7 +801,12 @@ void Application::initializeUi() { void Application::paintGL() { PROFILE_RANGE(__FUNCTION__); + _glWidget->makeCurrent(); PerformanceTimer perfTimer("paintGL"); + //Need accurate frame timing for the oculus rift + if (OculusManager::isConnected()) { + OculusManager::beginFrameTiming(); + } PerformanceWarning::setSuppressShortTimings(Menu::getInstance()->isOptionChecked(MenuOption::SuppressShortTimings)); bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); @@ -847,11 +852,6 @@ void Application::paintGL() { glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror); } - // Update camera position - if (!OculusManager::isConnected()) { - _myCamera.update(1.0f / _fps); - } - if (getShadowsEnabled()) { updateShadowMap(); } @@ -859,12 +859,10 @@ void Application::paintGL() { if (OculusManager::isConnected()) { //When in mirror mode, use camera rotation. Otherwise, use body rotation if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { - OculusManager::display(_myCamera.getRotation(), _myCamera.getPosition(), _myCamera); + OculusManager::display(_glWidget, _myCamera.getRotation(), _myCamera.getPosition(), _myCamera); } else { - OculusManager::display(_myAvatar->getWorldAlignedOrientation(), _myAvatar->getDefaultEyePosition(), _myCamera); + OculusManager::display(_glWidget, _myAvatar->getWorldAlignedOrientation(), _myAvatar->getDefaultEyePosition(), _myCamera); } - _myCamera.update(1.0f / _fps); - } else if (TV3DManager::isConnected()) { TV3DManager::display(_myCamera); @@ -883,8 +881,7 @@ void Application::paintGL() { glPopMatrix(); if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { - _rearMirrorTools->render(true); - + _rearMirrorTools->render(true, _glWidget->mapFromGlobal(QCursor::pos())); } else if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { renderRearViewMirror(_mirrorViewRect); } @@ -898,6 +895,13 @@ void Application::paintGL() { } } + if (!OculusManager::isConnected() || OculusManager::allowSwap()) { + _glWidget->swapBuffers(); + } + + if (OculusManager::isConnected()) { + OculusManager::endFrameTiming(); + } _frameCount++; } @@ -931,6 +935,7 @@ void Application::resetCamerasOnResizeGL(Camera& camera, int width, int height) } void Application::resizeGL(int width, int height) { + DependencyManager::get()->setFrameBufferSize(QSize(width, height)); resetCamerasOnResizeGL(_myCamera, width, height); glViewport(0, 0, width, height); // shouldn't this account for the menu??? @@ -1298,8 +1303,7 @@ void Application::keyPressEvent(QKeyEvent* event) { if (!event->isAutoRepeat()) { // this starts an HFActionEvent HFActionEvent startActionEvent(HFActionEvent::startType(), - _myCamera.computePickRay(getTrueMouseX(), - getTrueMouseY())); + computePickRay(getTrueMouseX(), getTrueMouseY())); sendEvent(this, &startActionEvent); } @@ -1404,8 +1408,7 @@ void Application::keyReleaseEvent(QKeyEvent* event) { if (!event->isAutoRepeat()) { // this ends the HFActionEvent HFActionEvent endActionEvent(HFActionEvent::endType(), - _myCamera.computePickRay(getTrueMouseX(), - getTrueMouseY())); + computePickRay(getTrueMouseX(), getTrueMouseY())); sendEvent(this, &endActionEvent); } break; @@ -1499,7 +1502,7 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { // nobody handled this - make it an action event on the _window object HFActionEvent actionEvent(HFActionEvent::startType(), - _myCamera.computePickRay(event->x(), event->y())); + computePickRay(event->x(), event->y())); sendEvent(this, &actionEvent); } else if (event->button() == Qt::RightButton) { @@ -1534,7 +1537,7 @@ void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { // fire an action end event HFActionEvent actionEvent(HFActionEvent::endType(), - _myCamera.computePickRay(event->x(), event->y())); + computePickRay(event->x(), event->y())); sendEvent(this, &actionEvent); } } @@ -2104,19 +2107,16 @@ void Application::init() { _entityClipboardRenderer.setViewFrustum(getViewFrustum()); _entityClipboardRenderer.setTree(&_entityClipboard); - _rearMirrorTools = new RearMirrorTools(_glWidget, _mirrorViewRect); + _rearMirrorTools = new RearMirrorTools(_mirrorViewRect); connect(_rearMirrorTools, SIGNAL(closeView()), SLOT(closeMirrorView())); connect(_rearMirrorTools, SIGNAL(restoreView()), SLOT(restoreMirrorView())); connect(_rearMirrorTools, SIGNAL(shrinkView()), SLOT(shrinkMirrorView())); connect(_rearMirrorTools, SIGNAL(resetView()), SLOT(resetSensors())); - // make sure our texture cache knows about window size changes - DependencyManager::get()->associateWithWidget(_glWidget); - // initialize the GlowEffect with our widget - DependencyManager::get()->init(_glWidget, - Menu::getInstance()->isOptionChecked(MenuOption::EnableGlowEffect)); + bool glow = Menu::getInstance()->isOptionChecked(MenuOption::EnableGlowEffect); + DependencyManager::get()->init(glow); } void Application::closeMirrorView() { @@ -2167,7 +2167,7 @@ void Application::updateMouseRay() { // make sure the frustum is up-to-date loadViewFrustum(_myCamera, _viewFrustum); - PickRay pickRay = _myCamera.computePickRay(getTrueMouseX(), getTrueMouseY()); + PickRay pickRay = computePickRay(getTrueMouseX(), getTrueMouseY()); _mouseRayOrigin = pickRay.origin; _mouseRayDirection = pickRay.direction; @@ -2979,8 +2979,17 @@ int Application::getBoundaryLevelAdjust() const { return DependencyManager::get()->getBoundaryLevelAdjust(); } -PickRay Application::computePickRay(float x, float y) { - return getCamera()->computePickRay(x, y); +PickRay Application::computePickRay(float x, float y) const { + glm::vec2 size = getCanvasSize(); + x /= size.x; + y /= size.y; + PickRay result; + if (isHMDMode()) { + ApplicationOverlay::computeHmdPickRay(glm::vec2(x, y), result.origin, result.direction); + } else { + getViewFrustum()->computePickRay(x, y, result.origin, result.direction); + } + return result; } QImage Application::renderAvatarBillboard() { @@ -3013,6 +3022,16 @@ ViewFrustum* Application::getViewFrustum() { return &_viewFrustum; } +const ViewFrustum* Application::getViewFrustum() const { +#ifdef DEBUG + if (QThread::currentThread() == activeRenderingThread) { + // FIXME, should this be an assert? + qWarning() << "Calling Application::getViewFrustum() from the active rendering thread, did you mean Application::getDisplayViewFrustum()?"; + } +#endif + return &_viewFrustum; +} + ViewFrustum* Application::getDisplayViewFrustum() { #ifdef DEBUG if (QThread::currentThread() != activeRenderingThread) { @@ -3376,7 +3395,6 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) { _mirrorCamera.setAspectRatio((float)region.width() / region.height()); _mirrorCamera.setRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI, 0.0f))); - _mirrorCamera.update(1.0f/_fps); // set the bounds of rear mirror view if (billboard) { @@ -3402,7 +3420,7 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) { glPopMatrix(); if (!billboard) { - _rearMirrorTools->render(false); + _rearMirrorTools->render(false, _glWidget->mapFromGlobal(QCursor::pos())); } // reset Viewport and projection matrix @@ -4283,7 +4301,7 @@ void Application::takeSnapshot() { player->setMedia(QUrl::fromLocalFile(inf.absoluteFilePath())); player->play(); - QString fileName = Snapshot::saveSnapshot(); + QString fileName = Snapshot::saveSnapshot(_glWidget->grabFrameBuffer()); AccountManager& accountManager = AccountManager::getInstance(); if (!accountManager.isLoggedIn()) { @@ -4471,3 +4489,52 @@ void Application::friendsWindowClosed() { void Application::postLambdaEvent(std::function f) { QCoreApplication::postEvent(this, new LambdaEvent(f)); } + +void Application::initPlugins() { + OculusManager::init(); +} + +void Application::shutdownPlugins() { + OculusManager::deinit(); +} + +glm::vec3 Application::getHeadPosition() const { + return OculusManager::getRelativePosition(); +} + + +glm::quat Application::getHeadOrientation() const { + return OculusManager::getOrientation(); +} + +glm::uvec2 Application::getCanvasSize() const { + return glm::uvec2(_glWidget->width(), _glWidget->height()); +} + +QSize Application::getDeviceSize() const { + return _glWidget->getDeviceSize(); +} + +int Application::getTrueMouseX() const { + return _glWidget->mapFromGlobal(QCursor::pos()).x(); +} + +int Application::getTrueMouseY() const { + return _glWidget->mapFromGlobal(QCursor::pos()).y(); +} + +bool Application::isThrottleRendering() const { + return _glWidget->isThrottleRendering(); +} + +PickRay Application::computePickRay() const { + return computePickRay(getTrueMouseX(), getTrueMouseY()); +} + +bool Application::hasFocus() const { + return _glWidget->hasFocus(); +} + +void Application::resizeGL() { + this->resizeGL(_glWidget->getDeviceWidth(), _glWidget->getDeviceHeight()); +} diff --git a/interface/src/Application.h b/interface/src/Application.h index 9f87d05711..027e0e20de 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -144,6 +144,8 @@ public: static glm::quat getOrientationForPath() { return getInstance()->_myAvatar->getOrientation(); } static glm::vec3 getPositionForAudio() { return getInstance()->_myAvatar->getHead()->getPosition(); } static glm::quat getOrientationForAudio() { return getInstance()->_myAvatar->getHead()->getFinalOrientationInWorldFrame(); } + static void initPlugins(); + static void shutdownPlugins(); Application(int& argc, char** argv, QElapsedTimer &startup_time); ~Application(); @@ -159,6 +161,8 @@ public: void paintGL(); void resizeGL(int width, int height); + void resizeEvent(QResizeEvent * size); + void keyPressEvent(QKeyEvent* event); void keyReleaseEvent(QKeyEvent* event); @@ -180,12 +184,19 @@ public: bool event(QEvent* event); bool eventFilter(QObject* object, QEvent* event); - GLCanvas* getGLWidget() { return _glWidget; } - bool isThrottleRendering() const { return _glWidget->isThrottleRendering(); } + glm::uvec2 getCanvasSize() const; + QSize getDeviceSize() const; + bool hasFocus() const; + PickRay computePickRay() const; + PickRay computeViewPickRay(float xRatio, float yRatio) const; + void resizeGL(); + + bool isThrottleRendering() const; Camera* getCamera() { return &_myCamera; } // Represents the current view frustum of the avatar. ViewFrustum* getViewFrustum(); + const ViewFrustum* getViewFrustum() const; // Represents the view frustum of the current rendering pass, // which might be different from the viewFrustum, i.e. shadowmap // passes, mirror window passes, etc @@ -207,8 +218,9 @@ public: bool mouseOnScreen() const; int getMouseX() const; int getMouseY() const; - int getTrueMouseX() const { return _glWidget->mapFromGlobal(QCursor::pos()).x(); } - int getTrueMouseY() const { return _glWidget->mapFromGlobal(QCursor::pos()).y(); } + glm::ivec2 getTrueMousePosition() const; + int getTrueMouseX() const; + int getTrueMouseY() const; int getMouseDragStartedX() const; int getMouseDragStartedY() const; int getTrueMouseDragStartedX() const { return _mouseDragStartedX; } @@ -219,6 +231,7 @@ public: QSystemTrayIcon* getTrayIcon() { return _trayIcon; } ApplicationOverlay& getApplicationOverlay() { return _applicationOverlay; } + const ApplicationOverlay& getApplicationOverlay() const { return _applicationOverlay; } Overlays& getOverlays() { return _overlays; } float getFps() const { return _fps; } @@ -278,15 +291,15 @@ public: virtual QThread* getMainThread() { return thread(); } virtual float getSizeScale() const; virtual int getBoundaryLevelAdjust() const; - virtual PickRay computePickRay(float x, float y); + virtual PickRay computePickRay(float x, float y) const; virtual const glm::vec3& getAvatarPosition() const { return _myAvatar->getPosition(); } NodeBounds& getNodeBoundsDisplay() { return _nodeBoundsDisplay; } FileLogger* getLogger() { return _logger; } - glm::vec2 getViewportDimensions() const { return glm::vec2(_glWidget->getDeviceWidth(), - _glWidget->getDeviceHeight()); } + glm::vec2 getViewportDimensions() const; + NodeToJurisdictionMap& getEntityServerJurisdictions() { return _entityServerJurisdictions; } void skipVersion(QString latestVersion); @@ -308,7 +321,9 @@ public: // rendering of several elements depend on that // TODO: carry that information on the Camera as a setting bool isHMDMode() const; - + glm::quat getHeadOrientation() const; + glm::vec3 getHeadPosition() const; + QRect getDesirableApplicationGeometry(); RunningScriptsWidget* getRunningScriptsWidget() { return _runningScriptsWidget; } diff --git a/interface/src/Camera.cpp b/interface/src/Camera.cpp index 6c6d165e7d..7db598bdfc 100644 --- a/interface/src/Camera.cpp +++ b/interface/src/Camera.cpp @@ -17,7 +17,6 @@ #include "Camera.h" #include "Menu.h" #include "Util.h" -#include "devices/OculusManager.h" CameraMode stringToMode(const QString& mode) { @@ -54,42 +53,24 @@ Camera::Camera() : _nearClip(DEFAULT_NEAR_CLIP), // default _farClip(DEFAULT_FAR_CLIP), // default _hmdPosition(), - _hmdRotation(), - _isKeepLookingAt(false), - _lookingAt(0.0f, 0.0f, 0.0f) + _hmdRotation() { } -void Camera::update(float deltaTime) { - if (_isKeepLookingAt) { - lookAt(_lookingAt); - } - return; -} - void Camera::setPosition(const glm::vec3& position) { _position = position; } void Camera::setRotation(const glm::quat& rotation) { _rotation = rotation; - if (_isKeepLookingAt) { - lookAt(_lookingAt); - } } void Camera::setHmdPosition(const glm::vec3& hmdPosition) { _hmdPosition = hmdPosition; - if (_isKeepLookingAt) { - lookAt(_lookingAt); - } } void Camera::setHmdRotation(const glm::quat& hmdRotation) { _hmdRotation = hmdRotation; - if (_isKeepLookingAt) { - lookAt(_lookingAt); - } } float Camera::getFarClip() const { @@ -120,21 +101,6 @@ void Camera::setFarClip(float f) { _farClip = f; } -PickRay Camera::computePickRay(float x, float y) { - auto glCanvas = Application::getInstance()->getGLWidget(); - return computeViewPickRay(x / glCanvas->width(), y / glCanvas->height()); -} - -PickRay Camera::computeViewPickRay(float xRatio, float yRatio) { - PickRay result; - if (OculusManager::isConnected()) { - Application::getInstance()->getApplicationOverlay().computeOculusPickRay(xRatio, yRatio, result.origin, result.direction); - } else { - Application::getInstance()->getViewFrustum()->computePickRay(xRatio, yRatio, result.origin, result.direction); - } - return result; -} - void Camera::setModeString(const QString& mode) { CameraMode targetMode = stringToMode(mode); @@ -163,17 +129,3 @@ void Camera::setModeString(const QString& mode) { QString Camera::getModeString() const { return modeToString(_mode); } - -void Camera::lookAt(const glm::vec3& lookAt) { - glm::vec3 up = IDENTITY_UP; - glm::mat4 lookAtMatrix = glm::lookAt(_position, lookAt, up); - glm::quat rotation = glm::quat_cast(lookAtMatrix); - rotation.w = -rotation.w; // Rosedale approved - _rotation = rotation; -} - -void Camera::keepLookingAt(const glm::vec3& point) { - lookAt(point); - _isKeepLookingAt = true; - _lookingAt = point; -} diff --git a/interface/src/Camera.h b/interface/src/Camera.h index 10572d3513..b2eef3a8cb 100644 --- a/interface/src/Camera.h +++ b/interface/src/Camera.h @@ -41,8 +41,6 @@ public: void initialize(); // instantly put the camera at the ideal position and rotation. - void update( float deltaTime ); - void setRotation(const glm::quat& rotation); void setHmdPosition(const glm::vec3& hmdPosition); void setHmdRotation(const glm::quat& hmdRotation); @@ -75,20 +73,6 @@ public slots: void setOrientation(const glm::quat& orientation) { setRotation(orientation); } glm::quat getOrientation() const { return getRotation(); } - - PickRay computePickRay(float x, float y); - PickRay computeViewPickRay(float xRatio, float yRatio); - - // These only work on independent cameras - /// one time change to what the camera is looking at - void lookAt(const glm::vec3& value); - - /// fix what the camera is looking at, and keep the camera looking at this even if position changes - void keepLookingAt(const glm::vec3& value); - - /// stops the keep looking at feature, doesn't change what's being looked at, but will stop camera from - /// continuing to update it's orientation to keep looking at the item - void stopLooking() { _isKeepLookingAt = false; } signals: void modeUpdated(const QString& newMode); @@ -105,8 +89,6 @@ private: glm::quat _rotation; glm::vec3 _hmdPosition; glm::quat _hmdRotation; - bool _isKeepLookingAt; - glm::vec3 _lookingAt; }; #endif // hifi_Camera_h diff --git a/interface/src/GLCanvas.cpp b/interface/src/GLCanvas.cpp index 9a9512a0b0..f9dfda77ad 100644 --- a/interface/src/GLCanvas.cpp +++ b/interface/src/GLCanvas.cpp @@ -16,7 +16,6 @@ #include "Application.h" #include "GLCanvas.h" #include "MainWindow.h" -#include "devices/OculusManager.h" const int MSECS_PER_FRAME_WHEN_THROTTLED = 66; @@ -60,21 +59,7 @@ void GLCanvas::initializeGL() { void GLCanvas::paintGL() { if (!_throttleRendering && !Application::getInstance()->getWindow()->isMinimized()) { - //Need accurate frame timing for the oculus rift - if (OculusManager::isConnected()) { - OculusManager::beginFrameTiming(); - } - Application::getInstance()->paintGL(); - - if (!OculusManager::isConnected()) { - swapBuffers(); - } else { - if (OculusManager::allowSwap()) { - swapBuffers(); - } - OculusManager::endFrameTiming(); - } } } @@ -110,18 +95,7 @@ void GLCanvas::activeChanged(Qt::ApplicationState state) { void GLCanvas::throttleRender() { _frameTimer.start(_idleRenderInterval); if (!Application::getInstance()->getWindow()->isMinimized()) { - //Need accurate frame timing for the oculus rift - if (OculusManager::isConnected()) { - OculusManager::beginFrameTiming(); - } - - makeCurrent(); Application::getInstance()->paintGL(); - swapBuffers(); - - if (OculusManager::isConnected()) { - OculusManager::endFrameTiming(); - } } } diff --git a/interface/src/audio/AudioToolBox.cpp b/interface/src/audio/AudioToolBox.cpp index 330e7bc194..4a5c0a7610 100644 --- a/interface/src/audio/AudioToolBox.cpp +++ b/interface/src/audio/AudioToolBox.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include "Application.h" #include "AudioToolBox.h" @@ -38,15 +39,14 @@ bool AudioToolBox::mousePressEvent(int x, int y) { void AudioToolBox::render(int x, int y, bool boxed) { glEnable(GL_TEXTURE_2D); - auto glCanvas = Application::getInstance()->getGLWidget(); - if (_micTextureId == 0) { - _micTextureId = glCanvas->bindTexture(QImage(PathUtils::resourcesPath() + "images/mic.svg")); + if (!_micTexture) { + _micTexture = DependencyManager::get()->getImageTexture(PathUtils::resourcesPath() + "images/mic.svg"); } - if (_muteTextureId == 0) { - _muteTextureId = glCanvas->bindTexture(QImage(PathUtils::resourcesPath() + "images/mic-mute.svg")); + if (!_muteTexture) { + _muteTexture = DependencyManager::get()->getImageTexture(PathUtils::resourcesPath() + "images/mic-mute.svg"); } - if (_boxTextureId == 0) { - _boxTextureId = glCanvas->bindTexture(QImage(PathUtils::resourcesPath() + "images/audio-box.svg")); + if (_boxTexture) { + _boxTexture = DependencyManager::get()->getImageTexture(PathUtils::resourcesPath() + "images/audio-box.svg"); } auto audioIO = DependencyManager::get(); @@ -59,11 +59,8 @@ void AudioToolBox::render(int x, int y, bool boxed) { const int BOX_HEIGHT = 44; QRect boxBounds = QRect(x - BOX_LEFT_PADDING, y - BOX_TOP_PADDING, BOX_WIDTH, BOX_HEIGHT); - - glBindTexture(GL_TEXTURE_2D, _boxTextureId); - glm::vec4 quadColor; - + if (isClipping) { quadColor = glm::vec4(1.0f, 0.0f, 0.0f, 1.0f); } else { @@ -71,9 +68,9 @@ void AudioToolBox::render(int x, int y, bool boxed) { } glm::vec2 topLeft(boxBounds.left(), boxBounds.top()); glm::vec2 bottomRight(boxBounds.right(), boxBounds.bottom()); - glm::vec2 texCoordTopLeft(1,1); - glm::vec2 texCoordBottomRight(0,0); - + static const glm::vec2 texCoordTopLeft(1,1); + static const glm::vec2 texCoordBottomRight(0, 0); + glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_boxTexture)); DependencyManager::get()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, quadColor); } @@ -81,10 +78,10 @@ void AudioToolBox::render(int x, int y, bool boxed) { _iconBounds = QRect(x, y, MUTE_ICON_SIZE, MUTE_ICON_SIZE); if (!audioIO->isMuted()) { - glBindTexture(GL_TEXTURE_2D, _micTextureId); + glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_micTexture)); iconColor = 1.0f; } else { - glBindTexture(GL_TEXTURE_2D, _muteTextureId); + glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_muteTexture)); // Make muted icon pulsate static const float PULSE_MIN = 0.4f; @@ -112,6 +109,6 @@ void AudioToolBox::render(int x, int y, bool boxed) { } DependencyManager::get()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, quadColor, _boxQuadID); - + glBindTexture(GL_TEXTURE_2D, 0); glDisable(GL_TEXTURE_2D); } \ No newline at end of file diff --git a/interface/src/audio/AudioToolBox.h b/interface/src/audio/AudioToolBox.h index 526de89b9c..d209b9930a 100644 --- a/interface/src/audio/AudioToolBox.h +++ b/interface/src/audio/AudioToolBox.h @@ -14,6 +14,7 @@ #include #include +#include class AudioToolBox : public Dependency { SINGLETON_DEPENDENCY @@ -24,9 +25,9 @@ public: protected: AudioToolBox(); private: - GLuint _micTextureId = 0; - GLuint _muteTextureId = 0; - GLuint _boxTextureId = 0; + gpu::TexturePointer _micTexture; + gpu::TexturePointer _muteTexture; + gpu::TexturePointer _boxTexture; int _boxQuadID = GeometryCache::UNKNOWN_ID; QRect _iconBounds; qint64 _iconPulseTimeReference = 0; diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 05f834255c..ef21ea918d 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -45,7 +45,6 @@ #include "Recorder.h" #include "Util.h" #include "world.h" -#include "devices/OculusManager.h" #include "InterfaceLogging.h" using namespace std; diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 41c2e9b54c..08e5984d1d 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -22,7 +22,6 @@ #include "Util.h" #include "devices/DdeFaceTracker.h" #include "devices/Faceshift.h" -#include "devices/OculusManager.h" using namespace std; @@ -309,7 +308,7 @@ glm::quat Head::getCameraOrientation() const { // to change the driving direction while in Oculus mode. It is used to support driving toward where you're // head is looking. Note that in oculus mode, your actual camera view and where your head is looking is not // always the same. - if (OculusManager::isConnected()) { + if (qApp->isHMDMode()) { return getOrientation(); } Avatar* owningAvatar = static_cast(_owningAvatar); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 557d630ebf..db59633c4c 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -42,7 +42,6 @@ #include "Physics.h" #include "Recorder.h" #include "devices/Faceshift.h" -#include "devices/OculusManager.h" #include "Util.h" #include "InterfaceLogging.h" @@ -230,12 +229,14 @@ void MyAvatar::simulate(float deltaTime) { void MyAvatar::updateFromTrackers(float deltaTime) { glm::vec3 estimatedPosition, estimatedRotation; - if (isPlaying() && !OculusManager::isConnected()) { + bool inHmd = qApp->isHMDMode(); + + if (isPlaying() && inHmd) { return; } - - if (OculusManager::isConnected()) { - estimatedPosition = OculusManager::getRelativePosition(); + + if (inHmd) { + estimatedPosition = qApp->getHeadPosition(); estimatedPosition.x *= -1.0f; _trackedHeadPosition = estimatedPosition; @@ -273,7 +274,7 @@ void MyAvatar::updateFromTrackers(float deltaTime) { Head* head = getHead(); - if (OculusManager::isConnected() || isPlaying()) { + if (inHmd || isPlaying()) { head->setDeltaPitch(estimatedRotation.x); head->setDeltaYaw(estimatedRotation.y); } else { @@ -293,7 +294,7 @@ void MyAvatar::updateFromTrackers(float deltaTime) { // NOTE: this is kinda a hack, it's the same hack we use to make the head tilt. But it's not really a mirror // it just makes you feel like you're looking in a mirror because the body movements of the avatar appear to // match your body movements. - if (OculusManager::isConnected() && Application::getInstance()->getCamera()->getMode() == CAMERA_MODE_MIRROR) { + if (inHmd && Application::getInstance()->getCamera()->getMode() == CAMERA_MODE_MIRROR) { relativePosition.x = -relativePosition.x; } @@ -882,8 +883,10 @@ void MyAvatar::updateLookAtTargetAvatar() { howManyLookingAtMe++; // Have that avatar look directly at my camera // Philip TODO: correct to look at left/right eye - if (OculusManager::isConnected()) { - avatar->getHead()->setCorrectedLookAtPosition(OculusManager::getLeftEyePosition()); + if (qApp->isHMDMode()) { + avatar->getHead()->setCorrectedLookAtPosition(Application::getInstance()->getViewFrustum()->getPosition()); + // FIXME what is the point of this? + // avatar->getHead()->setCorrectedLookAtPosition(OculusManager::getLeftEyePosition()); } else { avatar->getHead()->setCorrectedLookAtPosition(Application::getInstance()->getViewFrustum()->getPosition()); } @@ -1177,7 +1180,7 @@ void MyAvatar::renderBody(ViewFrustum* renderFrustum, RenderArgs::RenderMode ren renderFrustum->setNearClip(DEFAULT_NEAR_CLIP); } else { float clipDistance = _skeletonModel.getHeadClipDistance(); - if (OculusManager::isConnected()) { + if (qApp->isHMDMode()) { // If avatar is horizontally in front of camera, increase clip distance by the amount it is in front. glm::vec3 cameraToAvatar = _position - cameraPos; cameraToAvatar.y = 0.0f; @@ -1221,7 +1224,7 @@ void MyAvatar::updateOrientation(float deltaTime) { // Gather rotation information from keyboard const float TIME_BETWEEN_HMD_TURNS = 0.5f; const float HMD_TURN_DEGREES = 22.5f; - if (!OculusManager::isConnected()) { + if (!qApp->isHMDMode()) { // Smoothly rotate body with arrow keys if not in HMD _bodyYawDelta -= _driveKeys[ROT_RIGHT] * YAW_SPEED * deltaTime; _bodyYawDelta += _driveKeys[ROT_LEFT] * YAW_SPEED * deltaTime; @@ -1255,29 +1258,23 @@ void MyAvatar::updateOrientation(float deltaTime) { float MINIMUM_ROTATION_RATE = 2.0f; if (fabs(_bodyYawDelta) < MINIMUM_ROTATION_RATE) { _bodyYawDelta = 0.0f; } - if (OculusManager::isConnected()) { + if (qApp->isHMDMode()) { // these angles will be in radians - float yaw, pitch, roll; - OculusManager::getEulerAngles(yaw, pitch, roll); + glm::quat orientation = qApp->getHeadOrientation(); // ... so they need to be converted to degrees before we do math... - yaw *= DEGREES_PER_RADIAN; - pitch *= DEGREES_PER_RADIAN; - roll *= DEGREES_PER_RADIAN; - + glm::vec3 euler = glm::eulerAngles(orientation) * DEGREES_PER_RADIAN; + //Invert yaw and roll when in mirror mode - Head* head = getHead(); if (Application::getInstance()->getCamera()->getMode() == CAMERA_MODE_MIRROR) { - head->setBaseYaw(-yaw); - head->setBasePitch(pitch); - head->setBaseRoll(-roll); - } else { - head->setBaseYaw(yaw); - head->setBasePitch(pitch); - head->setBaseRoll(roll); + YAW(euler) *= -1.0; + ROLL(euler) *= -1.0; } + Head* head = getHead(); + head->setBaseYaw(YAW(euler)); + head->setBasePitch(PITCH(euler)); + head->setBaseRoll(ROLL(euler)); } - } glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVelocity, bool isHovering) { diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index 7d719873f4..2d6141aac9 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -455,8 +455,7 @@ void OculusManager::configureCamera(Camera& camera, int screenWidth, int screenH } //Displays everything for the oculus, frame timing must be active -void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &position, Camera& whichCamera) { - auto glCanvas = Application::getInstance()->getGLWidget(); +void OculusManager::display(QGLWidget * glCanvas, const glm::quat &bodyOrientation, const glm::vec3 &position, Camera& whichCamera) { #ifdef DEBUG // Ensure the frame counter always increments by exactly 1 @@ -593,7 +592,6 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p glm::vec3(_eyeRenderDesc[eye].HmdToEyeViewOffset.x, _eyeRenderDesc[eye].HmdToEyeViewOffset.y, _eyeRenderDesc[eye].HmdToEyeViewOffset.z)); _eyePositions[eye] = thisEyePosition; - _camera->update(1.0f / Application::getInstance()->getFps()); glMatrixMode(GL_PROJECTION); glLoadIdentity(); @@ -617,7 +615,7 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p _camera->setEyeOffsetPosition(glm::vec3(-_eyeRenderDesc[eye].HmdToEyeViewOffset.x, -_eyeRenderDesc[eye].HmdToEyeViewOffset.y, -_eyeRenderDesc[eye].HmdToEyeViewOffset.z)); Application::getInstance()->displaySide(*_camera, false, RenderArgs::MONO); - applicationOverlay.displayOverlayTextureOculus(*_camera); + applicationOverlay.displayOverlayTextureHmd(*_camera); }); _activeEye = ovrEye_Count; @@ -638,7 +636,8 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p glPopMatrix(); // restore our normal viewport - glViewport(0, 0, glCanvas->getDeviceWidth(), glCanvas->getDeviceHeight()); + auto deviceSize = qApp->getDeviceSize(); + glViewport(0, 0, deviceSize.width(), deviceSize.height()); #if 0 if (debugFrame && !timerActive) { @@ -707,8 +706,8 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p void OculusManager::renderDistortionMesh(ovrPosef eyeRenderPose[ovrEye_Count]) { glLoadIdentity(); - auto glCanvas = Application::getInstance()->getGLWidget(); - glOrtho(0, glCanvas->getDeviceWidth(), 0, glCanvas->getDeviceHeight(), -1.0, 1.0); + auto deviceSize = qApp->getDeviceSize(); + glOrtho(0, deviceSize.width(), 0, deviceSize.height(), -1.0, 1.0); glDisable(GL_DEPTH_TEST); @@ -795,8 +794,12 @@ void OculusManager::getEulerAngles(float& yaw, float& pitch, float& roll) { glm::vec3 OculusManager::getRelativePosition() { ovrTrackingState trackingState = ovrHmd_GetTrackingState(_ovrHmd, ovr_GetTimeInSeconds()); ovrVector3f headPosition = trackingState.HeadPose.ThePose.Position; - - return glm::vec3(headPosition.x, headPosition.y, headPosition.z); + return toGlm(trackingState.HeadPose.ThePose.Position); +} + +glm::quat OculusManager::getOrientation() { + ovrTrackingState trackingState = ovrHmd_GetTrackingState(_ovrHmd, ovr_GetTimeInSeconds()); + return toGlm(trackingState.HeadPose.ThePose.Orientation); } //Used to set the size of the glow framebuffers diff --git a/interface/src/devices/OculusManager.h b/interface/src/devices/OculusManager.h index 4b1bc98fb2..e41c6e8f9b 100644 --- a/interface/src/devices/OculusManager.h +++ b/interface/src/devices/OculusManager.h @@ -61,7 +61,7 @@ public: static void endFrameTiming(); static bool allowSwap(); static void configureCamera(Camera& camera, int screenWidth, int screenHeight); - static void display(const glm::quat &bodyOrientation, const glm::vec3 &position, Camera& whichCamera); + static void display(QGLWidget * glCanvas, const glm::quat &bodyOrientation, const glm::vec3 &position, Camera& whichCamera); static void reset(); /// param \yaw[out] yaw in radians @@ -69,6 +69,7 @@ public: /// param \roll[out] roll in radians static void getEulerAngles(float& yaw, float& pitch, float& roll); static glm::vec3 getRelativePosition(); + static glm::quat getOrientation(); static QSize getRenderTargetSize(); static void overrideOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& nearVal, diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index 2b85a0906f..d74e210642 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -16,7 +16,6 @@ #include "Application.h" #include "SixenseManager.h" -#include "devices/OculusManager.h" #include "UserActivityLogger.h" #include "InterfaceLogging.h" diff --git a/interface/src/devices/TV3DManager.cpp b/interface/src/devices/TV3DManager.cpp index 05d89d5a6d..90030b2f84 100644 --- a/interface/src/devices/TV3DManager.cpp +++ b/interface/src/devices/TV3DManager.cpp @@ -33,12 +33,9 @@ bool TV3DManager::isConnected() { } void TV3DManager::connect() { - auto glCanvas = Application::getInstance()->getGLWidget(); - int width = glCanvas->getDeviceWidth(); - int height = glCanvas->getDeviceHeight(); Camera& camera = *Application::getInstance()->getCamera(); - - configureCamera(camera, width, height); + auto deviceSize = qApp->getDeviceSize(); + configureCamera(camera, deviceSize.width(), deviceSize.height()); } @@ -91,9 +88,8 @@ void TV3DManager::display(Camera& whichCamera) { // left eye portal int portalX = 0; int portalY = 0; - auto glCanvas = Application::getInstance()->getGLWidget(); - QSize deviceSize = glCanvas->getDeviceSize() * - Application::getInstance()->getRenderResolutionScale(); + QSize deviceSize = qApp->getDeviceSize() * + qApp->getRenderResolutionScale(); int portalW = deviceSize.width() / 2; int portalH = deviceSize.height(); @@ -122,8 +118,9 @@ void TV3DManager::display(Camera& whichCamera) { glLoadIdentity(); // reset projection matrix glFrustum(_leftEye.left, _leftEye.right, _leftEye.bottom, _leftEye.top, nearZ, farZ); // set left view frustum GLfloat p[4][4]; + // Really? glGetFloatv(GL_PROJECTION_MATRIX, &(p[0][0])); - GLfloat cotangent = p[1][1]; + float cotangent = p[1][1]; GLfloat fov = atan(1.0f / cotangent); glTranslatef(_leftEye.modelTranslation, 0.0, 0.0); // translate to cancel parallax @@ -132,7 +129,7 @@ void TV3DManager::display(Camera& whichCamera) { eyeCamera.setEyeOffsetPosition(glm::vec3(-_activeEye->modelTranslation,0,0)); Application::getInstance()->displaySide(eyeCamera, false, RenderArgs::MONO); - applicationOverlay.displayOverlayTexture3DTV(whichCamera, _aspect, fov); + applicationOverlay.displayOverlayTextureStereo(whichCamera, _aspect, fov); _activeEye = NULL; } glPopMatrix(); @@ -161,7 +158,7 @@ void TV3DManager::display(Camera& whichCamera) { eyeCamera.setEyeOffsetPosition(glm::vec3(-_activeEye->modelTranslation,0,0)); Application::getInstance()->displaySide(eyeCamera, false, RenderArgs::MONO); - applicationOverlay.displayOverlayTexture3DTV(whichCamera, _aspect, fov); + applicationOverlay.displayOverlayTextureStereo(whichCamera, _aspect, fov); _activeEye = NULL; } glPopMatrix(); diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 7112944375..ae1b93959f 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -18,7 +18,6 @@ #include "AddressManager.h" #include "Application.h" -#include "devices/OculusManager.h" #include "InterfaceLogging.h" #ifdef Q_OS_WIN @@ -97,8 +96,8 @@ int main(int argc, const char* argv[]) { // Oculus initialization MUST PRECEDE OpenGL context creation. // The nature of the Application constructor means this has to be either here, // or in the main window ctor, before GL startup. - OculusManager::init(); - + Application::initPlugins(); + int exitCode; { QSettings::setDefaultFormat(QSettings::IniFormat); @@ -112,7 +111,7 @@ int main(int argc, const char* argv[]) { exitCode = app.exec(); } - OculusManager::deinit(); + Application::shutdownPlugins(); #ifdef Q_OS_WIN ReleaseMutex(mutex); #endif diff --git a/interface/src/scripting/ControllerScriptingInterface.cpp b/interface/src/scripting/ControllerScriptingInterface.cpp index 7d6012c880..5f12d73b37 100644 --- a/interface/src/scripting/ControllerScriptingInterface.cpp +++ b/interface/src/scripting/ControllerScriptingInterface.cpp @@ -286,8 +286,7 @@ void ControllerScriptingInterface::releaseJoystick(int joystickIndex) { } glm::vec2 ControllerScriptingInterface::getViewportDimensions() const { - auto glCanvas = Application::getInstance()->getGLWidget(); - return glm::vec2(glCanvas->width(), glCanvas->height()); + return Application::getInstance()->getCanvasSize(); } AbstractInputController* ControllerScriptingInterface::createInputController(const QString& deviceName, const QString& tracker) { diff --git a/interface/src/scripting/HMDScriptingInterface.h b/interface/src/scripting/HMDScriptingInterface.h index bcd458ca5e..9b0f8dc4d1 100644 --- a/interface/src/scripting/HMDScriptingInterface.h +++ b/interface/src/scripting/HMDScriptingInterface.h @@ -15,7 +15,6 @@ #include #include "Application.h" -#include "devices/OculusManager.h" class HMDScriptingInterface : public QObject { Q_OBJECT diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index bd2903863d..3aae6a4d4a 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -41,7 +41,7 @@ WebWindowClass* WindowScriptingInterface::doCreateWebWindow(const QString& title } QScriptValue WindowScriptingInterface::hasFocus() { - return Application::getInstance()->getGLWidget()->hasFocus(); + return Application::getInstance()->hasFocus(); } void WindowScriptingInterface::setFocus() { diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 8704a61261..7733720a8a 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -12,10 +12,13 @@ #include "InterfaceConfig.h" #include +#include #include #include #include +#include +#include #include #include @@ -25,7 +28,6 @@ #include "audio/AudioToolBox.h" #include "Application.h" #include "ApplicationOverlay.h" -#include "devices/OculusManager.h" #include "Util.h" #include "ui/Stats.h" @@ -45,7 +47,7 @@ const float CONNECTION_STATUS_BORDER_LINE_WIDTH = 4.0f; static const float MOUSE_PITCH_RANGE = 1.0f * PI; static const float MOUSE_YAW_RANGE = 0.5f * TWO_PI; - +static const glm::vec2 MOUSE_RANGE(MOUSE_YAW_RANGE, MOUSE_PITCH_RANGE); // Return a point's cartesian coordinates on a sphere from pitch and yaw glm::vec3 getPoint(float yaw, float pitch) { @@ -133,7 +135,7 @@ void ApplicationOverlay::renderReticle(glm::quat orientation, float alpha) { } ApplicationOverlay::ApplicationOverlay() : - _textureFov(glm::radians(DEFAULT_OCULUS_UI_ANGULAR_SIZE)), + _textureFov(glm::radians(DEFAULT_HMD_UI_ANGULAR_SIZE)), _textureAspectRatio(1.0f), _lastMouseMove(0), _magnifier(true), @@ -185,10 +187,10 @@ ApplicationOverlay::~ApplicationOverlay() { void ApplicationOverlay::renderOverlay() { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "ApplicationOverlay::displayOverlay()"); Overlays& overlays = qApp->getOverlays(); - auto glCanvas = Application::getInstance()->getGLWidget(); - _textureFov = glm::radians(_oculusUIAngularSize); - _textureAspectRatio = (float)glCanvas->getDeviceWidth() / (float)glCanvas->getDeviceHeight(); + _textureFov = glm::radians(_hmdUIAngularSize); + auto deviceSize = Application::getInstance()->getDeviceSize(); + _textureAspectRatio = (float)deviceSize.width() / (float)deviceSize.height(); //Handle fading and deactivation/activation of UI @@ -201,12 +203,12 @@ void ApplicationOverlay::renderOverlay() { _overlays.buildFramebufferObject(); _overlays.bind(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - + glPushMatrix(); { const float NEAR_CLIP = -10000; const float FAR_CLIP = 10000; glLoadIdentity(); - glOrtho(0, glCanvas->width(), glCanvas->height(), 0, NEAR_CLIP, FAR_CLIP); + glOrtho(0, deviceSize.width(), deviceSize.height(), 0, NEAR_CLIP, FAR_CLIP); glMatrixMode(GL_MODELVIEW); @@ -278,7 +280,7 @@ void ApplicationOverlay::displayOverlayTexture() { } // Draws the FBO texture for Oculus rift. -void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { +void ApplicationOverlay::displayOverlayTextureHmd(Camera& whichCamera) { if (_alpha == 0.0f) { return; } @@ -356,7 +358,7 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { } // Draws the FBO texture for 3DTV. -void ApplicationOverlay::displayOverlayTexture3DTV(Camera& whichCamera, float aspectRatio, float fov) { +void ApplicationOverlay::displayOverlayTextureStereo(Camera& whichCamera, float aspectRatio, float fov) { if (_alpha == 0.0f) { return; } @@ -411,19 +413,19 @@ void ApplicationOverlay::displayOverlayTexture3DTV(Camera& whichCamera, float as overlayColor); }); - auto glCanvas = Application::getInstance()->getGLWidget(); - if (_crosshairTexture == 0) { - _crosshairTexture = glCanvas->bindTexture(QImage(PathUtils::resourcesPath() + "images/sixense-reticle.png")); + if (!_crosshairTexture) { + _crosshairTexture = DependencyManager::get()-> + getImageTexture(PathUtils::resourcesPath() + "images/sixense-reticle.png"); } //draw the mouse pointer - glBindTexture(GL_TEXTURE_2D, _crosshairTexture); - - const float reticleSize = 40.0f / glCanvas->width() * quadWidth; + glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture)); + glm::vec2 canvasSize = qApp->getCanvasSize(); + const float reticleSize = 40.0f / canvasSize.x * quadWidth; x -= reticleSize / 2.0f; y += reticleSize / 2.0f; - const float mouseX = (qApp->getMouseX() / (float)glCanvas->width()) * quadWidth; - const float mouseY = (1.0 - (qApp->getMouseY() / (float)glCanvas->height())) * quadHeight; + const float mouseX = (qApp->getMouseX() / (float)canvasSize.x) * quadWidth; + const float mouseY = (1.0 - (qApp->getMouseY() / (float)canvasSize.y)) * quadHeight; glm::vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f }; @@ -447,24 +449,23 @@ void ApplicationOverlay::displayOverlayTexture3DTV(Camera& whichCamera, float as glEnable(GL_LIGHTING); } -void ApplicationOverlay::computeOculusPickRay(float x, float y, glm::vec3& origin, glm::vec3& direction) const { +void ApplicationOverlay::computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) { const MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); - const float pitch = (0.5f - y) * MOUSE_PITCH_RANGE; - const float yaw = (0.5f - x) * MOUSE_YAW_RANGE; - const glm::quat orientation(glm::vec3(pitch, yaw, 0.0f)); + cursorPos = 0.5f - cursorPos; + cursorPos *= MOUSE_RANGE; + const glm::quat orientation(glm::vec3(cursorPos, 0.0f)); const glm::vec3 localDirection = orientation * IDENTITY_FRONT; // Get cursor position - const glm::vec3 cursorPos = myAvatar->getDefaultEyePosition() + myAvatar->getOrientation() * localDirection; + const glm::vec3 cursorDir = myAvatar->getDefaultEyePosition() + myAvatar->getOrientation() * localDirection; // Ray start where the eye position is and stop where the cursor is origin = myAvatar->getEyePosition(); - direction = cursorPos - origin; + direction = cursorDir - origin; } //Caculate the click location using one of the sixense controllers. Scale is not applied QPoint ApplicationOverlay::getPalmClickLocation(const PalmData *palm) const { - auto glCanvas = Application::getInstance()->getGLWidget(); MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); glm::vec3 tip = myAvatar->getLaserPointerTipPosition(palm); @@ -475,8 +476,8 @@ QPoint ApplicationOverlay::getPalmClickLocation(const PalmData *palm) const { glm::vec3 tipPos = invOrientation * (tip - eyePos); QPoint rv; - - if (OculusManager::isConnected()) { + auto canvasSize = qApp->getCanvasSize(); + if (qApp->isHMDMode()) { float t; //We back the ray up by dir to ensure that it will not start inside the UI. @@ -495,8 +496,8 @@ QPoint ApplicationOverlay::getPalmClickLocation(const PalmData *palm) const { float u = asin(collisionPos.x) / (_textureFov)+0.5f; float v = 1.0 - (asin(collisionPos.y) / (_textureFov)+0.5f); - rv.setX(u * glCanvas->width()); - rv.setY(v * glCanvas->height()); + rv.setX(u * canvasSize.x); + rv.setY(v * canvasSize.y); } } else { //if they did not click on the overlay, just set the coords to INT_MAX @@ -513,8 +514,8 @@ QPoint ApplicationOverlay::getPalmClickLocation(const PalmData *palm) const { ndcSpacePos = glm::vec3(clipSpacePos) / clipSpacePos.w; } - rv.setX(((ndcSpacePos.x + 1.0) / 2.0) * glCanvas->width()); - rv.setY((1.0 - ((ndcSpacePos.y + 1.0) / 2.0)) * glCanvas->height()); + rv.setX(((ndcSpacePos.x + 1.0) / 2.0) * canvasSize.x); + rv.setY((1.0 - ((ndcSpacePos.y + 1.0) / 2.0)) * canvasSize.y); } return rv; } @@ -539,18 +540,17 @@ bool ApplicationOverlay::calculateRayUICollisionPoint(const glm::vec3& position, //Renders optional pointers void ApplicationOverlay::renderPointers() { - auto glCanvas = Application::getInstance()->getGLWidget(); - //lazily load crosshair texture if (_crosshairTexture == 0) { - _crosshairTexture = glCanvas->bindTexture(QImage(PathUtils::resourcesPath() + "images/sixense-reticle.png")); + _crosshairTexture = DependencyManager::get()-> + getImageTexture(PathUtils::resourcesPath() + "images/sixense-reticle.png"); } glEnable(GL_TEXTURE_2D); glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, _crosshairTexture); - - if (OculusManager::isConnected() && !qApp->getLastMouseMoveWasSimulated() && !qApp->isMouseHidden()) { + glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture)); + + if (qApp->isHMDMode() && !qApp->getLastMouseMoveWasSimulated() && !qApp->isMouseHidden()) { //If we are in oculus, render reticle later if (_lastMouseMove == 0) { _lastMouseMove = usecTimestampNow(); @@ -561,9 +561,9 @@ void ApplicationOverlay::renderPointers() { if (_reticlePosition[MOUSE] != position) { _lastMouseMove = usecTimestampNow(); } else if (usecTimestampNow() - _lastMouseMove > MAX_IDLE_TIME * USECS_PER_SECOND) { - float pitch = 0.0f, yaw = 0.0f, roll = 0.0f; // radians - OculusManager::getEulerAngles(yaw, pitch, roll); - glm::quat orientation(glm::vec3(pitch, yaw, roll)); + //float pitch = 0.0f, yaw = 0.0f, roll = 0.0f; // radians + //OculusManager::getEulerAngles(yaw, pitch, roll); + glm::quat orientation = qApp->getHeadOrientation(); // (glm::vec3(pitch, yaw, roll)); glm::vec3 result; MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); @@ -574,7 +574,8 @@ void ApplicationOverlay::renderPointers() { glm::vec2 spericalPos = directionToSpherical(glm::normalize(lookAtDirection)); glm::vec2 screenPos = sphericalToScreen(spericalPos); position = QPoint(screenPos.x, screenPos.y); - glCanvas->cursor().setPos(glCanvas->mapToGlobal(position)); + // FIXME + //glCanvas->cursor().setPos(glCanvas->mapToGlobal(position)); } else { qDebug() << "No collision point"; } @@ -597,7 +598,6 @@ void ApplicationOverlay::renderPointers() { } void ApplicationOverlay::renderControllerPointers() { - auto glCanvas = Application::getInstance()->getGLWidget(); MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); //Static variables used for storing controller state @@ -646,7 +646,7 @@ void ApplicationOverlay::renderControllerPointers() { //if we have the oculus, we should make the cursor smaller since it will be //magnified - if (OculusManager::isConnected()) { + if (qApp->isHMDMode()) { QPoint point = getPalmClickLocation(palmData); @@ -661,6 +661,7 @@ void ApplicationOverlay::renderControllerPointers() { continue; } + auto canvasSize = qApp->getCanvasSize(); int mouseX, mouseY; if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLasers)) { QPoint res = getPalmClickLocation(palmData); @@ -675,14 +676,14 @@ void ApplicationOverlay::renderControllerPointers() { float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2)); // Get the pixel range over which the xAngle and yAngle are scaled - float cursorRange = glCanvas->width() * SixenseManager::getInstance().getCursorPixelRangeMult(); + float cursorRange = canvasSize.x * SixenseManager::getInstance().getCursorPixelRangeMult(); - mouseX = (glCanvas->width() / 2.0f + cursorRange * xAngle); - mouseY = (glCanvas->height() / 2.0f + cursorRange * yAngle); + mouseX = (canvasSize.x / 2.0f + cursorRange * xAngle); + mouseY = (canvasSize.y / 2.0f + cursorRange * yAngle); } //If the cursor is out of the screen then don't render it - if (mouseX < 0 || mouseX >= glCanvas->width() || mouseY < 0 || mouseY >= glCanvas->height()) { + if (mouseX < 0 || mouseX >= canvasSize.x || mouseY < 0 || mouseY >= canvasSize.y) { _reticleActive[index] = false; continue; } @@ -707,7 +708,7 @@ void ApplicationOverlay::renderControllerPointers() { } void ApplicationOverlay::renderPointersOculus(const glm::vec3& eyePos) { - glBindTexture(GL_TEXTURE_2D, _crosshairTexture); + glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture)); glDisable(GL_DEPTH_TEST); glMatrixMode(GL_MODELVIEW); @@ -744,10 +745,10 @@ void ApplicationOverlay::renderMagnifier(glm::vec2 magPos, float sizeMult, bool if (!_magnifier) { return; } - auto glCanvas = Application::getInstance()->getGLWidget(); + auto canvasSize = qApp->getCanvasSize(); - const int widgetWidth = glCanvas->width(); - const int widgetHeight = glCanvas->height(); + const int widgetWidth = canvasSize.x; + const int widgetHeight = canvasSize.y; const float halfWidth = (MAGNIFY_WIDTH / _textureAspectRatio) * sizeMult / 2.0f; const float halfHeight = MAGNIFY_HEIGHT * sizeMult / 2.0f; @@ -809,7 +810,6 @@ void ApplicationOverlay::renderMagnifier(glm::vec2 magPos, float sizeMult, bool } void ApplicationOverlay::renderAudioMeter() { - auto glCanvas = Application::getInstance()->getGLWidget(); auto audio = DependencyManager::get(); // Audio VU Meter and Mute Icon @@ -822,7 +822,7 @@ void ApplicationOverlay::renderAudioMeter() { const int AUDIO_METER_X = MIRROR_VIEW_LEFT_PADDING + MUTE_ICON_SIZE + AUDIO_METER_GAP; int audioMeterY; - bool smallMirrorVisible = Menu::getInstance()->isOptionChecked(MenuOption::Mirror) && !OculusManager::isConnected(); + bool smallMirrorVisible = Menu::getInstance()->isOptionChecked(MenuOption::Mirror) && !qApp->isHMDMode(); bool boxed = smallMirrorVisible && !Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror); if (boxed) { @@ -857,16 +857,17 @@ void ApplicationOverlay::renderAudioMeter() { } bool isClipping = ((audio->getTimeSinceLastClip() > 0.0f) && (audio->getTimeSinceLastClip() < CLIPPING_INDICATOR_TIME)); + auto canvasSize = qApp->getCanvasSize(); if ((audio->getTimeSinceLastClip() > 0.0f) && (audio->getTimeSinceLastClip() < CLIPPING_INDICATOR_TIME)) { const float MAX_MAGNITUDE = 0.7f; float magnitude = MAX_MAGNITUDE * (1 - audio->getTimeSinceLastClip() / CLIPPING_INDICATOR_TIME); - renderCollisionOverlay(glCanvas->width(), glCanvas->height(), magnitude, 1.0f); + renderCollisionOverlay(canvasSize.x, canvasSize.y, magnitude, 1.0f); } DependencyManager::get()->render(MIRROR_VIEW_LEFT_PADDING + AUDIO_METER_GAP, audioMeterY, boxed); - DependencyManager::get()->render(glCanvas->width(), glCanvas->height()); - DependencyManager::get()->render(WHITE_TEXT, glCanvas->width(), glCanvas->height()); + DependencyManager::get()->render(canvasSize.x, canvasSize.y); + DependencyManager::get()->render(WHITE_TEXT, canvasSize.x, canvasSize.y); audioMeterY += AUDIO_METER_HEIGHT; @@ -927,7 +928,6 @@ void ApplicationOverlay::renderStatsAndLogs() { Application* application = Application::getInstance(); QSharedPointer bandwidthRecorder = DependencyManager::get(); - auto glCanvas = Application::getInstance()->getGLWidget(); const OctreePacketProcessor& octreePacketProcessor = application->getOctreePacketProcessor(); NodeBounds& nodeBoundsDisplay = application->getNodeBoundsDisplay(); @@ -950,12 +950,13 @@ void ApplicationOverlay::renderStatsAndLogs() { // Show on-screen msec timer if (Menu::getInstance()->isOptionChecked(MenuOption::FrameTimer)) { + auto canvasSize = qApp->getCanvasSize(); quint64 mSecsNow = floor(usecTimestampNow() / 1000.0 + 0.5); QString frameTimer = QString("%1\n").arg((int)(mSecsNow % 1000)); int timerBottom = (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) ? 80 : 20; - drawText(glCanvas->width() - 100, glCanvas->height() - timerBottom, + drawText(canvasSize.x - 100, canvasSize.y - timerBottom, 0.30f, 0.0f, 0, frameTimer.toUtf8().constData(), WHITE_TEXT); } nodeBoundsDisplay.drawOverlay(); @@ -965,25 +966,22 @@ void ApplicationOverlay::renderDomainConnectionStatusBorder() { auto nodeList = DependencyManager::get(); if (nodeList && !nodeList->getDomainHandler().isConnected()) { - auto glCanvas = Application::getInstance()->getGLWidget(); auto geometryCache = DependencyManager::get(); - int width = glCanvas->width(); - int height = glCanvas->height(); - - if (width != _previousBorderWidth || height != _previousBorderHeight) { + auto canvasSize = qApp->getCanvasSize(); + if (canvasSize.x != _previousBorderWidth || canvasSize.y != _previousBorderHeight) { glm::vec4 color(CONNECTION_STATUS_BORDER_COLOR[0], CONNECTION_STATUS_BORDER_COLOR[1], CONNECTION_STATUS_BORDER_COLOR[2], 1.0f); QVector border; border << glm::vec2(0, 0); - border << glm::vec2(0, height); - border << glm::vec2(width, height); - border << glm::vec2(width, 0); + border << glm::vec2(0, canvasSize.y); + border << glm::vec2(canvasSize.x, canvasSize.y); + border << glm::vec2(canvasSize.x, 0); border << glm::vec2(0, 0); geometryCache->updateVertices(_domainStatusBorder, border, color); - _previousBorderWidth = width; - _previousBorderHeight = height; + _previousBorderWidth = canvasSize.x; + _previousBorderHeight = canvasSize.y; } glLineWidth(CONNECTION_STATUS_BORDER_LINE_WIDTH); @@ -1101,8 +1099,8 @@ void ApplicationOverlay::TexturedHemisphere::cleanupVBO() { } void ApplicationOverlay::TexturedHemisphere::buildFramebufferObject() { - QSize size = Application::getInstance()->getGLWidget()->getDeviceSize(); - if (_framebufferObject != NULL && size == _framebufferObject->size()) { + auto deviceSize = qApp->getDeviceSize(); + if (_framebufferObject != NULL && deviceSize == _framebufferObject->size()) { // Already build return; } @@ -1111,7 +1109,7 @@ void ApplicationOverlay::TexturedHemisphere::buildFramebufferObject() { delete _framebufferObject; } - _framebufferObject = new QOpenGLFramebufferObject(size, QOpenGLFramebufferObject::Depth); + _framebufferObject = new QOpenGLFramebufferObject(deviceSize, QOpenGLFramebufferObject::Depth); glBindTexture(GL_TEXTURE_2D, getTexture()); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); @@ -1154,7 +1152,7 @@ GLuint ApplicationOverlay::TexturedHemisphere::getTexture() { return _framebufferObject->texture(); } -glm::vec2 ApplicationOverlay::directionToSpherical(glm::vec3 direction) const { +glm::vec2 ApplicationOverlay::directionToSpherical(const glm::vec3& direction) { glm::vec2 result; // Compute yaw glm::vec3 normalProjection = glm::normalize(glm::vec3(direction.x, 0.0f, direction.z)); @@ -1170,47 +1168,57 @@ glm::vec2 ApplicationOverlay::directionToSpherical(glm::vec3 direction) const { return result; } -glm::vec3 ApplicationOverlay::sphericalToDirection(glm::vec2 sphericalPos) const { +glm::vec3 ApplicationOverlay::sphericalToDirection(const glm::vec2& sphericalPos) { glm::quat rotation(glm::vec3(sphericalPos.y, sphericalPos.x, 0.0f)); return rotation * IDENTITY_FRONT; } -glm::vec2 ApplicationOverlay::screenToSpherical(glm::vec2 screenPos) const { - QSize screenSize = Application::getInstance()->getGLWidget()->getDeviceSize(); - float yaw = -(screenPos.x / screenSize.width() - 0.5f) * MOUSE_YAW_RANGE; - float pitch = (screenPos.y / screenSize.height() - 0.5f) * MOUSE_PITCH_RANGE; +glm::vec2 ApplicationOverlay::screenToSpherical(const glm::vec2& screenPos) { + auto screenSize = qApp->getCanvasSize(); + glm::vec2 result; + result.x = -(screenPos.x / screenSize.x - 0.5f); + result.y = (screenPos.y / screenSize.y - 0.5f); + result.x *= MOUSE_YAW_RANGE; + result.y *= MOUSE_PITCH_RANGE; - return glm::vec2(yaw, pitch); + return result; } -glm::vec2 ApplicationOverlay::sphericalToScreen(glm::vec2 sphericalPos) const { - QSize screenSize = Application::getInstance()->getGLWidget()->getDeviceSize(); - float x = (-sphericalPos.x / MOUSE_YAW_RANGE + 0.5f) * screenSize.width(); - float y = (sphericalPos.y / MOUSE_PITCH_RANGE + 0.5f) * screenSize.height(); - - return glm::vec2(x, y); +glm::vec2 ApplicationOverlay::sphericalToScreen(const glm::vec2& sphericalPos) { + glm::vec2 result = sphericalPos; + result.x *= -1.0; + result /= MOUSE_RANGE; + result += 0.5f; + result *= qApp->getCanvasSize(); + return result; } -glm::vec2 ApplicationOverlay::sphericalToOverlay(glm::vec2 sphericalPos) const { - QSize screenSize = Application::getInstance()->getGLWidget()->getDeviceSize(); - float x = (-sphericalPos.x / (_textureFov * _textureAspectRatio) + 0.5f) * screenSize.width(); - float y = (sphericalPos.y / _textureFov + 0.5f) * screenSize.height(); - - return glm::vec2(x, y); +glm::vec2 ApplicationOverlay::sphericalToOverlay(const glm::vec2& sphericalPos) const { + glm::vec2 result = sphericalPos; + result.x *= -1.0; + result /= _textureFov; + result.x /= _textureAspectRatio; + result += 0.5f; + result.x = (-sphericalPos.x / (_textureFov * _textureAspectRatio) + 0.5f); + result.y = (sphericalPos.y / _textureFov + 0.5f); + result *= qApp->getCanvasSize(); + return result; } -glm::vec2 ApplicationOverlay::overlayToSpherical(glm::vec2 overlayPos) const { - QSize screenSize = Application::getInstance()->getGLWidget()->getDeviceSize(); - float yaw = -(overlayPos.x / screenSize.width() - 0.5f) * _textureFov * _textureAspectRatio; - float pitch = (overlayPos.y / screenSize.height() - 0.5f) * _textureFov; - - return glm::vec2(yaw, pitch); +glm::vec2 ApplicationOverlay::overlayToSpherical(const glm::vec2& overlayPos) const { + glm::vec2 result = overlayPos; + result.x *= -1.0; + result /= qApp->getCanvasSize(); + result -= 0.5f; + result *= _textureFov; + result.x *= _textureAspectRatio; + return result; } -glm::vec2 ApplicationOverlay::screenToOverlay(glm::vec2 screenPos) const { +glm::vec2 ApplicationOverlay::screenToOverlay(const glm::vec2& screenPos) const { return sphericalToOverlay(screenToSpherical(screenPos)); } -glm::vec2 ApplicationOverlay::overlayToScreen(glm::vec2 overlayPos) const { +glm::vec2 ApplicationOverlay::overlayToScreen(const glm::vec2& overlayPos) const { return sphericalToScreen(overlayToSpherical(overlayPos)); } diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index e6c7526c5d..aa9cae9b3a 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -12,6 +12,7 @@ #ifndef hifi_ApplicationOverlay_h #define hifi_ApplicationOverlay_h +#include class Camera; class Overlays; class QOpenGLFramebufferObject; @@ -20,9 +21,11 @@ const float MAGNIFY_WIDTH = 220.0f; const float MAGNIFY_HEIGHT = 100.0f; const float MAGNIFY_MULT = 2.0f; -const float DEFAULT_OCULUS_UI_ANGULAR_SIZE = 72.0f; +const float DEFAULT_HMD_UI_ANGULAR_SIZE = 72.0f; // Handles the drawing of the overlays to the screen +// TODO, move divide up the rendering, displaying and input handling +// facilities of this class class ApplicationOverlay : public QObject { Q_OBJECT public: @@ -31,19 +34,18 @@ public: void renderOverlay(); void displayOverlayTexture(); - void displayOverlayTextureOculus(Camera& whichCamera); - void displayOverlayTexture3DTV(Camera& whichCamera, float aspectRatio, float fov); - - void computeOculusPickRay(float x, float y, glm::vec3& origin, glm::vec3& direction) const; + void displayOverlayTextureStereo(Camera& whichCamera, float aspectRatio, float fov); + void displayOverlayTextureHmd(Camera& whichCamera); + QPoint getPalmClickLocation(const PalmData *palm) const; bool calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const; bool hasMagnifier() const { return _magnifier; } void toggleMagnifier() { _magnifier = !_magnifier; } - float getOculusUIAngularSize() const { return _oculusUIAngularSize; } - void setOculusUIAngularSize(float oculusUIAngularSize) { _oculusUIAngularSize = oculusUIAngularSize; } - + float getHmdUIAngularSize() const { return _hmdUIAngularSize; } + void setHmdUIAngularSize(float hmdUIAngularSize) { _hmdUIAngularSize = hmdUIAngularSize; } + // Converter from one frame of reference to another. // Frame of reference: // Direction: Ray that represents the spherical values @@ -52,14 +54,16 @@ public: // Overlay: Position on the overlay (x,y) // (x,y) in Overlay are similar than (x,y) in Screen except they can be outside of the bound of te screen. // This allows for picking outside of the screen projection in 3D. - glm::vec2 directionToSpherical(glm::vec3 direction) const; - glm::vec3 sphericalToDirection(glm::vec2 sphericalPos) const; - glm::vec2 screenToSpherical(glm::vec2 screenPos) const; - glm::vec2 sphericalToScreen(glm::vec2 sphericalPos) const; - glm::vec2 sphericalToOverlay(glm::vec2 sphericalPos) const; - glm::vec2 overlayToSpherical(glm::vec2 overlayPos) const; - glm::vec2 screenToOverlay(glm::vec2 screenPos) const; - glm::vec2 overlayToScreen(glm::vec2 overlayPos) const; + glm::vec2 sphericalToOverlay(const glm::vec2 & sphericalPos) const; + glm::vec2 overlayToSpherical(const glm::vec2 & overlayPos) const; + glm::vec2 screenToOverlay(const glm::vec2 & screenPos) const; + glm::vec2 overlayToScreen(const glm::vec2 & overlayPos) const; + + static glm::vec2 directionToSpherical(const glm::vec3 & direction); + static glm::vec3 sphericalToDirection(const glm::vec2 & sphericalPos); + static glm::vec2 screenToSpherical(const glm::vec2 & screenPos); + static glm::vec2 sphericalToScreen(const glm::vec2 & sphericalPos); + static void computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction); private: // Interleaved vertex data @@ -91,7 +95,7 @@ private: VerticesIndices _vbo; }; - float _oculusUIAngularSize = DEFAULT_OCULUS_UI_ANGULAR_SIZE; + float _hmdUIAngularSize = DEFAULT_HMD_UI_ANGULAR_SIZE; void renderReticle(glm::quat orientation, float alpha); void renderPointers();; @@ -121,9 +125,8 @@ private: float _oculusUIRadius; float _trailingAudioLoudness; - GLuint _crosshairTexture; - // TODO, move divide up the rendering, displaying and input handling - // facilities of this class + + gpu::TexturePointer _crosshairTexture; GLuint _newUiTexture{ 0 }; int _reticleQuad; diff --git a/interface/src/ui/HMDToolsDialog.cpp b/interface/src/ui/HMDToolsDialog.cpp index 4a899a641e..231ffa6ed4 100644 --- a/interface/src/ui/HMDToolsDialog.cpp +++ b/interface/src/ui/HMDToolsDialog.cpp @@ -19,12 +19,10 @@ #include #include - #include "MainWindow.h" #include "Menu.h" #include "ui/DialogsManager.h" #include "ui/HMDToolsDialog.h" - #include "devices/OculusManager.h" HMDToolsDialog::HMDToolsDialog(QWidget* parent) : diff --git a/interface/src/ui/NodeBounds.cpp b/interface/src/ui/NodeBounds.cpp index f0f4d432db..2b1b2f56e1 100644 --- a/interface/src/ui/NodeBounds.cpp +++ b/interface/src/ui/NodeBounds.cpp @@ -38,9 +38,7 @@ void NodeBounds::draw() { // Compute ray to find selected nodes later on. We can't use the pre-computed ray in Application because it centers // itself after the cursor disappears. - Application* application = Application::getInstance(); - PickRay pickRay = application->getCamera()->computePickRay(application->getTrueMouseX(), - application->getTrueMouseY()); + PickRay pickRay = qApp->computePickRay(); // Variables to keep track of the selected node and properties to draw the cube later if needed Node* selectedNode = NULL; diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 98f7382097..ac436cbb10 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -170,7 +170,7 @@ void PreferencesDialog::loadPreferences() { ui.maxOctreePPSSpin->setValue(qApp->getOctreeQuery().getMaxOctreePacketsPerSecond()); - ui.oculusUIAngularSizeSpin->setValue(qApp->getApplicationOverlay().getOculusUIAngularSize()); + ui.oculusUIAngularSizeSpin->setValue(qApp->getApplicationOverlay().getHmdUIAngularSize()); SixenseManager& sixense = SixenseManager::getInstance(); ui.sixenseReticleMoveSpeedSpin->setValue(sixense.getReticleMoveSpeed()); @@ -216,8 +216,7 @@ void PreferencesDialog::savePreferences() { myAvatar->setLeanScale(ui.leanScaleSpin->value()); myAvatar->setClampedTargetScale(ui.avatarScaleSpin->value()); - auto glCanvas = Application::getInstance()->getGLWidget(); - Application::getInstance()->resizeGL(glCanvas->width(), glCanvas->height()); + Application::getInstance()->resizeGL(); DependencyManager::get()->getMyAvatar()->setRealWorldFieldOfView(ui.realWorldFieldOfViewSpin->value()); @@ -231,7 +230,7 @@ void PreferencesDialog::savePreferences() { qApp->getOctreeQuery().setMaxOctreePacketsPerSecond(ui.maxOctreePPSSpin->value()); - qApp->getApplicationOverlay().setOculusUIAngularSize(ui.oculusUIAngularSizeSpin->value()); + qApp->getApplicationOverlay().setHmdUIAngularSize(ui.oculusUIAngularSizeSpin->value()); SixenseManager& sixense = SixenseManager::getInstance(); sixense.setReticleMoveSpeed(ui.sixenseReticleMoveSpeedSpin->value()); @@ -255,7 +254,7 @@ void PreferencesDialog::savePreferences() { audio->setOutputStarveDetectionThreshold(ui.outputStarveDetectionThresholdSpinner->value()); audio->setOutputStarveDetectionPeriod(ui.outputStarveDetectionPeriodSpinner->value()); - Application::getInstance()->resizeGL(glCanvas->width(), glCanvas->height()); + Application::getInstance()->resizeGL(); // LOD items auto lodManager = DependencyManager::get(); diff --git a/interface/src/ui/RearMirrorTools.cpp b/interface/src/ui/RearMirrorTools.cpp index a215d20148..ec73668d9e 100644 --- a/interface/src/ui/RearMirrorTools.cpp +++ b/interface/src/ui/RearMirrorTools.cpp @@ -15,6 +15,7 @@ #include #include +#include #include "Application.h" #include "RearMirrorTools.h" @@ -28,16 +29,16 @@ const char ZOOM_LEVEL_SETTINGS[] = "ZoomLevel"; Setting::Handle RearMirrorTools::rearViewZoomLevel(QStringList() << SETTINGS_GROUP_NAME << ZOOM_LEVEL_SETTINGS, ZoomLevel::HEAD); -RearMirrorTools::RearMirrorTools(QGLWidget* parent, QRect& bounds) : - _parent(parent), +RearMirrorTools::RearMirrorTools(QRect& bounds) : _bounds(bounds), _windowed(false), _fullScreen(false) { - _closeTextureId = _parent->bindTexture(QImage(PathUtils::resourcesPath() + "images/close.svg")); + auto textureCache = DependencyManager::get(); + _closeTexture = textureCache->getImageTexture(PathUtils::resourcesPath() + "images/close.svg"); - _zoomHeadTextureId = _parent->bindTexture(QImage(PathUtils::resourcesPath() + "images/plus.svg")); - _zoomBodyTextureId = _parent->bindTexture(QImage(PathUtils::resourcesPath() + "images/minus.svg")); + _zoomHeadTexture = textureCache->getImageTexture(PathUtils::resourcesPath() + "images/plus.svg"); + _zoomBodyTexture = textureCache->getImageTexture(PathUtils::resourcesPath() + "images/minus.svg"); _shrinkIconRect = QRect(ICON_PADDING, ICON_PADDING, ICON_SIZE, ICON_SIZE); _closeIconRect = QRect(_bounds.left() + ICON_PADDING, _bounds.top() + ICON_PADDING, ICON_SIZE, ICON_SIZE); @@ -46,20 +47,19 @@ RearMirrorTools::RearMirrorTools(QGLWidget* parent, QRect& bounds) : _headZoomIconRect = QRect(_bounds.left() + ICON_PADDING, _bounds.bottom() - ICON_PADDING - ICON_SIZE, ICON_SIZE, ICON_SIZE); } -void RearMirrorTools::render(bool fullScreen) { +void RearMirrorTools::render(bool fullScreen, const QPoint & mousePosition) { if (fullScreen) { _fullScreen = true; - displayIcon(_parent->geometry(), _shrinkIconRect, _closeTextureId); + displayIcon(QRect(QPoint(), qApp->getDeviceSize()), _shrinkIconRect, _closeTexture); } else { // render rear view tools if mouse is in the bounds - QPoint mousePosition = _parent->mapFromGlobal(QCursor::pos()); - _windowed = _bounds.contains(mousePosition.x(), mousePosition.y()); + _windowed = _bounds.contains(mousePosition); if (_windowed) { - displayIcon(_bounds, _closeIconRect, _closeTextureId); + displayIcon(_bounds, _closeIconRect, _closeTexture); ZoomLevel zoomLevel = (ZoomLevel)rearViewZoomLevel.get(); - displayIcon(_bounds, _headZoomIconRect, _zoomHeadTextureId, zoomLevel == HEAD); - displayIcon(_bounds, _bodyZoomIconRect, _zoomBodyTextureId, zoomLevel == BODY); + displayIcon(_bounds, _headZoomIconRect, _zoomHeadTexture, zoomLevel == HEAD); + displayIcon(_bounds, _bodyZoomIconRect, _zoomBodyTexture, zoomLevel == BODY); } } } @@ -99,7 +99,7 @@ bool RearMirrorTools::mousePressEvent(int x, int y) { return false; } -void RearMirrorTools::displayIcon(QRect bounds, QRect iconBounds, GLuint textureId, bool selected) { +void RearMirrorTools::displayIcon(QRect bounds, QRect iconBounds, const gpu::TexturePointer& texture, bool selected) { glMatrixMode(GL_PROJECTION); glPushMatrix(); @@ -116,13 +116,13 @@ void RearMirrorTools::displayIcon(QRect bounds, QRect iconBounds, GLuint texture } else { quadColor = glm::vec4(1, 1, 1, 1); } - - glBindTexture(GL_TEXTURE_2D, textureId); + + glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(texture)); glm::vec2 topLeft(iconBounds.left(), iconBounds.top()); glm::vec2 bottomRight(iconBounds.right(), iconBounds.bottom()); - glm::vec2 texCoordTopLeft(0.0f, 1.0f); - glm::vec2 texCoordBottomRight(1.0f, 0.0f); + static const glm::vec2 texCoordTopLeft(0.0f, 1.0f); + static const glm::vec2 texCoordBottomRight(1.0f, 0.0f); DependencyManager::get()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, quadColor); diff --git a/interface/src/ui/RearMirrorTools.h b/interface/src/ui/RearMirrorTools.h index 8a76134627..2b359c2d07 100644 --- a/interface/src/ui/RearMirrorTools.h +++ b/interface/src/ui/RearMirrorTools.h @@ -12,10 +12,7 @@ #ifndef hifi_RearMirrorTools_h #define hifi_RearMirrorTools_h -#include "InterfaceConfig.h" - -#include - +#include #include enum ZoomLevel { @@ -26,8 +23,8 @@ enum ZoomLevel { class RearMirrorTools : public QObject { Q_OBJECT public: - RearMirrorTools(QGLWidget* parent, QRect& bounds); - void render(bool fullScreen); + RearMirrorTools(QRect& bounds); + void render(bool fullScreen, const QPoint & mousePos); bool mousePressEvent(int x, int y); static Setting::Handle rearViewZoomLevel; @@ -39,12 +36,10 @@ signals: void restoreView(); private: - QGLWidget* _parent; QRect _bounds; - GLuint _closeTextureId; - GLuint _resetTextureId; - GLuint _zoomBodyTextureId; - GLuint _zoomHeadTextureId; + gpu::TexturePointer _closeTexture; + gpu::TexturePointer _zoomBodyTexture; + gpu::TexturePointer _zoomHeadTexture; QRect _closeIconRect; QRect _resetIconRect; @@ -55,7 +50,7 @@ private: bool _windowed; bool _fullScreen; - void displayIcon(QRect bounds, QRect iconBounds, GLuint textureId, bool selected = false); + void displayIcon(QRect bounds, QRect iconBounds, const gpu::TexturePointer& texture, bool selected = false); }; #endif // hifi_RearMirrorTools_h diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp index 5a3109ff64..0d91f4e7ba 100644 --- a/interface/src/ui/Snapshot.cpp +++ b/interface/src/ui/Snapshot.cpp @@ -75,8 +75,8 @@ SnapshotMetaData* Snapshot::parseSnapshotData(QString snapshotPath) { return data; } -QString Snapshot::saveSnapshot() { - QFile* snapshotFile = savedFileForSnapshot(false); +QString Snapshot::saveSnapshot(QImage image) { + QFile* snapshotFile = savedFileForSnapshot(image, false); // we don't need the snapshot file, so close it, grab its filename and delete it snapshotFile->close(); @@ -88,14 +88,12 @@ QString Snapshot::saveSnapshot() { return snapshotPath; } -QTemporaryFile* Snapshot::saveTempSnapshot() { +QTemporaryFile* Snapshot::saveTempSnapshot(QImage image) { // return whatever we get back from saved file for snapshot - return static_cast(savedFileForSnapshot(true));; + return static_cast(savedFileForSnapshot(image, true));; } -QFile* Snapshot::savedFileForSnapshot(bool isTemporary) { - auto glCanvas = Application::getInstance()->getGLWidget(); - QImage shot = glCanvas->grabFrameBuffer(); +QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary) { Avatar* avatar = DependencyManager::get()->getMyAvatar(); diff --git a/interface/src/ui/Snapshot.h b/interface/src/ui/Snapshot.h index 2a40ee183b..e83f8b3ec4 100644 --- a/interface/src/ui/Snapshot.h +++ b/interface/src/ui/Snapshot.h @@ -42,13 +42,13 @@ private: class Snapshot { public: - static QString saveSnapshot(); - static QTemporaryFile* saveTempSnapshot(); + static QString saveSnapshot(QImage image); + static QTemporaryFile* saveTempSnapshot(QImage image); static SnapshotMetaData* parseSnapshotData(QString snapshotPath); static Setting::Handle snapshotsLocation; private: - static QFile* savedFileForSnapshot(bool isTemporary); + static QFile* savedFileForSnapshot(QImage & image, bool isTemporary); }; #endif // hifi_Snapshot_h diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index ec225dde72..161eb06f5f 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -57,8 +57,8 @@ Stats::Stats(): _octreeStatsWidth(STATS_OCTREE_MIN_WIDTH), _lastHorizontalOffset(0) { - auto glCanvas = Application::getInstance()->getGLWidget(); - resetWidth(glCanvas->width(), 0); + auto canvasSize = Application::getInstance()->getCanvasSize(); + resetWidth(canvasSize.x, 0); } void Stats::toggleExpanded() { @@ -68,7 +68,7 @@ void Stats::toggleExpanded() { // called on mouse click release // check for clicks over stats in order to expand or contract them void Stats::checkClick(int mouseX, int mouseY, int mouseDragStartedX, int mouseDragStartedY, int horizontalOffset) { - auto glCanvas = Application::getInstance()->getGLWidget(); + auto canvasSize = Application::getInstance()->getCanvasSize(); if (0 != glm::compMax(glm::abs(glm::ivec2(mouseX - mouseDragStartedX, mouseY - mouseDragStartedY)))) { // not worried about dragging on stats @@ -115,7 +115,7 @@ void Stats::checkClick(int mouseX, int mouseY, int mouseDragStartedX, int mouseD // top-right stats click lines = _expanded ? 11 : 3; statsHeight = lines * STATS_PELS_PER_LINE + 10; - statsWidth = glCanvas->width() - statsX; + statsWidth = canvasSize.x - statsX; if (mouseX > statsX && mouseX < statsX + statsWidth && mouseY > statsY && mouseY < statsY + statsHeight) { toggleExpanded(); return; @@ -123,8 +123,8 @@ void Stats::checkClick(int mouseX, int mouseY, int mouseDragStartedX, int mouseD } void Stats::resetWidth(int width, int horizontalOffset) { - auto glCanvas = Application::getInstance()->getGLWidget(); - int extraSpace = glCanvas->width() - horizontalOffset -2 + auto canvasSize = Application::getInstance()->getCanvasSize(); + int extraSpace = canvasSize.x - horizontalOffset - 2 - STATS_GENERAL_MIN_WIDTH - (Menu::getInstance()->isOptionChecked(MenuOption::TestPing) ? STATS_PING_MIN_WIDTH -1 : 0) - STATS_GEO_MIN_WIDTH @@ -148,7 +148,7 @@ void Stats::resetWidth(int width, int horizontalOffset) { _pingStatsWidth += (int) extraSpace / panels; } _geoStatsWidth += (int) extraSpace / panels; - _octreeStatsWidth += glCanvas->width() - + _octreeStatsWidth += canvasSize.x - (_generalStatsWidth + _pingStatsWidth + _geoStatsWidth + 3); } } @@ -197,7 +197,7 @@ void Stats::display( int outKbitsPerSecond, int voxelPacketsToProcess) { - auto glCanvas = Application::getInstance()->getGLWidget(); + auto canvasSize = Application::getInstance()->getCanvasSize(); unsigned int backgroundColor = 0x33333399; int verticalOffset = 0, lines = 0; @@ -211,7 +211,7 @@ void Stats::display( QSharedPointer bandwidthRecorder = DependencyManager::get(); if (_lastHorizontalOffset != horizontalOffset) { - resetWidth(glCanvas->width(), horizontalOffset); + resetWidth(canvasSize.x, horizontalOffset); _lastHorizontalOffset = horizontalOffset; } @@ -461,7 +461,7 @@ void Stats::display( lines = _expanded ? 10 : 2; - drawBackground(backgroundColor, horizontalOffset, 0, glCanvas->width() - horizontalOffset, + drawBackground(backgroundColor, horizontalOffset, 0, canvasSize.x - horizontalOffset, (lines + 1) * STATS_PELS_PER_LINE); horizontalOffset += 5; diff --git a/libraries/render-utils/src/AbstractViewStateInterface.h b/libraries/render-utils/src/AbstractViewStateInterface.h index 0d73614e7c..9f7042d325 100644 --- a/libraries/render-utils/src/AbstractViewStateInterface.h +++ b/libraries/render-utils/src/AbstractViewStateInterface.h @@ -45,7 +45,7 @@ public: virtual bool shouldRenderMesh(float largestDimension, float distanceToCamera) = 0; virtual float getSizeScale() const = 0; virtual int getBoundaryLevelAdjust() const = 0; - virtual PickRay computePickRay(float x, float y) = 0; + virtual PickRay computePickRay(float x, float y) const = 0; virtual const glm::vec3& getAvatarPosition() const = 0; }; diff --git a/libraries/render-utils/src/GlowEffect.cpp b/libraries/render-utils/src/GlowEffect.cpp index 6addd84da6..3040088ce5 100644 --- a/libraries/render-utils/src/GlowEffect.cpp +++ b/libraries/render-utils/src/GlowEffect.cpp @@ -31,7 +31,6 @@ GlowEffect::GlowEffect() _isOddFrame(false), _isFirstFrame(true), _intensity(0.0f), - _widget(NULL), _enabled(false) { } @@ -64,7 +63,7 @@ static ProgramObject* createProgram(const QString& name) { return program; } -void GlowEffect::init(QGLWidget* widget, bool enabled) { +void GlowEffect::init(bool enabled) { if (_initialized) { qCDebug(renderutils, "[ERROR] GlowEffeect is already initialized."); return; @@ -92,19 +91,9 @@ void GlowEffect::init(QGLWidget* widget, bool enabled) { _diffusionScaleLocation = _diffuseProgram->uniformLocation("diffusionScale"); _initialized = true; - _widget = widget; _enabled = enabled; } -int GlowEffect::getDeviceWidth() const { - return _widget->width() * (_widget->windowHandle() ? _widget->windowHandle()->devicePixelRatio() : 1.0f); -} - -int GlowEffect::getDeviceHeight() const { - return _widget->height() * (_widget->windowHandle() ? _widget->windowHandle()->devicePixelRatio() : 1.0f); -} - - void GlowEffect::prepare() { auto primaryFBO = DependencyManager::get()->getPrimaryFramebuffer(); GLuint fbo = gpu::GLBackend::getFramebufferID(primaryFBO); @@ -173,7 +162,8 @@ gpu::FramebufferPointer GlowEffect::render(bool toTexture) { } else { maybeBind(destFBO); if (!destFBO) { - glViewport(0, 0, getDeviceWidth(), getDeviceHeight()); + //destFBO->getSize(); + glViewport(0, 0, framebufferSize.width(), framebufferSize.height()); } glEnable(GL_TEXTURE_2D); glDisable(GL_LIGHTING); @@ -219,7 +209,7 @@ gpu::FramebufferPointer GlowEffect::render(bool toTexture) { } maybeBind(destFBO); if (!destFBO) { - glViewport(0, 0, getDeviceWidth(), getDeviceHeight()); + glViewport(0, 0, framebufferSize.width(), framebufferSize.height()); } _addSeparateProgram->bind(); renderFullscreenQuad(); @@ -228,7 +218,6 @@ gpu::FramebufferPointer GlowEffect::render(bool toTexture) { glBindTexture(GL_TEXTURE_2D, 0); glActiveTexture(GL_TEXTURE0); - } glPopMatrix(); diff --git a/libraries/render-utils/src/GlowEffect.h b/libraries/render-utils/src/GlowEffect.h index 6b7bad7689..1cee7b2e9d 100644 --- a/libraries/render-utils/src/GlowEffect.h +++ b/libraries/render-utils/src/GlowEffect.h @@ -34,7 +34,7 @@ public: /// (either the secondary or the tertiary). gpu::FramebufferPointer getFreeFramebuffer() const; - void init(QGLWidget* widget, bool enabled); + void init(bool enabled); /// Prepares the glow effect for rendering the current frame. To be called before rendering the scene. void prepare(); @@ -61,9 +61,6 @@ private: GlowEffect(); virtual ~GlowEffect(); - int getDeviceWidth() const; - int getDeviceHeight() const; - bool _initialized; ProgramObject* _addProgram; @@ -80,7 +77,6 @@ private: float _intensity; QStack _intensityStack; - QGLWidget* _widget; bool _enabled; }; diff --git a/libraries/render-utils/src/TextureCache.cpp b/libraries/render-utils/src/TextureCache.cpp index 63ca43725b..aa796ec48f 100644 --- a/libraries/render-utils/src/TextureCache.cpp +++ b/libraries/render-utils/src/TextureCache.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -32,8 +33,7 @@ TextureCache::TextureCache() : _permutationNormalTexture(0), _whiteTexture(0), _blueTexture(0), - _frameBufferSize(100, 100), - _associatedWidget(NULL) + _frameBufferSize(100, 100) { const qint64 TEXTURE_DEFAULT_UNUSED_MAX_SIZE = DEFAULT_UNUSED_MAX_SIZE; setUnusedResourceCacheSize(TEXTURE_DEFAULT_UNUSED_MAX_SIZE); @@ -290,22 +290,21 @@ GLuint TextureCache::getShadowDepthTextureID() { return gpu::GLBackend::getTextureID(_shadowTexture); } -bool TextureCache::eventFilter(QObject* watched, QEvent* event) { - if (event->type() == QEvent::Resize) { - QSize size = static_cast(event)->size(); - if (_frameBufferSize != size) { - _primaryFramebuffer.reset(); - _primaryColorTexture.reset(); - _primaryDepthTexture.reset(); - _primaryNormalTexture.reset(); - _primarySpecularTexture.reset(); - - _secondaryFramebuffer.reset(); - - _tertiaryFramebuffer.reset(); - } +/// Returns a texture version of an image file +gpu::TexturePointer TextureCache::getImageTexture(const QString & path) { + QImage image(path); + gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::UINT8, gpu::RGB); + gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::UINT8, gpu::RGB); + if (image.hasAlphaChannel()) { + formatGPU = gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA); + formatMip = gpu::Element(gpu::VEC4, gpu::UINT8, gpu::BGRA); } - return false; + gpu::TexturePointer texture = gpu::TexturePointer( + gpu::Texture::create2D(formatGPU, image.width(), image.height(), + gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); + texture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); + texture->autoGenerateMips(-1); + return texture; } QSharedPointer TextureCache::createResource(const QUrl& url, @@ -315,14 +314,6 @@ QSharedPointer TextureCache::createResource(const QUrl& url, &Resource::allReferencesCleared); } -void TextureCache::associateWithWidget(QGLWidget* widget) { - if (_associatedWidget) { - _associatedWidget->removeEventFilter(this); - } - _associatedWidget = widget; - _associatedWidget->installEventFilter(this); -} - Texture::Texture() { } diff --git a/libraries/render-utils/src/TextureCache.h b/libraries/render-utils/src/TextureCache.h index 42c1cecde3..e25a640ae7 100644 --- a/libraries/render-utils/src/TextureCache.h +++ b/libraries/render-utils/src/TextureCache.h @@ -35,9 +35,6 @@ class TextureCache : public ResourceCache, public Dependency { SINGLETON_DEPENDENCY public: - - void associateWithWidget(QGLWidget* widget); - /// Sets the desired texture resolution for the framebuffer objects. void setFrameBufferSize(QSize frameBufferSize); const QSize& getFrameBufferSize() const { return _frameBufferSize; } @@ -53,6 +50,9 @@ public: /// Returns the a pale blue texture (useful for a normal map). const gpu::TexturePointer& getBlueTexture(); + /// Returns a texture version of an image file + gpu::TexturePointer getImageTexture(const QString & path); + /// Loads a texture from the specified URL. NetworkTexturePointer getTexture(const QUrl& url, TextureType type = DEFAULT_TEXTURE, bool dilatable = false, const QByteArray& content = QByteArray()); @@ -94,8 +94,6 @@ public: /// Returns the ID of the shadow framebuffer object's depth texture. GLuint getShadowDepthTextureID(); - virtual bool eventFilter(QObject* watched, QEvent* event); - protected: virtual QSharedPointer createResource(const QUrl& url, @@ -127,7 +125,6 @@ private: gpu::TexturePointer _shadowTexture; QSize _frameBufferSize; - QGLWidget* _associatedWidget; }; /// A simple object wrapper for an OpenGL texture. diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp index 5d1b70275c..89fd15e179 100644 --- a/libraries/shared/src/GLMHelpers.cpp +++ b/libraries/shared/src/GLMHelpers.cpp @@ -336,6 +336,10 @@ QMatrix4x4 fromGlm(const glm::mat4 & m) { return QMatrix4x4(&m[0][0]).transposed(); } +QSize fromGlm(const glm::ivec2 & v) { + return QSize(v.x, v.y); +} + QRectF glmToRect(const glm::vec2 & pos, const glm::vec2 & size) { QRectF result(pos.x, pos.y, size.x, size.y); return result; diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index dda57a9cd9..a06f6f26cf 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -112,8 +112,13 @@ glm::vec2 toGlm(const QPointF & pt); glm::vec3 toGlm(const xColor & color); glm::vec4 toGlm(const QColor & color); +QSize fromGlm(const glm::ivec2 & v); QMatrix4x4 fromGlm(const glm::mat4 & m); QRectF glmToRect(const glm::vec2 & pos, const glm::vec2 & size); +#define YAW(euler) euler.y +#define PITCH(euler) euler.x +#define ROLL(euler) euler.z + #endif // hifi_GLMHelpers_h From 098a9bd70e87481fe1cfbe754a423bbfe75dca63 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Mon, 4 May 2015 13:10:16 -0700 Subject: [PATCH 18/22] Fixing sixense and restoring lookat --- interface/src/Application.cpp | 5 ++++ interface/src/Camera.cpp | 38 +++++++++++++++++++++++- interface/src/Camera.h | 17 +++++++++++ interface/src/devices/OculusManager.cpp | 1 + interface/src/devices/SixenseManager.cpp | 9 +++--- interface/src/devices/TV3DManager.cpp | 3 +- 6 files changed, 65 insertions(+), 8 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f5d0e3b01a..0b3df7540f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -852,6 +852,11 @@ void Application::paintGL() { glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror); } + // Update camera position + if (!OculusManager::isConnected()) { + _myCamera.update(1.0f / _fps); + } + if (getShadowsEnabled()) { updateShadowMap(); } diff --git a/interface/src/Camera.cpp b/interface/src/Camera.cpp index 7db598bdfc..1016d60ccf 100644 --- a/interface/src/Camera.cpp +++ b/interface/src/Camera.cpp @@ -53,24 +53,42 @@ Camera::Camera() : _nearClip(DEFAULT_NEAR_CLIP), // default _farClip(DEFAULT_FAR_CLIP), // default _hmdPosition(), - _hmdRotation() + _hmdRotation(), + _isKeepLookingAt(false), + _lookingAt(0.0f, 0.0f, 0.0f) { } +void Camera::update(float deltaTime) { + if (_isKeepLookingAt) { + lookAt(_lookingAt); + } + return; +} + void Camera::setPosition(const glm::vec3& position) { _position = position; } void Camera::setRotation(const glm::quat& rotation) { _rotation = rotation; + if (_isKeepLookingAt) { + lookAt(_lookingAt); + } } void Camera::setHmdPosition(const glm::vec3& hmdPosition) { _hmdPosition = hmdPosition; + if (_isKeepLookingAt) { + lookAt(_lookingAt); + } } void Camera::setHmdRotation(const glm::quat& hmdRotation) { _hmdRotation = hmdRotation; + if (_isKeepLookingAt) { + lookAt(_lookingAt); + } } float Camera::getFarClip() const { @@ -101,6 +119,10 @@ void Camera::setFarClip(float f) { _farClip = f; } +PickRay Camera::computePickRay(float x, float y) { + return qApp->computePickRay(x, y); +} + void Camera::setModeString(const QString& mode) { CameraMode targetMode = stringToMode(mode); @@ -129,3 +151,17 @@ void Camera::setModeString(const QString& mode) { QString Camera::getModeString() const { return modeToString(_mode); } + +void Camera::lookAt(const glm::vec3& lookAt) { + glm::vec3 up = IDENTITY_UP; + glm::mat4 lookAtMatrix = glm::lookAt(_position, lookAt, up); + glm::quat rotation = glm::quat_cast(lookAtMatrix); + rotation.w = -rotation.w; // Rosedale approved + _rotation = rotation; +} + +void Camera::keepLookingAt(const glm::vec3& point) { + lookAt(point); + _isKeepLookingAt = true; + _lookingAt = point; +} diff --git a/interface/src/Camera.h b/interface/src/Camera.h index b2eef3a8cb..e06e12f7dc 100644 --- a/interface/src/Camera.h +++ b/interface/src/Camera.h @@ -41,6 +41,8 @@ public: void initialize(); // instantly put the camera at the ideal position and rotation. + void update( float deltaTime ); + void setRotation(const glm::quat& rotation); void setHmdPosition(const glm::vec3& hmdPosition); void setHmdRotation(const glm::quat& hmdRotation); @@ -73,6 +75,19 @@ public slots: void setOrientation(const glm::quat& orientation) { setRotation(orientation); } glm::quat getOrientation() const { return getRotation(); } + + PickRay computePickRay(float x, float y); + + // These only work on independent cameras + /// one time change to what the camera is looking at + void lookAt(const glm::vec3& value); + + /// fix what the camera is looking at, and keep the camera looking at this even if position changes + void keepLookingAt(const glm::vec3& value); + + /// stops the keep looking at feature, doesn't change what's being looked at, but will stop camera from + /// continuing to update it's orientation to keep looking at the item + void stopLooking() { _isKeepLookingAt = false; } signals: void modeUpdated(const QString& newMode); @@ -89,6 +104,8 @@ private: glm::quat _rotation; glm::vec3 _hmdPosition; glm::quat _hmdRotation; + bool _isKeepLookingAt; + glm::vec3 _lookingAt; }; #endif // hifi_Camera_h diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index 2d6141aac9..872cb62e4b 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -592,6 +592,7 @@ void OculusManager::display(QGLWidget * glCanvas, const glm::quat &bodyOrientati glm::vec3(_eyeRenderDesc[eye].HmdToEyeViewOffset.x, _eyeRenderDesc[eye].HmdToEyeViewOffset.y, _eyeRenderDesc[eye].HmdToEyeViewOffset.z)); _eyePositions[eye] = thisEyePosition; + _camera->update(1.0f / Application::getInstance()->getFps()); glMatrixMode(GL_PROJECTION); glLoadIdentity(); diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index d74e210642..f1a762e64f 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -472,7 +472,6 @@ void SixenseManager::updateCalibration(const sixenseControllerData* controllers) //Injecting mouse movements and clicks void SixenseManager::emulateMouse(PalmData* palm, int index) { MyAvatar* avatar = DependencyManager::get()->getMyAvatar(); - auto glCanvas = Application::getInstance()->getGLWidget(); QPoint pos; Qt::MouseButton bumperButton; @@ -498,12 +497,12 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) { // Get the angles, scaled between (-0.5,0.5) float xAngle = (atan2(direction.z, direction.x) + M_PI_2); float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2)); - + auto canvasSize = qApp->getCanvasSize(); // Get the pixel range over which the xAngle and yAngle are scaled - float cursorRange = glCanvas->width() * getCursorPixelRangeMult(); + float cursorRange = canvasSize.x * getCursorPixelRangeMult(); - pos.setX(glCanvas->width() / 2.0f + cursorRange * xAngle); - pos.setY(glCanvas->height() / 2.0f + cursorRange * yAngle); + pos.setX(canvasSize.x / 2.0f + cursorRange * xAngle); + pos.setY(canvasSize.y / 2.0f + cursorRange * yAngle); } diff --git a/interface/src/devices/TV3DManager.cpp b/interface/src/devices/TV3DManager.cpp index 90030b2f84..9d5dd2faae 100644 --- a/interface/src/devices/TV3DManager.cpp +++ b/interface/src/devices/TV3DManager.cpp @@ -33,9 +33,8 @@ bool TV3DManager::isConnected() { } void TV3DManager::connect() { - Camera& camera = *Application::getInstance()->getCamera(); auto deviceSize = qApp->getDeviceSize(); - configureCamera(camera, deviceSize.width(), deviceSize.height()); + configureCamera(*(qApp->getCamera()), deviceSize.width(), deviceSize.height()); } From d2dbb2c116dfd20d2a25eae23a7cab1e347607f8 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 6 May 2015 11:57:34 -0700 Subject: [PATCH 19/22] Missing the code! --- libraries/fbx/src/OBJReader.cpp | 81 +++++++++++++++++++-------------- libraries/fbx/src/OBJReader.h | 4 +- 2 files changed, 51 insertions(+), 34 deletions(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index 86f75f614c..f78c35b8a0 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -35,6 +35,10 @@ const QString SMART_DEFAULT_MATERIAL_NAME = "High Fidelity smart default materia OBJTokenizer::OBJTokenizer(QIODevice* device) : _device(device), _pushedBackToken(-1) { } +const QByteArray OBJTokenizer::getLineAsDatum() { + return _device->readLine().trimmed(); +} + int OBJTokenizer::nextToken() { if (_pushedBackToken != NO_PUSHBACKED_TOKEN) { int token = _pushedBackToken; @@ -133,7 +137,7 @@ void setMeshPartDefaults(FBXMeshPart& meshPart, QString materialID) { } // OBJFace -bool OBJFace::add(const QByteArray& vertexIndex, const QByteArray& textureIndex, const QByteArray& normalIndex) { +bool OBJFace::add(const QByteArray& vertexIndex, const QByteArray& textureIndex, const QByteArray& normalIndex, const QVector& vertices) { bool ok; int index = vertexIndex.toInt(&ok); if (!ok) { @@ -145,6 +149,9 @@ bool OBJFace::add(const QByteArray& vertexIndex, const QByteArray& textureIndex, if (!ok) { return false; } + if (index < 0) { // Count backwards from the last one added. + index = vertices.count() + 1 + index; + } textureUVIndices.append(index - 1); } if (!normalIndex.isEmpty()) { @@ -184,6 +191,14 @@ void OBJFace::addFrom(const OBJFace* face, int index) { // add using data from f } } +bool OBJReader::isValidTexture(const QByteArray &filename) { + QUrl candidateUrl = url->resolved(QUrl(filename)); + QNetworkReply *netReply = request(candidateUrl, true); + bool isValid = netReply->isFinished() && (netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200); + netReply->deleteLater(); + return isValid; +} + void OBJReader::parseMaterialLibrary(QIODevice* device) { OBJTokenizer tokenizer(device); QString matName = SMART_DEFAULT_MATERIAL_NAME; @@ -221,16 +236,21 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { currentMaterial.diffuseColor = tokenizer.getVec3(); } else if (token == "Ks") { currentMaterial.specularColor = tokenizer.getVec3(); - } else if (token == "map_Kd") { - if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { - return; + } else if ((token == "map_Kd") || (token == "map_Ks")) { + QByteArray filename = QUrl(tokenizer.getLineAsDatum()).fileName().toUtf8(); + if (filename.endsWith(".tga")) { + qCDebug(modelformat) << "OBJ Reader WARNING: currently ignoring tga texture " << filename << " in " << url; + break; } - currentMaterial.diffuseTextureFilename = tokenizer.getDatum(); - } else if (token == "map_Ks") { - if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { - return; + if (isValidTexture(filename)) { + if (token == "map_Kd") { + currentMaterial.diffuseTextureFilename = filename; + } else { + currentMaterial.specularTextureFilename = filename; + } + } else { + qCDebug(modelformat) << "OBJ Reader WARNING: " << url << " ignoring missing texture " << filename; } - currentMaterial.diffuseTextureFilename = tokenizer.getDatum(); } } } @@ -302,7 +322,7 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi break; // Some files use mtllib over and over again for the same libraryName } librariesSeen[libraryName] = true; - QUrl libraryUrl = url->resolved(QUrl(libraryName)); + QUrl libraryUrl = url->resolved(QUrl(libraryName).fileName()); // Throw away any path part of libraryName, and merge against original url. qCDebug(modelformat) << "OBJ Reader new library:" << libraryName << " at:" << libraryUrl; QNetworkReply* netReply = request(libraryUrl, false); if (netReply->isFinished() && (netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200)) { @@ -345,9 +365,8 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi QList parts = token.split('/'); assert(parts.count() >= 1); assert(parts.count() <= 3); - // FIXME: if we want to handle negative indices, it has to be done here. const QByteArray noData {}; - face.add(parts[0], (parts.count() > 1) ? parts[1] : noData, (parts.count() > 2) ? parts[2] : noData); + face.add(parts[0], (parts.count() > 1) ? parts[1] : noData, (parts.count() > 2) ? parts[2] : noData, vertices); face.groupName = currentGroup; face.materialName = currentMaterialName; } @@ -428,14 +447,10 @@ FBXGeometry OBJReader::readOBJ(QIODevice* device, const QVariantHash& mapping, Q QByteArray base = basename.toUtf8(), textName = ""; for (int i = 0; i < extensions.count(); i++) { QByteArray candidateString = base + extensions[i]; - QUrl candidateUrl = url->resolved(QUrl(candidateString)); - QNetworkReply *netReply = request(candidateUrl, true); - if (netReply->isFinished() && (netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200)) { + if (isValidTexture(candidateString)) { textName = candidateString; - netReply->deleteLater(); break; } - netReply->deleteLater(); } if (!textName.isEmpty()) { preDefinedMaterial.diffuseTextureFilename = textName; @@ -447,24 +462,24 @@ FBXGeometry OBJReader::readOBJ(QIODevice* device, const QVariantHash& mapping, Q FaceGroup faceGroup = faceGroups[meshPartCount]; OBJFace leadFace = faceGroup[0]; // All the faces in the same group will have the same name and material. QString groupMaterialName = leadFace.materialName; - if ((leadFace.textureUVIndices.count() > 0) && groupMaterialName.isEmpty()) { + if (groupMaterialName.isEmpty() && (leadFace.textureUVIndices.count() > 0)) { + qCDebug(modelformat) << "OBJ Reader WARNING: " << url << " needs a texture that isn't specified. Using default mechanism."; + groupMaterialName = SMART_DEFAULT_MATERIAL_NAME; + } else if (!groupMaterialName.isEmpty() && !materials.contains(groupMaterialName)) { + qCDebug(modelformat) << "OBJ Reader WARNING: " << url << " specifies a material " << groupMaterialName << " that is not defined. Using default mechanism."; groupMaterialName = SMART_DEFAULT_MATERIAL_NAME; } - if (!groupMaterialName.isEmpty()) { - if (!materials.contains(groupMaterialName)) { - qCDebug(modelformat) << "OBJ Reader:" << url << " references undefined material " << groupMaterialName << "."; - } else { - OBJMaterial* material = &materials[groupMaterialName]; - // The code behind this is in transition. Some things are set directly in the FXBMeshPart... - meshPart.materialID = groupMaterialName; - meshPart.diffuseTexture.filename = material->diffuseTextureFilename; - meshPart.specularTexture.filename = material->specularTextureFilename; - // ... and some things are set in the underlying material. - meshPart._material->setDiffuse(material->diffuseColor); - meshPart._material->setSpecular(material->specularColor); - meshPart._material->setShininess(material->shininess); - meshPart._material->setOpacity(material->opacity); - } + if (!groupMaterialName.isEmpty()) { + OBJMaterial* material = &materials[groupMaterialName]; + // The code behind this is in transition. Some things are set directly in the FXBMeshPart... + meshPart.materialID = groupMaterialName; + meshPart.diffuseTexture.filename = material->diffuseTextureFilename; + meshPart.specularTexture.filename = material->specularTextureFilename; + // ... and some things are set in the underlying material. + meshPart._material->setDiffuse(material->diffuseColor); + meshPart._material->setSpecular(material->specularColor); + meshPart._material->setShininess(material->shininess); + meshPart._material->setOpacity(material->opacity); } qCDebug(modelformat) << "OBJ Reader part:" << meshPartCount << "name:" << leadFace.groupName << "material:" << groupMaterialName << "diffuse:" << meshPart._material->getDiffuse() << "faces:" << faceGroup.count() << "triangle indices will start with:" << mesh.vertices.count(); foreach(OBJFace face, faceGroup) { diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index 65a65c70ee..a61665cb86 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -14,6 +14,7 @@ public: int nextToken(); const QByteArray& getDatum() const { return _datum; } bool isNextTokenFloat(); + const QByteArray getLineAsDatum(); // some "filenames" have spaces in them void skipLine() { _device->readLine(); } void pushBackToken(int token) { _pushedBackToken = token; } void ungetChar(char ch) { _device->ungetChar(ch); } @@ -37,7 +38,7 @@ public: QString groupName; // We don't make use of hierarchical structure, but it can be preserved for debugging and future use. QString materialName; // Add one more set of vertex data. Answers true if successful - bool add(const QByteArray& vertexIndex, const QByteArray& textureIndex, const QByteArray& normalIndex); + bool add(const QByteArray& vertexIndex, const QByteArray& textureIndex, const QByteArray& normalIndex, const QVector& vertices); // Return a set of one or more OBJFaces from this one, in which each is just a triangle. // Even though FBXMeshPart can handle quads, it would be messy to try to keep track of mixed-size faces, so we treat everything as triangles. QVector triangulate(); @@ -77,6 +78,7 @@ private: QHash librariesSeen; bool parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, FBXGeometry& geometry, float& scaleGuess); void parseMaterialLibrary(QIODevice* device); + bool isValidTexture(const QByteArray &filename); // true if the file exists. TODO?: check content-type header and that it is a supported format. }; // What are these utilities doing here? One is used by fbx loading code in VHACD Utils, and the other a general debugging utility. From 35337ef2c209f56c0d3637ef2564125c775638d6 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 6 May 2015 12:28:45 -0700 Subject: [PATCH 20/22] Spell out order-of-evaluation for the compiler. --- libraries/fbx/src/OBJReader.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index f78c35b8a0..ebfa9b54f0 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -101,7 +101,10 @@ bool OBJTokenizer::isNextTokenFloat() { } glm::vec3 OBJTokenizer::getVec3() { - auto v = glm::vec3(getFloat(), getFloat(), getFloat()); // N.B.: getFloat() has side-effect + auto x = getFloat(); // N.B.: getFloat() has side-effect + auto y = getFloat(); // And order of arguments is different on Windows/Linux. + auto z = getFloat(); + auto v = glm::vec3(x, y, z); while (isNextTokenFloat()) { // the spec(s) get(s) vague here. might be w, might be a color... chop it off. nextToken(); From 351246fac11de6243d10cb5fcd6d64ec2b0affcc Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Wed, 6 May 2015 12:50:20 -0700 Subject: [PATCH 21/22] Fixing new image texture use from last upstream merge --- interface/src/devices/CameraToolBox.cpp | 14 +++++++------- interface/src/devices/CameraToolBox.h | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/interface/src/devices/CameraToolBox.cpp b/interface/src/devices/CameraToolBox.cpp index 26aff4bf9a..a1e00d7052 100644 --- a/interface/src/devices/CameraToolBox.cpp +++ b/interface/src/devices/CameraToolBox.cpp @@ -14,6 +14,7 @@ #include #include +#include "gpu/GLBackend.h" #include "Application.h" #include "CameraToolBox.h" #include "FaceTracker.h" @@ -74,21 +75,20 @@ void CameraToolBox::toggleMute() { void CameraToolBox::render(int x, int y, bool boxed) { glEnable(GL_TEXTURE_2D); - auto glCanvas = Application::getInstance()->getGLWidget(); - if (_enabledTextureId == 0) { - _enabledTextureId = glCanvas->bindTexture(QImage(PathUtils::resourcesPath() + "images/face.svg")); + if (!_enabledTexture) { + _enabledTexture = DependencyManager::get()->getImageTexture(PathUtils::resourcesPath() + "images/face.svg"); } - if (_mutedTextureId == 0) { - _mutedTextureId = glCanvas->bindTexture(QImage(PathUtils::resourcesPath() + "images/face-mute.svg")); + if (!_mutedTexture) { + _mutedTexture = DependencyManager::get()->getImageTexture(PathUtils::resourcesPath() + "images/face-mute.svg"); } const int MUTE_ICON_SIZE = 24; _iconBounds = QRect(x, y, MUTE_ICON_SIZE, MUTE_ICON_SIZE); float iconColor = 1.0f; if (!Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking)) { - glBindTexture(GL_TEXTURE_2D, _enabledTextureId); + glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_enabledTexture)); } else { - glBindTexture(GL_TEXTURE_2D, _mutedTextureId); + glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_mutedTexture)); // Make muted icon pulsate static const float PULSE_MIN = 0.4f; diff --git a/interface/src/devices/CameraToolBox.h b/interface/src/devices/CameraToolBox.h index 5f9241c81d..89e0c6a8dc 100644 --- a/interface/src/devices/CameraToolBox.h +++ b/interface/src/devices/CameraToolBox.h @@ -34,8 +34,8 @@ private slots: void toggleMute(); private: - GLuint _enabledTextureId = 0; - GLuint _mutedTextureId = 0; + gpu::TexturePointer _enabledTexture; + gpu::TexturePointer _mutedTexture; int _boxQuadID = GeometryCache::UNKNOWN_ID; QRect _iconBounds; qint64 _iconPulseTimeReference = 0; From 3c10d29d5384fad890f15d21a66b599b3e34d107 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Wed, 6 May 2015 14:09:43 -0700 Subject: [PATCH 22/22] Removing another reference to oculus manager --- interface/src/ui/ApplicationOverlay.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 21b10566d2..c410b992c4 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -820,7 +820,7 @@ void ApplicationOverlay::renderCameraToggle() { } int audioMeterY; - bool smallMirrorVisible = Menu::getInstance()->isOptionChecked(MenuOption::Mirror) && !OculusManager::isConnected(); + bool smallMirrorVisible = Menu::getInstance()->isOptionChecked(MenuOption::Mirror) && !qApp->isHMDMode(); bool boxed = smallMirrorVisible && !Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror); if (boxed) {